June 13

Deploying an ASP.NET Project From a Multi-Project Solution Using GitHub Actions

I’ve been studying for the AZ-400: Designing and Implementing Microsoft DevOps Solutions exam. As part of the preparation for that exam, you build a bunch of CI/CD pipelines. Every example given has a GitHub repo with 1 solution and 1 web project. That’s great for demoware, but we aren’t writing demoware.

Let’s say you have a website you wrote about 10 years ago using an earlier version of ASP.NET (read that as non-.NET Core) that you want to modernize/revamp. The approach I took was to create a new ASP.NET Core web project next to the older version of the website in a single solution.

The idea is that I could take my time moving parts from the old project to the new and still have a running version of the old website. Putting my DevOps hat on, I wanted to create a CI/CD pipeline using GitHub Actions. I started building the pipeline using the examples available in the Azure/webapps-deploy repo. I got the pipeline to successfully execute, or at least run with no errors. It was deploying the entire solution directory! Well, that ain’t gonna work.

For the purposes of this project, I just want to deploy the ASP.NET Core project. Using the .NET Core CLI, we can build the entire solution or individual projects.

We need to build the ASP.NET Core project only. In this example, we are using the Release configuration and I’ve told it not to restore NuGet packages because I did it in a previous step (not included in this blog post).

    - name: Build
      run: dotnet build ./NewWebsite/NewWebsite.csproj -c Release --no-restore

Next we need to gather the output for the project to be deployed. This example will publish the output of the NewWebsite project into a directory called publish located in the root of the repository. It will do it with the Release configuration.

    - name: Publish
      run: dotnet publish ./NewWebsite/NewWebsite.csproj -o ./publish -c Release

Once that is all gathered/packaged up, we need to deploy to the target Azure App Service. The value for app-name came from the Azure Web App Publish Profile. The publish-profile value is going to be the Azure Web App Publish Profile copied from Azure and added as a secret to your GitHub repository. The last part and the reason for this blog post, is the package setting. This should point to the directory you want to deploy.

    - uses: azure/webapps-deploy@v2
      with:
        app-name: 'newwebsite'
        publish-profile: ${{ secrets.azureWebAppPublishProfile }}
        package: ./publish/

That will deploy the contents of the publish directory to Azure. The trailing slash is important, if you leave that off, it will deploy the publish directory itself which is incorrect.

That’s what worked for me. I hope this helps you with your DevOps experience.

June 1

Microsoft Certified: Azure Developer Associate

Microsoft Certified: Azure Developer Associate

As I posted back in March, I have completed another Azure certification (Microsoft Certified: Azure Developer Associate). This time I took the AZ-204: Developing Solutions for Microsoft Azure exam. I took the exam back on April 15th and because the exam was in beta when I took it, I just got the results of the exam on May 28th. It was the first exam I have taken here at the house as opposed to going to a testing center. It wasn’t too bad except for the dogs barking and not being able to get up to tell them to knock it off.

The next one up will be Exam AZ-400: Designing and Implementing Microsoft DevOps Solutions to get the Microsoft Certified: DevOps Engineer Expert.

Thanks to this COVID-19 stuff, I’m still looking for a new opportunity. If you are looking for a .NET Core developer, let me know.

May 7

Virtual Brown Bag Meetings

Just recently Claudio Lassala and George Mauer have resurrected their Virtual Brown Bag Meetings on Thursdays from 12-1 pm Central. I like to attend them because it gives me a look at what people are doing outside of the tech stack I usually play in or ideas for new ways to play with my current tech stack.

Claudio posted something about the last meeting on LinkedIn where he said something to the effect of “come get alittle better than you are now”. To that end, I’d like to share a piece of info I learned in today’s meeting: Advanced Distributed Systems Desing (Online Course) from Particular Software is now FREE for a limited time. If you haven’t heard of Particular, you should know that Udi Dahan works there.

If you have time next Thursday between 12 and 1 come join the meeting!

March 30

Azure Certifications 2020

So thanks to this COVID-19 stuff, I suddenly find myself with more time on my hands. In an effort to try not to waste this time, I’ve decided to start taking some Microsoft Azure certifications. I have already taken AZ-900 Microsoft Azure Fundamentals. So the next step is gonna be AZ-204 Developing Solutions for Microsoft Azure which will give me an Azure Developer Associate certification. (Thanks for the idea Marianna!)

Oh, if you are looking for a senior .NET developer with Azure experience, let’s talk and see if I can help you out.

November 16

External Network Access to Kestrel in ASP.NET Core

Yesterday I gave a talk at a local user group and I ran into some networking issues with my demo. After some searching for a bit, I found the solution in this blog post: External Network Access to Kestrel and IIS Express in ASP.NET Core.

The missing command that I could not find yesterday is as follows:
dotnet run --urls http://0.0.0.0:5001

By running that command before you run your ASP.NET Core app, it will allow you to override the app’s default URLs.

August 7

Task From A Code Test

Earlier this week I took a coding test and the first task in the test was interesting and alittle fun to complete. At the start you are given a text file with integers (one per line) that are used as an input into another process. This process has been failing because of problems with the input file. You are tasked with writing a program to validate the input files and output any errors to a separate file for use in troubleshooting. The task is to validate the file according to a set of rules.

The rules are as follows:
1.Verify that all integers in the file fall between 1 and N where N is the total number of lines in the text file.
2.Verify that all integers between 1 and N exist in the file (So if the file contains 3 lines, they should be 1, 2, and 3 in any order).
3.Output any errors to another file.

Rather than use a bunch of loops directly to iterate through the lines, I opted for a LINQ approach. Having done LINQ for so long, that approach usually looks better to me than writing the loops directly. The code is on GitHub and is available by clicking here.

