Friday, April 30, 2021

Architecture Weekly

Interested in software architecture?

I discovered ‘Architecture Weekly’, a weekly list of links so you know what to read during the weekend.

Thursday, April 29, 2021

C#–Hide a base class for usage outside an assembly

I created an abstract base class in C# but didn’t want it to be used outside the assembly I created it in.

How can you achieve this?

Your first guess could be to change the access modifier of the base class to internal. When you try to do this you’ll get the following error message:

Inconsistent accessibility: base class 'MyBaseClass' is less accessible than class 'MyInheritedClass'

You have no other choice than to make the abstract class public what makes it visible outside the assembly.

Is there still a way to only allow classes in the same assembly to implement it?

The trick is to make the abstract base class public, but give it an internal default constructor:

This will allow MyBaseClass to be visible outside the assembly, but classes outside the assembly cannot inherit from it.

Tuesday, April 27, 2021

Azure DevOps–Auditing streams

  • Yesterday I blogged about Azure DevOps audit logs.  Although you could export the logs, it was only limited to the last 90 days. To have a full audit log over time, we need to take a different approach through audit streams.

Audit streams represent a pipeline that flows audit events from your Azure DevOps organization to a stream target. Every half hour or less, new audit events are bundled and streamed to your targets. Currently, the following stream targets are available for configuration:

  • Splunk – Connect to on-premises or cloud-based Splunk.
  • Azure Monitor Log - Send auditing logs to Azure Monitor Logs. Logs stored in Azure Monitor Logs can be queried and have alerts configured. Look for the table named AzureDevOpsAuditing. You can also connect Azure Sentinel to your workspace.
  • Azure Event Grid – For scenarios where you want your auditing logs to be sent somewhere else, whether inside or outside of Azure, you can set up an Azure Event Grid connection.

Create a stream

  • Select Organization settings.

  • Select Auditing.

  • Go to the Streams tab, and then select New stream.

  •   Select the stream target that you want to configure.

  • Specify the required fields and click on Set up.

