Dev Stories

#CASBAN6: Function base class (and an update to the DTO models)

#CASBAN6: Function base class (and an update to the DTO models)

After we have been setting up our Azure Function project last time, we are now able to create a base class for our Azure functions. The main goal is to achieve a common configuration for all functions to make our life easier later on.

CRUD defintion

Our API endpoints should provide us a CRUD (Create-Read-Update-Delete) interface. Our implementation will reflect this pattern as follows:

  • A creation endpoint
  • Two read endpoints – one for list results (like a list of posts) and one for receiving details of an entity
  • An update endpoint to change existing entities
  • A delete endpoint

As all of our entities are tied to a single blog’s Id, we will use this base class for all entities besides the Blog entity itself.

The base class

    public abstract class BlogFunctionBase
    {
        internal readonly BlogContext BlogContext;
        internal ILogger? Logger;
        internal JsonSerializerSettings? JsonSerializerSettings;

        protected BlogFunctionBase(BlogContext blogContext)
        {
            BlogContext = blogContext ?? throw new ArgumentNullException(nameof(blogContext));

            CreateNewtonSoftSerializerSettings();
        }

        private void CreateNewtonSoftSerializerSettings()
        {
            JsonSerializerSettings = NewtonsoftJsonObjectSerializer.CreateJsonSerializerSettings();

            JsonSerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();

            JsonSerializerSettings.NullValueHandling = NullValueHandling.Ignore;
            JsonSerializerSettings.Formatting = Formatting.Indented;
            JsonSerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
            JsonSerializerSettings.DateFormatHandling = DateFormatHandling.IsoDateFormat;
            JsonSerializerSettings.DateParseHandling = DateParseHandling.DateTimeOffset;
        }

        public virtual Task<HttpResponseData> Create([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequestData req,
            string blogId) =>
            throw new NotImplementedException();

        public virtual Task<HttpResponseData> GetList([HttpTriggerAttribute(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequestData req, string blogId) =>
            throw new NotImplementedException();

        public virtual Task<HttpResponseData> GetSingle([HttpTriggerAttribute(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequestData req, string blogId, string id) =>
            throw new NotImplementedException();

        public virtual Task<HttpResponseData> Update([HttpTriggerAttribute(AuthorizationLevel.Anonymous, "put", Route = null)] HttpRequestData req, string blogId, string id) =>
            throw new NotImplementedException();

        public virtual Task<HttpResponseData> Delete([HttpTriggerAttribute(AuthorizationLevel.Anonymous, "delete", Route = null)] HttpRequestData req, string blogId, string id) =>
            throw new NotImplementedException();

    }

Knowing the definition of our API endpoints, there shouldn’t be any surprises with that implementation. As a base class, the definition is of course abstract.

In the constructor, I am setting up how JSON objects will be handled. I am following common practices in formatting. The only thing that is different from using JSON.NET directly is the fact we need to explicitly use the NewtonsoftJsonObjectSerializer.CreateJsonSerializerSettings method to create an instance of the JsonSerializerSettings property.

We also have an internal ILogger? property, which will be used in the derived class to create a typed instance for logging purposes. Last but not least, I am enforcing passing in a BlogContext instance from our Entity Framework implementation.

If you look at the method declaration, you’ll see the authorization level is set to Anonymous. As I am using Azure Active Directory, I am handling the authorization on a separate layer (there will be a post on that topic as well). Besides that, there is nothing special. All methods need a blogId and those endpoints that interact with a resource need a resource id as well.

The blog entity has a similar structure, with some differences in the parameter definitions. To keep things simple, I decided to let it be different from the base class definition above. We will see this in the next post of this series.

Update to the DTO models

While the blog series is ongoing, it still lags a bit behind on what I am currently working on (you can follow the dev branch on GitHub for an up-to-date view). As I am currently working on the administration client for our blog, I started to implement an SDK that can be used by all clients (the blog’s website will also just be a client). See the original post on DTOs here.

To be able to create a generic implementation of the calls to the API endpoints, I needed to create also a base class for the DTO models. It is a very simple class, as you can see:

public abstract class DtoModelBase
{
    public virtual Guid? BlogId { get; set; }

    public virtual Guid? ResourceId { get; set; }
}

This base class allows me to specify a Type that derives from it in the SDK API calls – which was my main goal. Second, I have now a common ResourceId property instead of the Id being named after the class name of the DTO. Both properties are virtual to allow me to specify the Required attributes in the derived classes as needed. You can see these changes on GitHub.

The reason I am writing about the change already today is that it will have impact on how the functions are implemented, as both the API functions and the Client SDK use the same DTO classes.

Conclusion

In this post, we had a look at the base class for our Azure functions we will use as an API and on the updated DTO models. With this prerequisite post in place, we will have a look at the function implementations in the next post.

Until the next post, happy coding, everyone!

Posted by msicc in Azure, Dev Stories, MAUI, 0 comments
#CASBAN6: Setting up an Azure Functions project for the API

#CASBAN6: Setting up an Azure Functions project for the API

Now that we have our Entity Framework project in place and our DTO mappings ready, it is finally time to create the API for our blogging engine. I am using Azure Functions for this.

Out-of-Process vs. In-Process

Azure Functions can be run in-process or in an isolated process. The isolated project decouples the function app from the underlying process, which enables additional features like custom middleware and Dependency Injection. Besides that, it allows you to run non-LTS versions, which can be helpful sometimes. These were the main reasons for choosing the Out-of-Process model. If you want to learn more about that topic, I recommend reading the docs.

Create the project

As you may have noticed, I recently became kind of a fan of JetBrains’ Rider IDE. Some steps may be different if done in Visual Studio, but you will be able to follow along.

First, make sure you have the Azure plugin installed. Go to the Settings, and select Plugins on the list at the left-hand side. Search for ‘Azure’ and install the Azure Toolkit for Rider. You will need to restart the application.

JetBrains Rider Plugin Settings

Once you have the plugin installed, open your solution and create a new project in it (I made it in a separate folder). Select the Azure Functions template on the left.

JetBrains Rider New Project dialog with Azure Functions selected.

I named the project BlogFunctions. Select the Isolated worker runtime option, and as Framework, we keep it on .NET 6 for the time being.

NuGet packages and project references

To enable all the functionalities we are going to use and add, we need some NuGet packages:

  • Microsoft.Azure.Functions.Worker, Version=1.10.0
  • Microsoft.Azure.Functions.Worker.Sdk, Version=1.7.0
  • Microsoft.Azure.Functions.Worker.Extensions.Http, Version=3.0.13
  • Microsoft.Azure.Functions.Worker.Extensions.ServiceBus, Version=5.7.0
  • Microsoft.Extensions.DependencyInjection, Version=6.0.1
  • Microsoft.Azure.Core.NewtonsoftJson, Version=1.0.0

Please note that I use the latest version that support .NET 6 and not the .NET 7. We also need to reference the projects we already created before, as you can see in this picture:

Project and Package references in the Function app

Program.cs

Now we finally can have a look at our Program.cs file. I am not using top-level statements here, but feel free if you want to, the code doesn’t change, just the surroundings.

To make our application running, we need to create a new HostBuilder object. I still prefer Newtonsoft.Json over System.Text.Json, so let’s add that one first:

IHost? host = new HostBuilder().ConfigureFunctionsWorkerDefaults(worker => worker.UseNewtonsoftJson()).Build();

host.Run();

In order to be able to use our Entity Framework project we created already earlier, we need to add a ConnectionString and also configure the application to instantiate our DBContext. Update the code above as follows:

string? sqlConnectionString = Environment.GetEnvironmentVariable("SqlConnectionString");

IHost? host = 
	new HostBuilder().
		ConfigureFunctionsWorkerDefaults(worker => worker.UseNewtonsoftJson()).              
		ConfigureServices(services =>
		{
			if (!string.IsNullOrWhiteSpace(sqlConnectionString))
				services.AddDbContext<BlogContext>(options =>
					options.UseSqlServer(sqlConnectionString));	              
		}).
		Build();

host.Run();

The connection string will be read from the local.settings.json file locally and from the Function app’s configuration on Azure. For the moment, just add your local ConnectionString:

{
  "IsEncrypted": false,
    "Values": {
        "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
        "AzureWebJobsStorage": "UseDevelopmentStorage=true",
        "SqlConnectionString": "Data Source=localhost;Initial Catalog=localDB;User ID=sa;Password=thisShouldB3Stronger!",
        
    }
}

Side note: If you have a look into the GitHub repo, you will see that there are some entries for OpenAPI in both files. The OpenAPI integration will get its own blog post later in this blog series, but for this post, they are not important.

Conclusion

In this post, we had a look at how to set up an Azure Functions app with Newtonsoft.Json and our Entity.Framework DbContext. In the next post, we will have a look at some Extensions that will be helpful, as well as the base class implementation of most functions within the app. As always, I hope this post was helpful for some of you.

Until the next post, happy coding, everyone!

Posted by msicc in Azure, Dev Stories, 2 comments
#CASBAN6: the DTOs and mappings

#CASBAN6: the DTOs and mappings

We already have created our database and our entities, so let’s have a look at how we bring the data to our API consuming applications.

If we recap, our entity models contain all the relations and identifiers. This could lead to some issues like circular references during serialization and unnecessary data repetition. Luckily for us, there is already a solution for this—it’s called data transfer object (DTO). The main purposes of a DTO is to serve data while being serializable (see also Wikipedia).

The DTO project

If you have been following along, you might already have guessed that I have created a separate project for the DTO model classes. The overall structure is similar to what you have already seen in my last post, where I showed you the entity model.

Implementation

Let’s have an exemplary look at the Medium entity class:

using System;
using System.Collections.Generic;

namespace MSiccDev.ServerlessBlog.EntityModel
{
    public class Medium
    {
        public Guid MediumId { get; set; }

        public Uri MediumUrl { get; set; }

        public string AlternativeText { get; set; }

        public string Description { get; set; }

        public Guid MediumTypeId { get; set; }
        public MediumType MediumType { get; set; }

        public Guid BlogId { get; set; }
        public Blog Blog { get; set; }

        public ICollection<Post> Posts { get; set; }
        public ICollection<Author> Authors { get; set; }

        public List<PostMediumMapping> PostMediumMappings { get; set; }

    }
}

The entity contains all relationships on the database. Our API will constrain a lot of them already down (we will see in a later post how), for example by requiring the BlogId for every call as primary identifier. There are a lot of other connection points, but we also want to be able to use the Medium endpoint just for managing media.

Here is the Medium DTO:

using System;
namespace MSiccDev.ServerlessBlog.DtoModel
{
    public class Medium
    {
        public Guid MediumId { get; set; }

        public Uri MediumUrl { get; set; }

        public string AlternativeText { get; set; }

        public string Description { get; set; }

        public MediumType MediumType { get; set; }

        public bool? IsPostImage { get; set; } = null;
    }
}


The class contains all the information we need. With this DTO, we will be able to manage media files alone but also in its usage context (which is mostly within posts of a blog).

Mapping helpers

To convert entity objects to data transfer objects and vice versa, we are using mappings. Mappings are converters that bring the data into the desired shape. On the contrary to our model classes, mappings are allowed to modify data during the conversion.

No library this time

If you are wondering why I am not using one of the established libraries for mappings, there are several reasons. When I came to the point of DTO implementation in the developing process, I evaluated the options for the mappings.

All of them had quite a learning curve, in the end, I was faster writing my own mappings. On bigger systems like shops or similar projects, I would probably have chosen the other path. There is also a small chance I change my mind one day, which would result in a refactoring session then.

Both mapping helper classes are, once again, in their own project.

Converting entities to DTOs

As you can see in the EntityToDtoMapExtensions class, I created extension methods for all entity objects. To remain on the Medium class, here are the particular implementations (there should be no surprise):

public static DtoModel.Medium ToDto(this EntityModel.Medium entity)
{
    return new DtoModel.Medium()
    {
        MediumId = entity.MediumId,
        MediumType = entity.MediumType.ToDto(),
        MediumUrl = entity.MediumUrl,
        AlternativeText = entity.AlternativeText,
        Description = entity.Description
    };
}

public static DtoModel.MediumType ToDto(this EntityModel.MediumType entity)
{
    return new DtoModel.MediumType()
    {
        MediumTypeId = entity.MediumTypeId,
        MimeType = entity.MimeType,
        Name = entity.Name,
        Encoding = entity.Encoding
    };
}

You may have noticed that I am not setting the IsPostImage property from within the extension. The information is only important in the context of a post, which is why the ToDto method for the post is setting it to true or false. Otherwise, it will be null and can be omitted in the API response.

Converting DTOs to entities

There are two scenarios where we need to convert DTOs to entities: one is the creation of new entities, the other is updating existing entities. Being very creative with the names, I implemented a CreateFrom and an UpdateWith method for each DTO type.

You can have a look at all implementations on Github, like above, here we are focusing on the Medium DTO extensions:

public static EntityModel.Medium CreateFrom(this DtoModel.Medium dto, Guid blogId)
{
    return new EntityModel.Medium()
    {
        BlogId = blogId,
        MediumId = dto.MediumId,
        MediumTypeId = dto.MediumType?.MediumTypeId ?? default,
        MediumUrl = dto.MediumUrl,
        AlternativeText = dto.AlternativeText,
        Description = dto.Description,
    };
}

public static EntityModel.Medium UpdateWith(this EntityModel.Medium existingMedium, DtoModel.Medium updatedMedium)
{
    if (existingMedium.MediumId != updatedMedium.MediumId)
        throw new ArgumentException("MediumId must be equal in UPDATE operation.");

    if (existingMedium.AlternativeText != updatedMedium.AlternativeText)
        existingMedium.AlternativeText = updatedMedium.AlternativeText;

    if (existingMedium.Description != updatedMedium.Description)
        existingMedium.Description = updatedMedium.Description;

    if (existingMedium.MediumTypeId != updatedMedium.MediumType.MediumTypeId)
        existingMedium.MediumTypeId = updatedMedium.MediumType.MediumTypeId;

    if (existingMedium.MediumUrl != updatedMedium.MediumUrl)
        existingMedium.MediumUrl = updatedMedium.MediumUrl;

    return existingMedium;
}

Once again, there should be no surprise in the implementation. If you have a look at the other methods, you will find them implemented similarly.

Conclusion

In this post, I explained why we need DTOs and showed you how I implemented them. We also had a look at the mapping extensions to convert the entities to data transfer objects and vice versa. Now that we have them in place, we are able to start implementing our Azure Functions, which is where we are heading to next in the #CASBAN6 blog series.

Until the next post, happy coding, everyone!

Posted by msicc in Azure, Database, Dev Stories, 2 comments
#CASBAN6: Implementing the data model using EntityFramework Core (separate libraries)

#CASBAN6: Implementing the data model using EntityFramework Core (separate libraries)

EntityModel library

When I started the project, I started with creating the classes for all the tables that I need for my blog engine. I have put them into their own library to keep things clean.

The classes also use ICollection references for relationships whenever required. Let’s have a look at the Blog class:

public class Blog 
{
    public Guid BlogId { get; set; }

    public string Name { get; set; }

    public string Slogan { get; set; }

    public Uri LogoUrl { get; set; }

    public ICollection<Post> Posts { get; set; }

    public ICollection<Author> Authors { get; set; }

    public ICollection<Tag> Tags { get; set; }

    public ICollection<Medium> Media { get; set; }

}

The other classes are implemented similarly to reflect the data model I showed you in my last post. You can have a look at the other class implementations in the GitHub repo (folder: EntityModel).

EFCore library

The EFCore library has three main components:

  1. BlogContext
  2. Configurations
  3. Seed extension

BlogContext

The BlogContext is straight forward and follows the pattern described here in the documentation for using a factory (spoiler: we will do that later):

public sealed class BlogContext : DbContext
{
    public BlogContext(DbContextOptions<BlogContext> options) : base(options)
    {

    }

    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }
    public DbSet<Author> Authors { get; set; }
    public DbSet<MSiccDev.ServerlessBlog.EntityModel.Medium> Media { get; set; }
    public DbSet<MediumType> MediaTypes { get; set; }
    public DbSet<Tag> Tags { get; set; }
}

The class is declaring a constructor that uses the DBContextOptions<DBContext> parameter that allows our factory to configure the context later on for the migrations. Of course, we need references to all the possible DbSets as well to be able to access them via the BlogContext instance.

Configurations

In Entity Framework, we can configure our tables with configurations. By implementing the IEntityTypeConfiguration interface for all of our models in a separate file for each, we are continuing to keep our code clean and easily maintainable. Here is how the implementation for the Blog table:

public class BlogConfiguration : IEntityTypeConfiguration<Blog>
{
    public void Configure(EntityTypeBuilder<Blog> builder)
    {
        builder.Property(nameof(Blog.BlogId)).
            IsRequired();

        builder.Property(nameof(Blog.BlogId)).
            ValueGeneratedOnAdd();

        builder.HasKey(blog => blog.BlogId).
            HasName($"PK_{nameof(Blog.BlogId)}");

        builder.Property(nameof(Blog.Name)).
            HasMaxLength(255).
            IsRequired();

        builder.Property(nameof(Blog.Slogan)).
            HasMaxLength(255).
            IsRequired();

        builder.Property(nameof(Blog.LogoUrl)).
            IsRequired();
    }
}

Most of the fluent implementations above are self-explaining. The other classes of the project are implemented in a similar way, you can find them here in the Github repository.

I implemented my configurations by following the docs, which I absolutely recommend reading:

To apply the configurations, we need to override the OnModelCreating method:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.ApplyConfiguration(new BlogConfiguration());
    modelBuilder.ApplyConfiguration(new MediumypeConfiguration());
    modelBuilder.ApplyConfiguration(new MediumConfiguration());
    modelBuilder.ApplyConfiguration(new AuthorConfiguration());
    modelBuilder.ApplyConfiguration(new TagConfiguration());
    modelBuilder.ApplyConfiguration(new PostConfiguration());
}

Seed extension

To verify our configurations are working, we need some test data. This is where the seeding feature of EF Core comes in handy, and it helped me to improve my data model a lot. I am using the extension method approach here to implement a blog with three test posts, including all relations, constraints, and property configurations. You can find the full implementation here in the Github repository.

Besides the docs on EF Core data seeding, these links helped me to understand and write my seed implementation:

With this extension method in place, applying the seed is just one line of code at the end of the OnModelCreating override away:

modelBuilder.Seed();

Now we have everything together, we finally can turn to actually migrate our code to database.

EFCore.DesignDummy library

Because I am running this whole thing on a Mac, I need to use the CLI tools for all migrations. To keep also this step in its own library, I created a DesignDummy library which I use for pushing the migrations to my local database.

The library requires a reference to the EFCore library as well as to the EntityModel library. On top of that, we need the Microsoft.EntityFrameworkCore.Design NuGet package.

Now that our dependencies are in place, we just need to create an implementation of the IDesignTimeDbContextFactory interface as described here in the docs:

public class BlogContextFactory : IDesignTimeDbContextFactory<BlogContext>
{
    public BlogContext CreateDbContext(string[] args)
    {
        BlogContext? instance = null;

        var optionsBuilder = new DbContextOptionsBuilder<BlogContext>();

        optionsBuilder.UseSqlServer(dbContextBuilder =>
            dbContextBuilder.MigrationsAssembly("EFCore.DesignDummy")).
            EnableSensitiveDataLogging();

        instance = new BlogContext(optionsBuilder.Options);

        return instance;
    }
}

Now let’s create our first migration and push it to the database (find the docs here). In your terminal window, change to the folder of your dummy project. Once you’re in the correct folder, create a new migration with the add command:

dotnet ef migrations add {MigrationName}

To push the migration you just created to the database, use the update command with the connection parameter:

dotnet ef database update --connection 'Data Source=localhost;Initial Catalog=localDB;User ID=sa;Password=thisShouldB3Stronger!'

If all goes well, you should now be able to view your database with the seeded data:

database seeded

Conclusion

In this post, I showed you how to create the model for the database and their matching IEntityTypeConfiguration implementations. We learned how to create a IDesignTimeDbContextFactory and how to add migrations and push them to the database. The full code is on GitHub for your further exploration.

As always, I hope this post is helpful for some of you.

Until the next post, happy coding, everyone!

Posted by msicc in Azure, Database, Dev Stories, 3 comments
#CASBAN6: the data model explained

#CASBAN6: the data model explained

Preface

Initially, this post should have been about the direct implementation and design of the data model for my serverless blog engine. As the data model became a bit more complex, I decided to split the data model post into two posts. The first aims to explain the data model, while the second post is for the implementation with Entity Framework Core.

The data model

A picture is worth a thousand words, they say. So here is a complete picture of the data model:

CASBAN6 data model

I will go through the model table by table and tell you a sentence or two on each of it for the rest of this post.

The tables

__EFMigrationsHistory

This table just stores all the MigrationIds and is handled by the Entity Framework. I recommend you to not touch this table.

Blogs

In theory, the database could hold more than one blog. By adding a new row to this table, you are creating a new blog in the database. The BlogId is essential for a range of other tables.

Authors

To be able to issue blog posts, our blog needs at least one author. As you may have more than one person to fill your blog, a collection of authors can be saved within this table. One author can only be assigned to one blog (at least for now, this is intentional).

Posts

The content fuelling our blog is in the posts table. One post can only be on one blog. The published date will be set on insert, all subsequent changes will modify the LastModified column. Also, one post can only have one author. The author can be replaced on updating the row in the table.

The slug can be used to create a human-readable URL for the post (instead of the PostId, which is a GUID). The slug must be unique across all blogs.

Tags

In order to group and categorize posts, I decided to go with a tags-only approach (unlike other platforms, which allow both categories and tags). Tags are unique to a blog, but can be used in multiple posts.

Media

Of course, our blog should support also media content like images, videos and other types. I opted in for a URL-based approach, which is making it easier to add content from other platforms (like videos hosted on dedicated platforms). A medium can be used in several posts.

MediaTypes

To make it a bit easier to determine a medium’s type, I added the MediaTypes table. It holds information about the MIME-Type and possibly also the encoding of a file. The uniqueness is based off the MIME-Type.

Mapping tables

As we learned already above, both tags and media can be used in multiple posts. At the same time, posts can have multiple media and also multiple tags. To cover this many-to-many relationships, I use two mapping-tables with a composite primary key to ensure their uniqueness across the blog.

Conclusion

In this post, I outlined the data model of my serverless blog engine. In the next post, I will show you the implementation of this model with Entity Framework Core.

Until the next post, happy coding, everyone!

Posted by msicc in Azure, Database, Dev Stories, 2 comments
#CASBAN6: How to set up a local Microsoft SQL database on macOS

#CASBAN6: How to set up a local Microsoft SQL database on macOS

Microsoft’s SQL Server cannot be installed directly on macOS, like on Windows machines. Luckily, there is a not so complicated solution using a Docker container – provided by Microsoft themselves.

Install Docker

Obviously, the first step is to download Docker and install it on your Mac. Just head over to the Docker website and download the appropriate version. Install the app by opening the disk image and follow the instructions.

Install SQL Server

After installing the Docker desktop client, head over to the docker hub of Microsoft’s SQL Server. You can choose between SQL Server 2017, 2019 and 2022, with the latter one being preview (as of publishing time of this post). To download the image, we need to open a terminal and download it with the pull command:

sudo docker pull mcr.microsoft.com/mssql/server:2019-latest

I am selecting 2019 here as it is closest to what Azure SQL databases uses as of publishing time of this post.

Create a server instance

Microsoft makes it quite easy to create a server instance, we just need to copy the appropriate run command from the docker hub website. I am using SQL Server Express for my testing purposes:

 docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=thisShouldB3Stronger!' -e 'MSSQL_PID=Express' -p 1433:1433 --name mssql  -d mcr.microsoft.com/mssql/server:2019-latest

Once you run this command without any error, type in docker ps to verify the image is up and running. If all goes well, you should see something like this:

Create a database

Now that we have a running server instance, we can finally create a database for our purposes. We are using this terminal command to achieve our goal:

docker exec -i mssql /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P 'thisShouldB3Stronger!' -Q 'CREATE DATABASE localDB'

We are logging into our server with this and send the command to create our database. Alternatively, we could already connect using DBeaver(link below) to create the Database. In both cases, we have our local database up and running by now.

Connect

There are several ways to connect to this database. The one we are going to use with Entity Framework Core is the good old connection string:

//template: Data Source=localhost;Initial Catalog=<database>;User ID=sa;Password=<password>

Data Source=localhost;Initial Catalog=localDB;User ID=sa;Password=thisShouldB3Stronger!

If you want to access your database with a GUI, I recommend using either Visual Studio Code with the Azure and SQL workload installed or the Community Edition of DBeaver.

DBeaver Community screenshot

Visual Studio allows connecting on a database level, while DBeaver can be used to connect at server level as well. Both of them also support access to Azure SQL databases, which will be helpful later on.

Conclusion

Microsoft’s SQL Server is not available for macOS. Nonetheless, we are able to quickly set up a Docker container that runs MS SQL and set up a local database for testing. I wrote this post for completeness of the series.

Useful links

Until the next post – happy coding, everyone!

Posted by msicc in Azure, Database, Dev Stories, 3 comments
#CASBAN6: Creating A Serverless Blog on Azure with .NET 6 (new series)

#CASBAN6: Creating A Serverless Blog on Azure with .NET 6 (new series)

Motivation

I was planning to run my blog without WordPress for quite some time. For one, because WordPress is really blown up as a platform. The second reason is more of a practical nature – this project gives me lots of stuff to improve my programming skills. I already started to move my developer website away from WordPress with ASP.NET CORE and Razor Pages. Eventually I arrived at the point where I needed to implement a blog engine for the news section. So, I have two websites (including this one here) that will take advantage of the outcome of this journey.

High Level Architecture

Now that the ‘why’ is clear, let’s have a look at the ‘how’:

There are several layers in my concept. The data layer consists of a serverless MS SQL instance on Azure, on which I will work with the help of Entity Framework Core and Azure Functions for all the CRUD operations of the blog. I will use the powers of Azure API Management, which will allow me to provide a secure layer for the clients – of course, an ASP.NET CORE Website with RazorPages, flanked by a .NET MAUI admin client (no web administration). Once the former two are done, I will also add a mobile client for this blog. It will be the next major update for my existing blog reader that is already in the app stores.

For comments, I will use Disqus. This way, I have a proven comment system where anyone can use his/her favorite account to participate in discussions. They also have an API, so there is a good chance that I will be able to implement Disqus in the Desktop and Mobile clients.

Last but not least, there are (for now) two open points – performance measuring/logging and notifications. I haven’t decided yet how to implement these – but I guess there will be an Azure based implementation as well (until there are good reasons to use another service).

Open Source

Most of the software I will write and blog about in this series will be available publicly on GitHub. You can find the repository already there, including stuff for the next two upcoming blog posts already in there.

Index

I will update this blog post regularly with a link new entries of the series.

Additional note

Please note that I am working on this in my spare time. This may result in delays between the blog posts and the updates committed into the repository on GitHub.

Until the next post – happy coding, everyone!


Title Image by Roman from Pixabay

Posted by msicc in Android, Azure, Dev Stories, iOS, MAUI, Web, 2 comments
Make the IServiceProvider of your MAUI application accessible with the MVVM CommunityToolkit

Make the IServiceProvider of your MAUI application accessible with the MVVM CommunityToolkit

As you might know, I am in the process of converting all my internal used libraries to be .NET MAUI compatible. This is quite a bigger task than initially thought, although I somehow enjoy the process. One thing I ran pretty fast into is the fact that you can’t access the MAUI app’s IServiceProvider by default.

Possible solutions

As always, there is more than one solution. While @DavidOrtinau shows one approach in the WeatherTwentyOne application that accesses the platform implementation of the Services, I prefer another approach that uses, in fact, Dependency Injection to achieve the same goal.

Implementation

I am subclassing the Microsoft.Maui.Controls.Application to provide my own, overloaded constructor where I inject the IServiceProvider used by the MAUI application. Within the constructor, I am using the MVVM CommunityToolkit’s Ioc.Default.ConfigureServices method to initialize the toolkit’s Ioc handler. Here is the code:

using CommunityToolkit.Mvvm.DependencyInjection;

namespace MauiTestApp
{
	public class MyMauiAppImpl : Microsoft.Maui.Controls.Application
	{
		public MyMauiAppImpl(IServiceProvider services) 
		{
            Ioc.Default.ConfigureServices(services);
        }
	}
}

Usage

Using the class is straight forward. Open your App.xaml file and replace the Application base with your MyMauiAppImpl:

<local:MyMauiAppImpl
             xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:MauiTestApp"
             x:Class="MauiTestApp.App">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Resources/Styles/Colors.xaml" />
                <ResourceDictionary Source="Resources/Styles/Styles.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</local:MyMauiAppImpl>

And, of course, the same goes for the code behind-file App.xaml.cs:

namespace MauiTestApp;

public partial class App : MyMauiAppImpl
{
	public App(IServiceProvider serviceProvider) : base(serviceProvider)
	{
		InitializeComponent();

		MainPage = new AppShell;
	}

}

That’s it, you can now use the MVVM CommunityToolkit’s Ioc.Default implementation to access the registered Services, ViewModels and Views.

Conclusion

In this post, I showed you a simple (and even easily reusable way) of making the IServiceProvider of your .NET MAUI application available. I also linked to an alternative approach, if you do not want to subclass the application object, I recommend that way.

As always, I hope this post is helpful for some of you.

Until the next post, happy coding, everyone!
Posted by msicc in Dev Stories, MAUI, Xamarin, 1 comment
Invoke platform code in a MAUI app using the built-in Dependency Injection

Invoke platform code in a MAUI app using the built-in Dependency Injection

In Xamarin.Forms, my internal libraries for MVVM helped me to keep my applications cleanly structured and abstracted. I recently started the process of porting them over to .NET MAUI. I was quickly reaching the point where I needed to invoke platform specific code, so I read up the documentation.

The suggested way

The documentation suggests creating a partial class with partial method definitions and a corresponding partial classes with the partial method implementations (like described in the docs). I tried to follow the above-mentioned MAUI documentation and copy/pasted the code sample in there and thought everything is going to be fine. Well, it was not. I wasn’t even able to compile the solution with that code on my Mac in the first place.

In search for a possible cause of this, I did not find a solution immediately. In the end, it turned out that I needed to implement the partial class method on all platforms specified in the TargetFrameworks within the .csproj file. It should have been obvious due to the fact that MAUI is a single project with multiple target frameworks, but it wasn’t on that day.

On top of that, Visual Studio did some strange changes to the .csproj file specifying unnecessary None, Compile and Include targets that should not be generated explicitly, which added a lot to my confusion as well. After removing them from the project file and adding an implementation for all platforms, I was able to compile and test the code from the docs.

But I love my interfaces!

Likewise, that’s why I did not stop there. Following the abstraction approach, interfaces allow us to define the common surface of the API without worrying about the implementation details. That’s not the case for the partial classes approach, like the problems I had showed.

Luckily for us, .NET MAUI supports multi targeting. This means an interface can have a platform specific implementation while being defined in the shared part of the application. If you have used the MSBuildExtras package before, you know already how that works. Best part – .NET MAUI already provides the multi targeting configuration out of the box.

Show me some code!

First, let’s define a simple interface for this exercise:

namespace MAUIDITest.InterfaceDemo
{
    public interface IPlatformDiTestService
    {
        string SayYourPlatformName();
    }
}

Now we are going to implement the platform specific implementations. Go to the first platforms folder and add a new class named PlatformDiTestService. Then – and this is really important to make it work – adjust the namespace to be the same as the one of the interface. Last, but not least, implement the interface, for example like this:

namespace MAUIDITest.InterfaceDemo
{
    public class PlatformDiTestService : IPlatformDiTestService
    {
        public string SayYourPlatformName()
        {
            return "I am MacOS!";
        }
    }
}

Repeat this for all platforms, and replace the platform’s name accordingly.

Using Dependency Injection in MAUI

If you have been following along my past blog posts, you know that I recently switched to the CommunityToolkit MVVM (read more here and here). I already heard that MAUI will get the same DI container built-in, so the choice was obvious. Now let’s have a look how easy we can inject our interface into our ViewModel. Head over to your MauiProgram.cs file and update the CreateMauiApp method:

	public static MauiApp CreateMauiApp()
	{
		var builder = MauiApp.CreateBuilder();
		builder
			.UseMauiApp<App>()
			.ConfigureFonts(fonts =>
			{
				fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
				fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
			});

		//lowest dependency
		builder.Services.AddSingleton<IPlatformDiTestService, PlatformDiTestService>();
		//relies on IPlatformDiTestService
		builder.Services.AddTransient<MainPageViewModel>();
		//relies on MainPageViewModel
		builder.Services.AddTransient<MainPage>();

		return builder.Build();
	}

First, add the registration of the interface and the implementation. In the sample above, the MainPageViewModel relies on the interface and gets it automatically injected by the DI handler. For testing purposes, I even inject the MainViewModel into the MainPage‘s constructor. This is very likely to change in a real world application.

For completeness, here is the MainPageViewModel class:

using System;
using System.ComponentModel;
using System.Windows.Input;
using MAUIDITest.InterfaceDemo;

namespace MAUIDITest.ViewModel
{
    public class MainPageViewModel : INotifyPropertyChanged
    {
        private readonly IPlatformDiTestService _platformDiTestService;
        private string sayYourPlatformNameValue = "Click the 'Reveal platform' button";
        private Command _sayYourPlatformNameCommand;

        public event PropertyChangedEventHandler PropertyChanged;


        public MainPageViewModel(IPlatformDiTestService platformDiTestService)
        {
            _platformDiTestService = platformDiTestService;
        }

        public void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        public string SayYourPlatformNameValue
        {
            get => sayYourPlatformNameValue;
            set
            {
                sayYourPlatformNameValue = value;
                OnPropertyChanged(nameof(this.SayYourPlatformNameValue));
            }
        }

        public Command SayYourPlatformNameCommand => _sayYourPlatformNameCommand ??=
            new Command(() => { this.SayYourPlatformNameValue = _platformDiTestService.SayYourPlatformName(); });
    }
}

And of course, you want to see the MainPage.xaml.cs file as well:

using MAUIDITest.InterfaceDemo;
using MAUIDITest.ViewModel;

namespace MAUIDITest;

public partial class MainPage : ContentPage
{
    private readonly IPlatformDiTestService _platformDiTestService;
    int count = 0;

	public MainPage(MainPageViewModel mainPageViewModel)
	{
		InitializeComponent();

		this.BindingContext = mainPageViewModel;
    }

	private void OnCounterClicked(object sender, EventArgs e)
	{
		count++;

		if (count == 1)
			CounterBtn.Text = $"Clicked {count} time";
		else
			CounterBtn.Text = $"Clicked {count} times";

		SemanticScreenReader.Announce(CounterBtn.Text);
	}
}

Last, but not least, the updated MainPage.xaml file:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MAUIDITest.MainPage">
			 
    <ScrollView>
        <VerticalStackLayout 
            Spacing="25" 
            Padding="30,0" 
            VerticalOptions="Center">

            <Image
                Source="dotnet_bot.png"
                SemanticProperties.Description="Cute dot net bot waving hi to you!"
                HeightRequest="200"
                HorizontalOptions="Center" />
                
            <Label 
                Text="Hello, World!"
                SemanticProperties.HeadingLevel="Level1"
                FontSize="32"
                HorizontalOptions="Center" />
            
            <Label 
                Text="Welcome to .NET Multi-platform App UI"
                SemanticProperties.HeadingLevel="Level2"
                SemanticProperties.Description="Welcome to dot net Multi platform App U I"
                FontSize="18"
                HorizontalOptions="Center" />

            <Button 
                x:Name="CounterBtn"
                Text="Click me"
                SemanticProperties.Hint="Counts the number of times you click"
                Clicked="OnCounterClicked"
                HorizontalOptions="Center" />


            <Label Text="Test of built in DI:" FontSize="Large" HorizontalOptions="Center"></Label>
            <Label x:Name="PlatformNameLbl" FontSize="Large" HorizontalOptions="Center" Text="{Binding SayYourPlatformNameValue}" />

            <Button Text="Reveal platform" HorizontalOptions="Center" Command="{Binding SayYourPlatformNameCommand}"/>

        </VerticalStackLayout>
    </ScrollView>
 
</ContentPage>

I did not change the default code that comes with the template. You can easily recreate this by using the default MAUI template of Visual Studio and copy/paste the code snippets above to play around with it.

Conclusion

I only started my journey to update my internal libraries to .NET MAUI. I stumbled pretty fast with that platform invoking code, but luckily, I was able to move along. Platform specific code can be handled pretty much the same as before, which I hope I was able to show you in this post. I’ll write more posts on my updating experiences to MAUI as they happen.

Until the next post, happy coding, everyone!
Posted by msicc in Dev Stories, MAUI, Xamarin, 4 comments
Using Microsoft’s Extensions.DependencyInjection package in (Xamarin.Forms) MVVM applications (Part 2)

Using Microsoft’s Extensions.DependencyInjection package in (Xamarin.Forms) MVVM applications (Part 2)

The Key

Our goal is to add keyed registrations to the IServiceCollection, so we need a common denominator to build upon. As I was able to use a string with the SimpleIoc implementation of MVVMLight for years now, I decided to move on with that and created the following, very complex interface:

public interface IViewModelKey
{
    string Key { get; set; }
}

Every ViewModel that should be registered by Key needs to implement that interface from now on in my MVVM environment.

The Resolver

Back in the MVVMLight times, I was able to query the SimpleIoc registrations with the key I was searching for. In the Microsoft.Extensions.DependencyInjection world, things get a bit more complex. While there are different ways to solve the problem (there are some libraries extending the IServiceProvider with additional methods out there, for example), I decided to use the IServiceProvider itself and go down the resolver interface/implementation road.

Let’s have a look at the interface first:

public interface IViewModelByKeyResolver<T> where T : IViewModelKey
{
    public T GetViewModelByKey(string key);
}

Nothing too special here, just a generic implementation of the resolver interface with the requirement of the IViewModelKey implementation from above. This makes the usage pretty straight forward. The more important part here is the implementation, though. Let’s have a look at mine:

public class ViewModelByKeyResolver<T> : IViewModelByKeyResolver<T> where T : IViewModelKey
{
    private readonly IServiceProvider _serviceProvider;

    public ViewModelByKeyResolver(IServiceProvider serviceProvider)
        => _serviceProvider = serviceProvider;

    public T GetViewModelByKey(string key)
        => _serviceProvider.GetServices<T>().SingleOrDefault(vm => vm.Key == key);
}

The registration of the implementation will automatically inject the IServiceProvider instance at runtime for me here. The GetViewModelByKey method searches all registrations of the given type for the key and returns the desired instance.

Registering the Resolver and keyed ViewModels

The registration of the resolver is done like all the other registrations:

this.ServiceDescriptors.TryAddSingleton<IViewModelByKeyResolver<KeyedViewModel>, ViewModelByKeyResolver<KeyedViewModel>>();

Replace KeyedViewModel with your individual type that implements your key interface. That’s it.

For the registration of the KeyedViewModel instances, there is one thing to pay attention to, though. You cannot use the TryAdd{Lifetime} methods here for registration. Instead, just use the Add{Lifetime} method to register them. Here is a sample:

this.ServiceDescriptors.AddSingleton<KeyedViewModel>(new KeyedViewModel("Key1"));
this.ServiceDescriptors.AddSingleton<KeyedViewModel>(new KeyedViewModel("Key2"));
this.ServiceDescriptors.AddSingleton<KeyedViewModel>(new KeyedViewModel("Key3"));
this.ServiceDescriptors.AddSingleton<KeyedViewModel>(new KeyedViewModel("Key4"));
this.ServiceDescriptors.AddSingleton<KeyedViewModel>(new KeyedViewModel("Key5"));

If you know the keyed ViewModels already at the time of your app startup, you can add them right away and create the IServiceProvider instance as shown in my first post. In most cases, however, you will know the information of the keyed instances only at runtime. Luckily, my Xamarin.Forms implementation already has the solution built in. Here is a short reminder:

public ServiceCollection? ServiceDescriptors { get; private set; }

private IServiceProvider? _services;

public IServiceProvider? Services => _services ??= BuildServiceProvider();

public IServiceProvider? BuildServiceProvider(bool resetExisiting = false)
{
    if (this.ServiceDescriptors == null)
        throw new ArgumentNullException($"Please register your Services and ViewModels first with the {nameof(RegisterServices)} and {nameof(RegisterViewModels)} methods.");

    if (resetExisiting)
        _services = null;

    if (_services == null)
        _services = ServiceDescriptors.BuildServiceProvider();

    return _services;
}

The BuildServiceProvider method has an additional parameter that allows to reset the existing IServiceProvider. This way, I can keep my existing registrations and just add the new keyed ones dynamically. Please note that you may need to reinitialize your already registered and used ViewModels under certain circumstances after performing the reset.

Accessing a keyed ViewModel

Last but not least, I need to show you how to access a ViewModel by its key. Luckily, this is not that hard:

KeyedViewModel vm4 = IocManager.Current.Services.GetService<IViewModelByKeyResolver<KeyedViewModel>>().GetViewModelByKey("Key4");
KeyedViewModel vm2 = IocManager.Current.Services.GetService<IViewModelByKeyResolver<KeyedViewModel>>().GetViewModelByKey("Key2");

Conclusion

By switching to the CommunityToolkit.MVVM package and utilizing Microsoft’s Extension.DependencyInjection package together with it, my MVVM environment is ready for upcoming challenges like .NET MAUI. I will be able to use it on all .NET platforms and just need to adapt my Xamarin.Forms implementation to others (which I have done already for one of our internal tools at work in WPF). Even keyed ViewModel instance can be used similarly as before, as I showed you in this post.

As always, I hope this post will be helpful for some of you.

Until the next post, happy coding!

Posted by msicc in Dev Stories, Xamarin, 1 comment