Avatar

Blog (pg. 9)

  • Published on
    Having already been caught out by the ASP.NET deadlock issue when using async code synchronously I did a lot of reading on the subject, from various sources and ended up compiling a list of rules/conventions to follow when dealing with async code. This is by no means a gospel list and I don't elaborate on why each guideline exists, but it's just a concise set of reminders of things to consider when writing async code, since the compiler will not pick up warnings for a lot of these things. The list is shared below:
    • Don't use .Result or .Wait() - use async/await (unless absolutely necessary - e.g. destructor, domain value factory - see final point)
    • Don't used async void - always return a Task
    • Always use "ConfigureAwait(false)" inside async library code (unless you need the context) [NB. Not applicable to .NET Core, bear in mind your consumers for .NET Standard]
    • Don't throw parameter exceptions in an async method, make the async method private and check the params in a public Task method before calling the private one
    • Don't "await Task.FromResult" - remove the async and just return the Task directly (unless async null or type casting).
    • When returning Task, don't return "null" - you must return Task.FromResult<T>(null);
    • Make sure any function returning Task is suffixed as "Async" to guide the caller
    • Don't use parallel for each with async/await, use tasks and Task.WhenAll (unless need custom partitioning)
    • Constructors and destructors cannot be marked async, therefore should not call async methods (no compiler warning)
    • For ctors - consider making it a factory with CreateInstanceAsync
    • For destructors - try to design against it, or fall back to Task.Run().GetAwaiter().GetResult() syntax (see final point)
    • When using .Wait() and .Result syntax, ensure it runs on a background thread with no context e.g. ... Task.Run(() => theMainAsync()).GetAwaiter().GetResult();
      [Technically GetAwaiter().GetResult() is not for public consumption and should be replaced with .Result, however I prefer it because it unwraps exceptions and it shows that you have purposefully coded as blocking code]
    General approach: In general, start off by making the interface return Task<> .. update the classes to return Task, then look to see if anything inside the function is truly async. If yes - mark the function async and use await (unless you can pass through).. if No, then just return a Task.FromResult to satisfy the interface.. Don't use async/await inside of a parallel loop. You can block here since its already async and on another thread, or change to tasks. If you *need* to convert to synchronous (such as for passing a value factory) then use Task.Run to encapsulate the blocking call, so that its on another thread with a different context and use .GetAwaiter().GetResult() to unwrap exceptions. I have uploaded some examples to GitHub: https://github.com/craigwardman/AsyncExamples
  • Published on
    You can use entity framework to make spatial queries against your Microsoft SQL Server database, which is great when the majority of the ORM code is using EF. What isn't great is that for this to work you have to add some native DLLs into your project, namely the "Microsoft.SqlServer.Types.dll" - this comes in 32 bit and 64 bit variants. Seemingly this also needs to appear in any top level application container, which is usually a different project to the one housing your ORM code. In the end these DLLs seem to get everywhere, except the deployed environment and cause a lot of problems at runtime if they're missing. For my purposes though, I didn't need full in-memory spatial support, I simply wanted to add spatial predicates to my where clauses and return normal "non-spatial" objects back from SQL Server. This is really easy to do in isolation, just write the SQL text and execute it directly using EF. However, most of the time you actually want to combine this with another set of undeterminate predicates which have been applied to the IQueryable in other parts of the code. To solve this issue, I wrote a helper which allows me tag on my spatial query as the last execution step of the IQueryable (e.g. replacing the ToList()) as follows:
    
    internal static class EFSpatialHelper
    {
            public static Task<List<T>> QueryByProximityAsync<T>(DbContext context, IQueryable<T> query, Proximity proximity)
            {
                string sqlPointGeogPreamble = $@"
    Declare @GeogPoint as geography
    Declare @GeogShape as geography
    
    SET @GeogPoint = geography::STGeomFromText('POINT (' + Cast(@longitude as varchar) + ' ' + Cast(@latitude as varchar) + ')', 4326)
    SET @GeogShape = @GeogPoint.STBuffer(@buffer)
    
    ";
                string sqlGeogPredicate = "@GeogShape.STIntersects(Point) = 1";
    
                List<SqlParameter> sqlParams = new List<SqlParameter>();
                sqlParams.Add(new SqlParameter("@longitude", proximity.Longitude));
                sqlParams.Add(new SqlParameter("@latitude", proximity.Latitude));
                sqlParams.Add(new SqlParameter("@buffer", proximity.RadiusInMeters));
    
                return InjectAndExecuteSql(context, query, sqlPointGeogPreamble, sqlGeogPredicate, sqlParams.ToArray());
            }
    
            internal static Task<List<T>> GetByWellKnownTextAsync<T>(DbContext context, IQueryable<T> query, string wellKnownText)
            {
                string sqlWktGeogPreamble = @"
    Declare @GeogPolygon as geography
    Declare @GeomPolygon as geometry
    
    Set @GeomPolygon = Geometry::STGeomFromText(@WKT, 4326)
    Set @GeogPolygon = Geography::STGeomFromWKB(@GeomPolygon.MakeValid().STUnion(@GeomPolygon.STStartPoint()).STAsBinary(), 4326)
    
    ";
                string sqlGeogPredicate = "Point.STIntersects(@GeogPolygon)=1";
    
                return InjectAndExecuteSql(context, query, sqlWktGeogPreamble, sqlGeogPredicate, new SqlParameter("@WKT", wellKnownText));
            }
    
            private static Task<List<T>> InjectAndExecuteSql<T>(DbContext context, IQueryable<T> query, string spatialPreamble, string spatialPredicate, params SqlParameter[] spatialParams)
            {
                ObjectQuery<T> objectQuery = GetQueryFromQueryable(query);
                string sqlQryText = $"{spatialPreamble}{objectQuery.ToTraceString()}";
    
                // the query text in here is quite simple, we know it doesnt have an order by or a group by, so just stick predicates on the end.
                if (!sqlQryText.ToUpper().Contains("WHERE"))
                {
                    sqlQryText = $"{sqlQryText} WHERE {spatialPredicate}";
                }
                else
                {
                    sqlQryText = $"{Regex.Replace(sqlQryText, "WHERE", "WHERE (", RegexOptions.IgnoreCase)}) AND {spatialPredicate}";
                }
    
                List<SqlParameter> sqlParams = new List<SqlParameter>();
                if (spatialParams != null)
                {
                    sqlParams.AddRange(spatialParams);
                }
    
                foreach (var p in objectQuery.Parameters)
                {
                    sqlParams.Add(new SqlParameter(p.Name, p.Value));
                }
    
                return context.Database.SqlQuery<T>(
                    sqlQryText,
                    sqlParams.ToArray()).ToListAsync();
            }
    
            private static ObjectQuery<T> GetQueryFromQueryable<T>(IQueryable<T> query)
            {
                var internalQueryField = query.GetType().GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).Where(f => f.Name.Equals("_internalQuery")).FirstOrDefault();
                var internalQuery = internalQueryField.GetValue(query);
                var objectQueryField = internalQuery.GetType().GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).Where(f => f.Name.Equals("_objectQuery")).FirstOrDefault();
                return objectQueryField.GetValue(internalQuery) as ObjectQuery<T>;
            }
    }
    
    Here is an example of the modified logic for executing a query:
    
    if (wellKnownText != string.Empty)
    {
        return await this.GetItemsInWellKnownTextAsync(context, fullQuery, wellKnownText).ConfigureAwait(false);
    }
    else if (proximity != null)
    {
        return await this.GetItemsInProximityAsync(context, fullQuery, proximity).ConfigureAwait(false);
    }
    else
    {
        return await fullQuery.ToListAsync().ConfigureAwait(false);
    }
    
  • Published on
    It is possible to create interactive graphical maps in many front end frameworks. In the past I have written mapping applications using Virtual Earth (JavaScript), Bing Maps (Silverlight), Bing Maps (JavaScript) and MapBox (JavaScript). The client side implementations vary, but there are common themes especially from a server side point of view. There are a lot of concepts that be represented in re-usable C# code. For this reason, I have created a class library of ".NET Mapping" objects and helpers, which represent the mapping concepts such as geographical points, bounding boxes, proximities, distance calculation and zoom level calculation. You can view or download the library on GitHub.
  • Published on
    Sometimes when using Microsoft Azure AAD for authentication the website goes into a redirect permaloop. So far, this has been caused by two different things for me - so I will collate the causes and solutions below:
    1. HTTP vs HTTPS - Since the Microsoft login server is running on HTTPS you must also run your website on HTTPS, since the login cookie will be issued using the secure flag, which prevents it from being read using HTTP. There are various ways to force SSL on your website, from global filters to URL re-writing, but in general make sure to setup your site URLs using HTTPS in the Azure portal and to browse to HTTPS.
    2. OWIN Middleware Cookie issue - There is a known issue with the OWIN middleware which means that it's cookies are sometimes lost when issued in conjunction with some other cookie, e.g. the session cookie. The easiest solution to this one I have found is to install a NuGet package called "Kentor.OwinCookieSaver" and then add the following line to startup.auth.cs
      
       public void ConfigureAuth(IAppBuilder app)
              {
                  app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
                  app.UseKentorOwinCookieSaver(); // <-- ADD THIS LINE
      
  • Published on
    The below C# code creates a sub-class of the System.Net.Http.HttpClient that deals with adding the "Authorization" header to subsequent calls, using OAuth client credentials grant.
    
    using System;
    using System.Collections.Generic;
    using System.Net.Http;
    using Newtonsoft.Json.Linq;
    
    namespace SomeProject.WebAPI.Client
    {
        internal sealed class SomeWebApiHttpClient : HttpClient
        {
            private static string authToken;
            private static DateTime authTokenTime;
    
            private SomeWebApiHttpClient(ISomeWebApiClientConfig config)
            {
                this.BaseAddress = new Uri(config.SomeWebApiBaseAddress);
            }
            
            public static async Task<SomeWebApiHttpClient> CreateInstanceAsync(ISomeWebApiClientConfig config)
            {
                var instance = new SomeWebApiHttpClient(config);
                instance.DefaultRequestHeaders.Add("Authorization", await GetAuthAsync(config.SomeWebApiTenantId, config.CallingClientId, config.CallingClientKey));
    
                return instance;
            }
    
            private static async Task<string> GetAuthAsync(string tenantId, string callingClientId, string callingClientKey)
            {
                if (string.IsNullOrEmpty(authToken) || DateTime.Now.Subtract(authTokenTime).TotalMinutes > 45)
                {
                    using (var authClient = new HttpClient())
                    {
                        authClient.BaseAddress = new Uri("https://login.microsoftonline.com/");
    
                        Dictionary<string, string> bodyContent = new Dictionary<string, string>();
                        bodyContent.Add("grant_type", "client_credentials");
                        bodyContent.Add("client_id", callingClientId);
                        bodyContent.Add("client_secret", callingClientKey);
                        bodyContent.Add("resource", "https://somedomain.onmicrosoft.com/APP-URI-ID");
    
                        var result = await authClient.PostAsync(
                            $"{tenantId}/oauth2/token",
                            new FormUrlEncodedContent(bodyContent)).ConfigureAwait(false);
    
                        if (result.IsSuccessStatusCode)
                        {
                            dynamic tokenResult = (dynamic)JValue.Parse(await result.Content.ReadAsStringAsync().ConfigureAwait(false));
    
                            authToken = $"{tokenResult.token_type} {tokenResult.access_token}";
                            authTokenTime = DateTime.Now;
                        }
                        else
                        {
                            Trace.WriteLine($"Failed to login to API - {await result.Content.ReadAsStringAsync().ConfigureAwait(false)}");
                            
                            return string.Empty;
                        }
                    }
                }
    
                return authToken;
            }
        }
    }
    
    In the above code you must correctly set the domain and APP-URI-ID for the WebAPI this client is supposed to be using (or pass them in the config if they are variable). The configuration is passed in via an interface so that the calling code can inject whatever implementation they have for storing the configuration. E.g. In the web project composition root, a class that reads from the web.config using ConfigurationManager. The interface is defined as below:
    
    public interface ISomeWebApiClientConfig
        {
            string SomeWebApiBaseAddress { get; }
            string SomeWebApiTenantId { get; }
            string CallingClientId { get; }
            string CallingClientKey { get; }
        }
    
    These settings can be found in the Azure portal within the active directory that defines the client/server applications. Now the rest of the code base can use this client to call the API's endpoints, without worrying about injecting the auth header. Example usage of the HttpClient to call an example API endpoint:
    
    using System.Collections.Generic;
    using System.Diagnostics;
    using Newtonsoft.Json.Linq;
    
    namespace SomeProject.WebAPI.Client
    {
        public class ExampleDataClient
        {
            private readonly ISomeWebApiClientConfig config;
    
            public ExampleDataClient(ISomeWebApiClientConfig config)
            {
                this.config = config;
            }
    
            public async Task<IEnumerable<SomeExampleData>> GetSomeExampleData(string someParam)
            {
                using (var client = await SomeWebApiHttpClient.CreateInstanceAsync(this.config).ConfigureAwait(false))
                {
                    var result = await client.GetAsync($"api/SomeExampleData").ConfigureAwait(false);
    
                    if (result.IsSuccessStatusCode)
                    {
                        var returnValue = JValue.Parse(await result.Content.ReadAsStringAsync().ConfigureAwait(false));
    
                        if (returnValue.HasValues)
                        {
    			List<SomeExampleData> outputList = new List<SomeExampleData>();
                           foreach (var item in returnValue)
                           {
                               outputList.Add(SomeExampleData.FromJObject(item));
                           }
                           
                           return outputList.AsEnumerable();
                        }
                        else
                        {
                            return Task.FromResult<IEnumerable<SomeExampleData>>(null);
                        }
                    }
                    else
                    {
                        Trace.WriteLine($"HTTP error: {result.RequestMessage.RequestUri} ({result.StatusCode}) - {await result.Content.ReadAsStringAsync().ConfigureAwait(false)}");
                        return Task.FromResult<IEnumerable<SomeExampleData>>(null);
                    }
                }
            }
        }
    }