More information: https://docs.microsoft.com/en-us/azure/devops/organizations/audit/auditing-streaming?view=azure-devops

    Monday, April 26, 2021

    Azure DevOps–Auditing

    A lesser known feature in Azure DevOps Services(it doesn’t exist in Azure DevOps) is the audit log. The audit log contains many changes that occur throughout an Azure DevOps organization. The feature is still in preview and the list of events that are tracked through this feature keeps growing.

    Check the audit logs

    Let’s find out how to check the audit log:

    • Select Auditing from the left menu

    .

    • The auditing page gives you a limited view into the available audit events and recorded data. If you want to access all the data, it is possible to export the data.  Click on the Export log button in the right corner.

    Remark:

    If you can't find the auditing event you're looking for in the following table, be sure to check the REST API: https://auditservice.dev.azure.com/{YOUR_ORGANIZATION}/_apis/audit/actions. Replace {YOUR_ORGANIZATION} with the name of your organization.

    More information: https://docs.microsoft.com/en-us/azure/devops/organizations/audit/azure-devops-auditing?view=azure-devops&tabs=preview-page

    Friday, April 23, 2021

    The Art of Computers

    Scott Hanselman is always entertaining but if you are new to programming or not, this is a great talk about some important concepts in IT:

    Thursday, April 22, 2021

    ASP.NET HttpHandler - Concurrent requests

    To solve a specific issue(maybe more about this in another post) we created a custom HttpHandler in ASP.NET. Inside this handler we need access to the session. Therefore we implemented the IRequiresSessionState marker interface:

    The HttpHandler worked as expected until we started to increase the load. We noticed that a lot of requests remained pending…

    This could be traced to the usage of the IRequiresSessionState that put an exclusive lock on the current session. As a consequence the # of concurrent requests for one user are restriced.

    The good news is that another marker interface exists, IReadOnlySessionState, that does not acquire an exclusive lock. In case you only need to read data from the session state and don’t need to change any session data, you can switch to this interface:

    Wednesday, April 21, 2021

    Kubernetes–Schedule a pod on a Linux node–Part 2

    Yesterday I blogged about the usage of the “kubernetes.io/os” nodeselector to schedule a pod specifically on a windows or linux node pool.

    Today I want to share another way through the usage of taints and tolerations.

    This Kubernetes feature allows users to mark a node (taint the node) so that no pods can be scheduled to it, unless a pod has a toleration that indicates it will accept this taint. Using this Kubernetes feature we can create nodes that are reserved (dedicated) for specific pods.  Using this, we can pick one OS and taint all the nodes with this OS, so that any pods that do not specify toleration will use the non-tainted nodes. This Taint effectively allows us to specify a default OS for the cluster, and if you want to use the other OS, you need to state this explicitly.

    To enable this, we first need to taint our Windows nodes. We would run this command for each Windows node:

    kubectl taint nodes <nodeName> OS=Windows:NoSchedule
    

    After this is applied it will prevent any pods that do not tolerate that condition from running on that node.

    Now we need to update our pod spec for our Windows app to tolerate this.

    If no toleration is specified, the pod will default to running on the Linux nodes.

    More information: https://docs.microsoft.com/en-us/azure-stack/aks-hci/adapt-apps-mixed-os-clusters

    Tuesday, April 20, 2021

    Kubernetes–Schedule a pod on a Linux node–Part 1

    On our Kubernetes(AKS) cluster we have multiple node pools some with Linux nodes and some with Windows nodes:

    But what if you want to schedule specific workloads on Linux or Windows nodes? The easiest way to control this is through the OS node selector. The OS node selector(“kubernetes.io/os”) is a built-in selector which indicates the OS that the node is running. We can use this selector to ensure that the pod is only deployed to nodes with the right OS.

    Here is an example for Windows pods:

    And here is in example for Linux pods:

    Unfortunately you don’t always have direct control on the pod specification(e.g. when you are using a 3th party Helm chart). In that case we have to take a different approach. But that is for another blog post tomorrow…

    Monday, April 19, 2021

    XUnit–Dependency Injection

    To use the standard IoC container inside your Xunit tests, I would recommend to use a separate fixture. Inside this fixture you can add all dependencies to the ServiceCollection and build the ServiceProvider:

    Now you can use the IoC container inside your tests by injecting the fixture inside the constructor:

    Sunday, April 18, 2021

    C# 9–Target typed conditional expressions

    Before C# 9, I didn’t use conditional expressions much because the different branches in the conditional couldn’t have different types.

    With C# 9, this has finally changed; different types are allowed, as long as both of them convert to the target type.

    Let’s explain this through an example; I have a Pet baseclass and two inherited classes Cat and Dog. If I want to use them in a conditional expression in .NET Core 3.1(using C# 8), I get the following error message:

    CS8400: Feature ‘target typed conditional expressions’ is not available in C# 8.0. Please use language version 9.0 or greater.

    When I upgrade to .NET 5(using C# 9) the code above works!

    More information: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/target-typed-conditional-expression

    Friday, April 16, 2021

    Intentional Design

    There a thousands(millions?) of (mobile) apps out there. But what makes the difference between a good and a great app experience?

    The answer is simple: Intentional design. I recommend watching this session by Doug LeMoine.

    Doug walks us through 5 elements of intentional design. Each element needs to be considered in your design process:

    1. Radical Simplification

    2. Deep Understanding

    3. Extreme Focus

    4. Personal Connection

    5. Direct Communication

    Thursday, April 15, 2021

    ASP.NET Core–Avoid AutoRedirect in named or typed httpclients

    In ASP.NET Core I typically avoid to use the HttpClientFactory directly but instead I use the named or typed httpclients.

    You can configure the client using the AddHttpClient method(in case of named clients):

    or inside the constructur (in case of typed clients):

    This provides proper encapsulation and isolation of the configuration of each httpclient.

    But what if you want more control on the HttpClient behavior? For example, you want to disable AutoRedirect on the HttpClient. That is not possible through the configuration above.

    In that case you can use the ConfigurePrimaryHttpMessageHandler extension method.

    For a named client:

    For a typed client:

    Wednesday, April 14, 2021

    Angular --deploy-url and --base-href

    As long you are running your Angular application at a root URL (e.g. www.myangularapp.com) you don’t need to worry that much about either the ‘--deploy-url’ and ‘--base-href’ parameters.

    But once you want to serve your Angular application from a server sub folder(e.g. www.mywebsite.com/angularapp ) these parameters become important.

    --base-href

    If you deploy your Angular app to a subfolder, the ‘--base-href’ is important to generate the correct routes. This parameter will update the <base href> tag inside the index.html.

    For example, if the index.html is on the server at /angularapp/index.html, the base href should be set to <base href="/angularapp/">.

    More information: https://angular.io/guide/deployment

    --deploy-url

    A second parameter that is important is ‘--deploy-url’. This parameter will update the generated url’s for our assets(scripts, css) inside the index.html.

    To make your assets available at /angularapp/, the deploy url should be set to /angularapp/.

    A complete example would look like this:

    ng build --prod --base-href /angularapp/ --deploy-url /angularapp/
    

    Monday, April 12, 2021

    Link an Azure Container Registry to multiple AKS clusters

    To link an Azure Container Registry(ACR) to an AKS cluster, you can do this during cluster creation or set it up after the cluster is up and running.

    Create a new cluster with ACR:

    az aks create -n myAKSCluster -g myResourceGroup --generate-ssh-keys --attach-acr myContainerRegistry

    Link ACR after creation:

    az aks update -n myAKSCluster -g myResourceGroup --attach-acr myContainerRegistry

    This works as long as the ACR and the AKS cluster are inside the same subscription.  If you try the command above with the ACR and AKS in different subscriptions, you get the following error message:

    cli.azure.cli.core.azclierror: The resource with name 'myContainerRegistry' and type 'Microsoft.ContainerRegistry/registries' could not be found in subscription 'MySubscription'.

    cli.azure.cli.core.azclierror: The resource with name 'myContainerRegistry' and type 'Microsoft.ContainerRegistry/registries' could not be found in subscription 'MySubscription'.

    This doesn’t mean that this is not possible. To get this working you need to specify the complete resource URI/

    az aks update --subscription MySubscription -g myResourceGroup -n myAKSCluster --attach-acr "/subscriptions/f84a2db1-aede-57cd-a7c2-e55f8b68b8ef/resourceGroups/myOtherResourceGroup/providers/Microsoft.ContainerRegistry/registries/myContainerRegistry"

    Friday, April 9, 2021

    Azure AKS- Scale out to ACI through virtual nodes

    One of the nice features of AKS(Azure Kubernetes Service) is that you can create a virtual nodes that can be used to scale out to ACI.

    This feature is really useful in following scenario’s:

    • Temporary burst of capacity
    • On-demand processing, e.g. batch jobs
    • Isolated processing for untrusted code

    The virtual nodes feature is build on top of the Virtual Kubelet technology.

    To enable this feature you need to extend the deployment yaml with the configuration below. This configuration will first try to schedule the pod on the default node pools. If not enough resources are available, the pod will be scheduled on the virtual node:

    A good introduction can be found here:

     

    Remark: Virtual nodes only supports Linux pods at the moment of writing

    Thursday, April 8, 2021

    Application Insights–Report browser usage

    I was looking at a way to report browser usage statistics through Application Insights.

    Here are the required steps:

    • Browse to the Application Insights resource in the Azure Portal
    • Go to the Monitoring section and click on Logs

    • Click on Get Started on the Welcome screen

    • Close the queries screen

    • Enter the following query and click on Run
      • pageViews | summarize count() by client_Browser

    • Check the results

     

    Wednesday, April 7, 2021

    AutoRest- Duplicate operation detected

    I was trying to use AutoRest to generate a client SDK for one of my API’s. This turned out not to be the success I was hoping for.

    Yesterday I explained that I got into trouble because an OperationId was missing. But after adding it, it still didn’t work. Instead I got the following error message:

    Duplicate operation detected

    Here was the API controller I was using:

    AutoRest uses the OpenAPI generated through Swashbuckle. When I took  a look at the OpenAPI file, I noticed that indeed the same OperationId was used:

    This is because I was using the ActionName as the OperationId. As I have multiple Action methods with the same name they all result in the same OperationIe

    To fix it, you can do 2 things:

    • Using the [SwaggerOperation] attribute
    • Using the Name property on the Http attribute ([HttpGet], [HttpPost],…)

    Using the [SwaggerOperation] attribute

    By adding a [SwaggerOperation] attribute to the controller action method, you can control the generated OperationId:

    Using the Name property

    An alternative solution is to use the Name property on the HTTP routing attributes:

    Tuesday, April 6, 2021

    AutoRest–OperationId is required for all operations

    I was trying to use AutoRest to generate a client SDK for one of my API’s. This turned out not to be the success I was hoping for. I downloaded the swagger.json file for my API and executed AutoRest through the following command:

    autorest --csharp --input-file=swagger.json

    This command failed with the following exception message:

    FATAL: OperationId is required for all operations.

    Here was the API controller I was using:

    And here is an extract of the generated swagger.json:

    As you can see, there is indeed no OperationId specified in the swagger.json. The swagger documentation has the following to tell about the OperationId:

    operationId is an optional unique string used to identify an operation. If provided, these IDs must be unique among all operations described in your API.

    Some common use cases for operationId are:

    • Some code generators use this value to name the corresponding methods in code.
    • Links can refer to the linked operations by operationId.

    AutoRest uses the OperationId to identify each method. Unfortunately as the OperationId is optional, it is not generated by Swashbuckle automatically.

    To change this, you can add an extra line in your Swashbuckle configuration;

    This will use the ActionName as the OperationId. Of course you can also use a different naming strategy.

    Monday, April 5, 2021

    AutoRest–Generate a client library for your REST API

    I was investigating some ways to generate a C# client library based on the OpenAPI definition of my REST API. One of the solutions I found was AutoRest.

    AutoRest is an open source tool created by Microsoft so that Azure service teams could start producing generated client libraries from new Swagger and OpenAPI 2.0 specifications.

    Install AutoRest

    To install AutoRest, you can use NPM:

    # Depending on your configuration you may need to be elevated or root to run this. (on OSX/Linux use 'sudo' )
    npm install -g autorest
    
    # run using command 'autorest' to check if installation worked
    autorest – --help

    Generate a C# client

    To generate a C# client, you first need to have your OpenAPI file available. When you are using Swashbuckle in ASP.NET Core, you can browse to the swagger endpoint and download the swagger.json.

    Next step is to invoke AutoRest:

    autorest --input-file=swagger.json – --csharp

    This will generate a C# project with a REST API client.