Monthly Archives: February 2020

Add Entity Framework Core to an Existing Asp.Net Core Project

This is one of those posts born of the fact that I’m constantly googling this, and coming up with videos and Microsoft docs – I just want a single, quick step by step guide; so here it is.

Step One – NuGet packages

If you’re using SQL Server, then you’ll need these packages (technically, you only need the first, unless you want to actually create or run a migration yourself):

Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Tools

Step Two – Create DBContext (and entity classes if needed)

The minimum DBContext looks like this:

    public class MyDbContext : DbContext
    {

        public MyDbContext(DbContextOptions<MyDbContext> options)
            : base(options)
        {

        }

        public DbSet<MyEntity> MyEntities { get; set; }
        public DbSet<MyOtherEntity> MyOtherEntities { get; set; }
    }

You can feed your model directly into EF. That’s much easier to do than it used to be, for example, any field called Id will automatically be inferred to be a Primary Key; however, at some point, you’re going to want to do something data specific to your model, and at that point, you lose the separation between data and business layers. A nice way that I’ve found around this is to subclass your models:

    public class MyEntity : MyModel
    {
    }

Step Three – Startup.cs

You’ll need to register the DbContext in your DI pipeline:

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddDbContext<MyDbContext>(a =>
                a.UseSqlServer(Configuration.GetConnectionString("SqlConnectionString")));
        }

Your connection string goes in appsettings.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "SqlConnectionString": "Server=(localdb)\\mssqllocaldb;Database=MyDatabase;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

Step Four – Migration

Since you’ve installed EF Tools, you can add a migration:

Add-Migration InitialMigration -StartupProject MySolution.MyProject

The other option is to switch on automatic migrations; although, I would personally advise against that, as you can quickly lose track of what it’s doing.

That’s it – once you run:

Update-Database

You should have a working EF instance.

Step Five (optional) – Auto-Migration

You may also wish to have the system upgrade itself to the latest version of the DB. Whether you choose to do this depends on exactly how much control you need over the changes to your DB. I’ve heard that some people create the migrations as SQL Scripts and hand them to a DBA.

Should you wish to have EF manage that for you, then you can do the following:

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            UpdateDatabase(app);

. . .

private void UpdateDatabase(IApplicationBuilder app)
        {
            using var serviceScope = app.ApplicationServices
                .GetRequiredService<IServiceScopeFactory>()
                .CreateScope();

            using var context = serviceScope.ServiceProvider.GetService<CourseSelectDbContext>();

            context.Database.Migrate();            
        }

That’s it – now, next time I want to find this, I can search my own website!

References

https://docs.microsoft.com/en-us/ef/core/get-started/install/

https://docs.microsoft.com/en-us/aspnet/core/data/ef-rp/intro?view=aspnetcore-3.1&tabs=visual-studio

https://blog.rsuter.com/automatically-migrate-your-entity-framework-core-managed-database-on-asp-net-core-application-start/

An ADR Visual Studio Tool – Part 6 – Visually Opening Files and Changing the Command Title

Following on from this post, which is the latest in a series on creating a Visual Studio Extension, I’m covering a couple of loose ends.

Visually Opening Files

In order to cause Visual Studio to open a file, you’ll need to call .Open on the ProjectItem; this returns a Window; if you then want this window to become visible, simply tell it so. All of this must be done on the UI thread:

            await Microsoft.VisualStudio.Shell.ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
            var window = originalProjectItem.Open();
            window.Visible = true;

Change the Command Title

When you create an extension, by default it gets the same name as the project with the word ‘Window’ appended to it; in my case, that was AdrWindow. In order to change that, have a look for the file:

[ProjectName]Package.vsct

In my case, that was:

AdrPackage.vsct

For the default set-up, the button text will he here:

        <Strings>
          <ButtonText>Adr Manager</ButtonText>
        </Strings>

References

https://stackoverflow.com/questions/26574839/developing-a-visual-studio-extension-how-to-open-a-source-file-and-jump-to-a-sp

My object won’t deserialise using System.Text.Json

I imagine this is a very common scenario, so I thought it worth documenting.

Let’s take the following controller method in a bog standard Asp.Net Core API:

        public async Task<DataContainer> Get()
        {
            var data = Enumerable.Range(1, 5).Select(index => new MyData
            {
                Value1 = index,
                Value2 = "some other value"
            })
            .ToList();

            return new DataContainer()
            {
                Data = data
            };
        }

Here’s a method that consumes it:

            var client = _clientFactory.CreateClient();
            var result = await client.GetAsync($"https://localhost:22222/endpoint");

            using var responseStream = await result.Content.ReadAsStreamAsync();
            var data = await JsonSerializer.DeserializeAsync<DataContainer>(responseStream);

So, you’ve expect this to work, right? It makes no difference what DataContainer looks like, because you’re serialising and deserialising using the same technology. In fact, it just returns a correctly structured object, but every value will be null.

