- Home>
- .NET
I have been a C#/.NET developer for nearly ten years. Before that, I was a Java/Spring Framework developer for a couple of years. As with every change in technology, it took a bit of time to catch up and get comfortable in the new environment. However, the move from Spring Framework to .NET was an upgrade for me personally in terms of developer satisfaction. For instance, I was using Java 7 or 8, and the corresponding version of C# felt slightly more modern to me. I enjoyed using C# delegates, which I don’t believe had an equivalent in Java at the time, to build reusable code.
In addition, it’s easier to get started with good design patterns like dependency injection in .NET, protect API endpoints using middleware, and start web apps. I specifically like how .NET uses a code-first approach to configure dependencies using code instead of annotations. In comparison, using Spring Framework at the time, I would need to decorate service classes to use them as dependencies. I also like how easy it is to add NuGet packages in .NET compared to adding libraries in Java.
Fast forward to today, I feel at home and comfortable with all the great features that .NET has provided. For instance, it’s easy for me to set up a new .NET project with clean architecture, Entity Framework, Microsoft Authentication Library (MSAL), MediatR, etc., and get a solid starter codebase to build great apps. I especially enjoy how the .NET team and Microsoft strive to enable .NET developers to go full-stack with .NET, with frameworks like Blazor — which I myself am using to build user interfaces.
As someone who cares about clean code, I like how .NET and C# allow developers to implement layered architecture or clean architecture by simply creating different projects for different layers and controlling dependencies between them more robustly than just using folders in the same project. This is especially useful if you want to implement clean architecture. For example, using different projects for each layer allows me to enforce that the domain layer does not have dependencies on other projects by simply not adding those projects as dependencies. If I have just one project and implement different layers in separate packages or directories, it’s harder to ensure that code in the domain layer does not reference code in other folders or layers.
Being an object-oriented language, I enjoy developing in C# and using its object-oriented features to implement well-established design patterns. For example, the Command Query Responsibility Segregation (CQRS) pattern is a good way to adhere to the Single Responsibility Principle. Essentially, a single responsibility can be encapsulated into a command or query, with its corresponding implementation in a handler. In C# or similar object-oriented languages, you can define the command or query in an interface or abstract class, and the handler in a concrete class. The MediatR library makes it seamless to register queries, commands, and their corresponding handlers so you can use them in service classes by simply executing commands or queries, with the library handling the appropriate lookup.
Regarding the open-closed principle, the idea is to allow extending the functionality of a class or module without modifying the class. One way C# helps to implement this pattern is via interfaces. For example, suppose you have a class that utilizes Redis for caching. Instead of injecting this class directly into other classes, you can create an interface that defines the necessary caching methods and have the service implement this interface. Then, you inject an instance of the interface, not the concrete class. This approach aligns with the open-closed principle because it allows you to extend caching behavior at runtime by injecting a different implementation of the interface, without changing the classes that depend on it.
Last but not least, I like the user interface in Visual Studio for managing packages. It’s easy to search for and add packages and libraries. Back when I was focusing on Java codebases a few years ago, I used Maven to manage Java libraries. To add a library, I had to know and specify the group ID, artifact ID, version, and scope. To me, dependency management in Java just felt less intuitive and harder to use compared to .NET, thanks to the available tooling and IDE support.
I’m pleased that Microsoft and the .NET team have been consistently releasing updates that continue to improve .NET and C#, as well as enhance the developer experience. At the same time, I’m also excited to build applications in other languages, particularly Python. In fact, I’ve recently been focusing on building apps in Python. I felt reluctant and awkward at first because I was so used to writing code in C# and enjoying all the benefits of .NET. But I’m getting better at it and plan to share more in upcoming posts — particularly about the techniques and tools I use to organize the codebase and write code that meets my standards of cleanliness and maintainability.
Since the advent of Large Language Models (LLMs), we have been leveraging the technology to streamline processes and improve staff efficiency. One of the things LLMs are good at is coming up with coherent text based on a given context. Leveraging this strength, we have applied the Retrieval Augmented Generation (RAG) pattern to build chatbots that help staff across different areas, including human resources, contract management, and other department-specific procedures. For the user interface of the chatbots, we use Microsoft Teams, as it’s our primary communication platform. The bots’ functionalities are quite similar in nature, in the sense that they all pull data from a data store by calling APIs, sending the user’s chat message, and replying to messages in Microsoft Teams. However, because each bot requires a different category of data for use as the LLM’s context, we need a separate Microsoft Teams app for each bot. Behind the scenes, the Teams app calls the corresponding web API, which is an ASP.NET Core application, to send and receive messages. The ASP.NET Core applications are very similar in nature, in that they all call APIs to get the LLM’s response for a user’s request. As such, we were thinking of ways to minimize code duplication and the infrastructure needed to support multiple bots. In the web API, we follow a clean architecture with infrastructure, a shared kernel, a domain layer, and other typical layers. One approach I thought of was that we could reuse all the layers except for the web API. We would have one web API project for each bot we want to support. However, that approach still requires separate infrastructure for each bot—things like Key Vault, App Insights, Blob Storage, etc.—since each bot is a separate app that serves a different domain. Luckily, after some back-and-forth discussions with GitHub Copilot and reading sample code, I tested and found a way to support multiple bots using the same codebase and infrastructure.
Continue readingThis post was written a while ago, but I’ve never published it.
In this post, I show an example of using SQL row level security to isolate data among different organizations in a same database using row level security. In that post, I show an example of reading a value in the database session context as part of the filtering logic. In this post, I show an example of how to preset a value in the database session context from ASP.NET core application, and also how to authenticate users from multiple tenants using Microsoft Identity Framework.
Continue reading
In this post, I write about building an API to help me analyze real estate properties.
Continue readingIn this post, I show an example of scraping data in C# using HtmlAgilityPack. I come across HtmlAgilityPack because I need to get data from Zillow to analyze properties deals. I was able to scrape the data I want without much trouble using HtmlAgilityPack with a bit of XPath, LINQ and regular expression.
Continue readingIn the previous post about row level security in SQL server, I gave an example of reading a value from the database session context to feed into the security function for filtering data. In this post, I show examples of calling the sp_set_session_context store procedure from an ASP.NET core web API to store a value into the session context.
Continue readingIn this post, I’m going to share some of the issues, misunderstandings I ran into when trying to setup and deploy a WebJob alongside a web application using azure pipelines. The WebJob is a console application, and the web app is an ASP.NET core. Both the WebJob and web app target .NET 5.
Continue readingI have an API which needs to authenticate against azure ad to obtain an access token for calling another downstream API. When registering an application in azure AD for the caller API, I could either setup a shared secret or a certificate for the API to use as part of its credentials in a client credentials flow . In the past, I had always used a shared secret as it was more convenient and easier to setup. However, using certificate provides stronger security. After spending a few hours of googling and hacking, I was able to setup and use a certificate instead of a shared secret as credentials for the caller API to authenticate against azure AD.
Continue readingIn this post, I’m going to share my understanding of clean architecture after reading about it in the book “Clean Architecture” and other posts, as well as how I implement the clean architecture in an ASP.NET core project.
Continue readingIn this post, I share the three libraries I find useful for unit testing a .NET project: EF Core SQLite Database Provider, Bogus and Moq.
Continue reading