Category: C#, LINQ | LEAVE A COMMENT
August 4

Rebooting & Blogging

I had originally created this blog years ago and tried to get into blogging. I got a few posts up but then kind of lost interest when life started happening. So when I was recently and unexpectedly given an opportunity to “reboot” my career, I remembered some .NET Rocks! podcasts from the last year that addressed taking your carreer to the next level (Working on your Career with John Sonmez and Managing Your Career with John Sonmez). I decided to go check out his website SimpleProgrammer.com to see what I could find that might help me. One of the things I found was his online 3 week blogging course. I wasn’t terribly motivated to blog, but I know blogging would help me and I decided to give it a shot. There are no silver bullets with this, but the course gives you a path forward to building or re-building your blog as the case may be. It turns out that is exactly what I needed to get going.

If you are looking to start a blog or just find something to help jumpstart your blog, I would recommend checking out John Sonmez’s Blog Course.

August 3

Registering Types With the SimpleIoC Container

I’ve been kicking the tires on the Command Query Seperation principle/pattern lately. I’ve been using the CQS-Sample as a starting point. So far, the approach to breaking things down into Commands and Queries is pretty slick and much easier to unit test. The interfaces are used for dependency injection with an IoC container like SimpleIoC (which comes with MVVMLight). If you have done any work with IoC containers, you’ll know that you need to register your types with the container either explicitly or through some kind of convention. With a few classes it’s easy to just bang out the few lines of code you need to do it. In this case, the number of commands and queries is growing and having to explicitly register each one is becoming monotonous. So how to I register my queries and commands without having to write any code to explicitly register my types?

Assumptions:

  1. We are working with MVVMLight and SimpleIoC.
  2. For any query or command processor interface there is exactly 1 implementation of it.
  3. All queries are marked with an IQuery interface.
  4. All commands are marked with an ICommand interface (not to be confused with the System.Windows.Input.ICommand).
  5. All command processors implement ICommandProcessor<TCommand> where TCommand is a class that inherits from ICommand.

Step 1: Get an instance of MethodInfo for SimpleIoC’s Register<TInterface, TClass>() method.

Normally, getting the MethodInfo for a method you’d like to invoke is more straight forward. This method has 8 overloads so some LINQ was needed to get the correct one.

var registerMethodInfo = SimpleIoc.Default.GetType().GetMethods()
.Where(m => m.Name == "Register")
.Select(m => new
{
Method = m,
Params = m.GetParameters(),
Args = m.GetGenericArguments()
})
.Where(x => x.Params.Length == 0)
.Where(x => x.Args.Length == 2)
.Select(x => x.Method)
.First();

Step 2: Get a list of all types in the current AppDomain.

This step is pretty straightforward. Notice the .ToList() call at the end of method chain. This is to make sure that when we use types in the following steps, we won’t be enumerating the IEnumerable of types more than once.

var types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).ToList();

Step 3: Get all query interfaces that inherit from IQuery.

var queryInterfaces = from t in types
where t != typeof (IQuery)
where t.IsInterface
where typeof (IQuery).IsAssignableFrom(t)
select t;

Step 4: Create a query implementation class to query interface mapping

This step creates the query interface to query type mapping. The result is projected into an anonymous type that will be easy to use in the next step.

var queryMappings = from queryInterface in queryInterfaces
from t in types
where t.GetInterface(queryInterface.Name) == queryInterface
select new {queryInterface, Query = t};

Step 5: Register the query types

This is the last bit of reflection needed to register the queries. This code iterates of the query mappings from the previous step, gets a generic MethodInfo using the interface and implementation, and then invokes it on the current SimpleIoC instance.

foreach (var queryMapping in queryMappings)
{
var genericRegisterMethodInfo = registerMethodInfo.MakeGenericMethod(queryMapping.queryInterface, queryMapping.Query);
genericRegisterMethodInfo.Invoke(SimpleIoc.Default, null);
}

Step 6: Get a list of classes that inherit from ICommand.

var commands = from t in types
where t != typeof (ICommand)
where typeof (ICommand).IsAssignableFrom(t)
select t;

Step 7: Make the ICommandProcessor<T> interfaces.

This is an interesting step in the process. The command processors do not have a common non-generic interface, so we’ll have to make the generic type of ICommandProcessor<TCommand>.

 

var commandProcessorInterfaces = from command in commands
select new {CommandProcessorInterface = typeof (ICommandProcessor<>).MakeGenericType(command)};

Step 8: Map ICommandProcessor<T> interfaces to ICommandProcessor<T> implementations

Now we’ll create the mapping between the ICommandProcessor<TCommand>s and their implementations.

var commandProcessorMappings = from commandProcessorInterface in commandProcessorInterfaces
from t in types
where t.GetInterface(commandProcessorInterface.CommandProcessorInterface.Name) == commandProcessorInterface.CommandProcessorInterface
select new {commandProcessorInterface.CommandProcessorInterface, CommandProcessor = t};

Step 9: Register the command processor types

Same thing as step 5. Just iterating over the command mappings to make the generic MethodInfos and then invoking them on the current SimpleIoC instance.

foreach (var commandProcessorMapping in commandProcessorMappings)
{
var genericRegisterMethodInfo = registerMethodInfo.MakeGenericMethod(commandProcessorMapping.CommandProcessorInterface, commandProcessorMapping.CommandProcessor);
genericRegisterMethodInfo.Invoke(SimpleIoc.Default, null);
}

And that’s how I register all my command processors and queries through reflection. LINQ and Reflection FTW!

Are there better approaches? Sure. Are there better IoC containers out there? You betcha. But, this approach is Good Enough™ for now.

Category: C#, LINQ | LEAVE A COMMENT