I’ll tell you what does work, is this:

            var client = _clientFactory.CreateClient();
            var result = await client.GetAsync($"https://localhost:22222/endpoint");

            var responseString = await result.Content.ReadAsStringAsync();
            var data = Newtonsoft.Json.JsonConvert.DeserializeObject<DataContainer>(responseString);             

If you look at the JSON, it looks kosher enough (again, the exact structure is irrelevant). So, what’s the issue?

Interestingly, when the JSON is serialised, it is serialised into camelCase; so if you have a variable such as MyData, it will get serialised as myData. Newtonsoft deals with this, because it’s a common use case in .Net; however, System.Text.Json.Deserialize* assumes that the casing will match the object!

This is odd considering it changes it on the way out!!

Okay, so what’s the fix?

You just tell Deserialize* to use camelCase:

            var client = _clientFactory.CreateClient();
            var result = await client.GetAsync($"https://localhost:22222/endpoint");

            using var responseStream = await result.Content.ReadAsStreamAsync();

            var options = new JsonSerializerOptions()
            {
                PropertyNamingPolicy = JsonNamingPolicy.CamelCase
            };
            var data = await JsonSerializer.DeserializeAsync<DataContainer>(responseStream, options);

Annoying, eh?

References

https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#table-of-differences-between-newtonsoftjson-and-systemtextjson

https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-how-to#use-camel-case-for-all-json-property-names

Using HttpClientFactory

In .Net Framework (prior to .Net Core), HttpClient was something of a pain. You essentially kept it around in a static variable. If you didn’t, and used too many of them, you could end up issues such as socket exhaustion.

I think most people got around this by implementing their own version of a HttpClientFactory… but now you don’t need to.

In this post, I’m covering the simplest possible use case inside an Asp.Net Core application. The articles linked at the bottom of this post go into much more detail.

Usage

The first step to using the HttpClientFactory is to add it to the IoC:

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();
            services.AddHttpClient();
        }

Then, simply inject it into your controller; for example:

        public HomeController(ILogger<HomeController> logger,
            IHttpClientFactory clientFactory)
        {
            _logger = logger;
            _clientFactory = clientFactory;
        }

Finally, you request a client like this:

var client = _clientFactory.CreateClient();

Then you can use it as a normal HttpClient:

var result = await client.GetAsync($"https://localhost:22222/endpoint");

References

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-3.1

https://www.stevejgordon.co.uk/introduction-to-httpclientfactory-aspnetcore

Calling an Azure Signalr Instance from an Azure function

I’ve been playing around with the Azure Signalr Service. I’m particularly interested in how you can bind to this from an Azure function. Imagine the following scenario:

You’re sat there on your web application, and I press a button on my console application and you suddenly get notified. It’s actually remarkably easy to set-up (although there are definitely a few little things that can trip you up – many thanks to Anthony Chu for his help with some of those!)

If you want to see the code for this, it’s here.

Create an Azure Signalr Service

Let’s start by setting up an Azure Signalr service:

You’ll need to configure a few things:

The pricing tier is your call, but obviously, free is less money than … well, not free! The region should be wherever you plan to deploy your function / app service to, although I won’t actually deploy either of those in this post, and the ServiceMode should be Serverless.

Once you’ve created that, make a note of the connection string (accessed from Keys).

Create a Web App

Follow this post to create a basic web application. You’ll need to change the startup.cs as follows:

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSignalR().AddAzureSignalR();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            //app.UseDefaultFiles();
            //app.UseStaticFiles();

            app.UseFileServer();
            app.UseRouting();
            app.UseAuthorization();

            app.UseEndpoints(routes =>
            {
                routes.MapHub<InfoRelay>("/InfoRelay");
            });
        }

Next, we’ll need to change index.html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
    
    <script src="lib/@microsoft/signalr/dist/browser/signalr.js"></script>
    <script src="getmessages.js" type="text/javascript"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">

</head>
<body>
    <div class="container">
        <div class="row">
            <div class="col-2">
                <h1><span class="label label-default">Message</span></h1>
            </div>
            <div class="col-4">
                <h1><span id="messageInput" class="label label-default"></span></h1>
            </div>
        </div>
        <div class="row">&nbsp;</div>
    </div>
    <div class="row">
        <div class="col-12">
            <hr />
        </div>
    </div>
</body>

</html>

The signalr package that’s referenced is an npm package:

npm install @microsoft/signalr

Next, we need the getmessages.js:

function bindConnectionMessage(connection) {
    var messageCallback = function (name, message) {
        if (!message) return;

        console.log("message received:" + message.Value);

        const msg = document.getElementById("messageInput");
        msg.textContent = message.Value;
    };
    // Create a function that the hub can call to broadcast messages.
    connection.on('broadcastMessage', messageCallback);
    connection.on('echo', messageCallback);
    connection.on('receive', messageCallback);
}

function onConnected(connection) {
    console.log("onConnected called");
}

var connection = new signalR.HubConnectionBuilder()
    .withUrl('/InfoRelay')
    .withAutomaticReconnect()
    .configureLogging(signalR.LogLevel.Debug)
    .build();

bindConnectionMessage(connection);
connection.start()
    .then(function () {
        onConnected(connection);
    })
    .catch(function (error) {
        console.error(error.message);
    });

The automatic reconnect and logging are optional (although at least while you’re writing this, I would strongly recommend the logging).

Functions App

Oddly, this is the simplest of all:

    public static class Function1
    {       
        [FunctionName("messages")]
        public static Task SendMessage(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post")] object message,
            [SignalR(HubName = "InfoRelay")] IAsyncCollector<SignalRMessage> signalRMessages)
        {
            return signalRMessages.AddAsync(
                new SignalRMessage
                {
                    Target = "broadcastMessage",
                    Arguments = new[] { "test", message }
                });
        }
    }

The big thing here is the binding – SignalRMessage binding allows it to return the message to the hub (specified in HubName). Also, pay attention to the Target – this needs to match up the the event that the JS code is listening for (in this case: “broadcastMessage”).

Console App

Finally, we can send the initial message to set the whole chain off – the console app code looks like this:

        static async Task Main(string[] args)
        {
            Console.WriteLine($"Press any key to send a message");
            Console.ReadLine();

            HttpClient client = new HttpClient();
            string url = "http://localhost:7071/api/messages";
            
            HttpContent content = new StringContent("{'Value': 'Hello'}", Encoding.UTF8, "application/json");

            HttpResponseMessage response = await client.PostAsync(url, content);
            string results = await response.Content.ReadAsStringAsync();

            Console.WriteLine($"results: {results}");
            Console.ReadLine();
        }

So, all we’re doing here is invoking the function.

Now when you run this (remember that you’ll need to run all three projects), press enter in the console app, and you should see the “Hello” message pop up on the web app.

References

https://docs.microsoft.com/en-us/aspnet/core/signalr/javascript-client?view=aspnetcore-3.1

https://docs.microsoft.com/en-us/aspnet/core/signalr/dotnet-client?view=aspnetcore-3.1&tabs=visual-studio

https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-signalr-service?tabs=csharp

Add Application Insights to an Azure Resource

Application Insights provides a set of metric tools to analyse the performance and behaviour of various Azure services. For example, you can see how many calls you have to your Azure Web site, or you can see how many errors your service has generated.

This post is concerned with the scenario where you would want to manually log to application insights. The idea being that, in addition to the above metrics, you can output specific log messages in a central location. You might just want to log some debug information (“Code reached here”, “now here” – don’t try and say you’ve never been there!) Or you might find that there is a particular event in your program that you want to track, or maybe you’ve got two different resources, and you’re trying to work out how quick or frequent the communication between them is.

Set-up

The first step is to set-up a new App Insights service in the Azure Portal (you can also use the recently released Azure Portal App).

Select to create a new resource, and pick Application Insights:

When you create the resource, you’ll be asked for some basic details (try to keep the location in the same region as the app(s) you’ll be monitoring):

The instrumentation key is shown in the overview, and you will need this later:

You should be able to see things like failed requests, response time, etc. However, we’ve just configured this, so it’ll be quiet for now:

Check the “Search” window (which is where log entries will appear):

The other place you can see the output is “Logs (Analytics)”.

Create Web Job

The next thing we need is something to trace; let’s go back to a web job.

Once you’ve set-up your web job, app AppInsights from NuGet:

Install-Package ApplicationInsights.Helpers.WebJobs

The class that we’re principally interested here is the TelemetryClient. You’ll need to instantiate this class; there’s two ways to do this:

var config = Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.CreateDefault();
 
var tc = new TelemetryClient(config);

This works if you link the App Insights to the resource that you’re tracking in Azure; you’ll usually to that here:

Once you’ve switched it on, you can link your resource; for example:

The other way to link them, without telling Azure that they are linked, is this:

TelemetryConfiguration.Active.InstrumentationKey = InstrumentationKey;

(You can use the instrumentation key that you noted earlier.)

Tracing

Now you’ve configured the telemetry client, let’s say you want to track an exception:

    var ai = new TelemetryClient(config);
    ai.TrackException(exception, properties);

Or you just want to trace something happening:

    var ai = new TelemetryClient(config);
    ai.TrackTrace(text);

Side note

The following code will result in a warning, telling you that it’s deprecated:

    var ai = new TelemetryClient();
    ai.TrackTrace(text);

References

http://pmichaels.net/2017/08/13/creating-basic-azure-web-job/

https://blogs.msdn.microsoft.com/devops/2015/01/07/application-insights-support-for-multiple-environments-stamps-and-app-versions/

https://docs.microsoft.com/en-us/azure/application-insights/app-insights-cloudservices

https://docs.microsoft.com/en-us/azure/application-insights/app-insights-windows-desktop

https://github.com/MicrosoftDocs/azure-docs/issues/40482