.Net Core Best Practices – Every .Net Developer Must Know

By Saurabh Barot   |   7 March, 2022
.Net Core Best Practices – Every .Net Developer Must Know

Quick Summary:

.NET Core best practices are discussed in this article. When creating a web application, the most crucial factor to consider is performance. With each new.NET core release, Microsoft has demonstrated that.NET core is the most powerful, versatile, and complete framework for developing powerful Web, desktop, mobile, and cloud-based applications.

.NET core is one of the most popular and powerful frameworks among developers. It’s built from the ground up to be modular, lightweight, fast, and cross-platform. It contains the essential functionality needed to run basic.NET core programs. Other features are available as a NuGet package that can be added to the app as needed. As a result, the.NET core program performs better, has a smaller memory footprint, and is easier to maintain.

The following statistics can see the popularity of the .NET core.

  • According to Google Trends, there were approx of 66  queries for .NET core.

.Net Google Trends

.Net Core Stackoverflow Stats

.Net Stackoverflow Developers Stats

Now that we know the popularity of the .NET core let’s look at some of the best ways to help you have faster and smoother applications.  However, DOTNET Core best practices here are divided into categories. But keep in mind that various other categories are not included here:

.NET Core Best Practices

The .NET core best practices in this article are divided into seven categories. They are as follows:

  • Logging
  • Dependency injection
  • Security
  • Web API
  • Performance
  • Caching
  • Exception Handling

.NET Core Best Practices List
Let’s take a look at each one of them respectively:

.NET Core Best Practice #1: Logging

The logging dotnet core best practice will be discussed in this section. Logging generally takes a back place to unit testing and documentation in the software development environment. However, logging is a useful tool for debugging in production and gives valuable information about how your program is used in the real world. When things go wrong, the data in logs aid both the development and operations teams in troubleshooting and promptly resolving the issue. Although blogs are important, not all of them are created equal. When building them, you must follow best practices to ensure that they contain the correct information when you need it.

Logging Approach

Use Native libraries

Although it is feasible to create your logging library from scratch, most developers use a tried-and-true solution. Fortunately, the.NET SDK includes a useful native library named TraceSource. You must enable TraceSource in your config file, which is usually named application. Config is located in the same location as your program executable by adding the code below to the application. Config. file, you can enable the TraceSource.


<configuration>  
  <system.diagnostics>  
    <sources>  
      <source name="TraceTest"   
        switchName="sourceSwitch"   
        switchType="System.Diagnostics.SourceSwitch">  
        <listeners>  
          <add name="console"   
            type="System.Diagnostics.ConsoleTraceListener">  
            <filter type="System.Diagnostics.EventTypeFilter"   
              initializeData="Error"/>  
          </add>  
          <add name="myListener"/>  
          <remove name="Default"/>  
        </listeners>  
      </source>  
    </sources>  
    <switches>  
      <add name="sourceSwitch" value="Error"/>  
    </switches>  
  </system.diagnostics>  
</configuration>

Write logs in file not on console

Logging to the console is useful, but only for a short time. What if you need to compare the outcomes of a series of debug tests or keep track of an event log? You can route log messages to the most relevant places by writing them in separate locations.

These requirements are easily met with TraceSource. This logging library delivers its messages to a listner object, which then sends them to a chosen place like the console, the Windows Event Log, or a file. Add the following code to your configuration file to add a listener that writes to a file:


<configuration>
  <system.diagnostics>
    <trace autoflush="false" indentsize="4">
      <listeners>
        <add name="myListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="TextWriterOutput.log" />
        <remove name="Default" />
      </listeners>
    </trace>
  </system.diagnostics>
</configuration>

This snippet adds the listener object called myListener and writes your message to a log file titled TextWriterOutput.log.Then you can use the trace class to write text to the file:

Trace.TraceInformation("Test message.");
// You must close or flush the trace to empty the output buffer.
Trace.Flush();

You can also add a dynamic listener at runtime. The following code example shows how to dynamically add a new listener to the Trace. Listeners collection instead of using the static  configuration file:

Trace.Listeners.Add(new TextWriterTraceListener("TextWriterOutput.log", "myListener"));
Trace.TraceInformation("Test message.");
// You must close or flush the trace to empty the output buffer.
Trace.Flush();

LookinG for .NET developers to hire?

Get in touch to develop highly scalable web app project.

Use A Third Party Logging Library

The.NET SDK’s TraceSource library is useful in various contexts, but it lacks the kind of Highlevel APIs you’d expect in a conventional logging system. Libraries like Log4Net, Nlog, and serilog can handle the heavy work that comes with System. Diagnostic features.

So you don’t have to, trace the source. Which library you chose will be determined by your specific requirements, albeit they are all functionally comparable.ConfigureLog4Net must be configured in the same way as the trace source in your app’s configuration file.


<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
  </configSections>
  <log4net>
   <!-- In log4net, output destinations are known as appenders —>
    <!-- Roll the file when it reaches 1MB -->
    <appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender">
      <!-- Specify which file to write to -->
      <param name="File" value="myLoggerFile.log"/>
      <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
      <appendToFile value="true" />
      <rollingStyle value="Size" />
      <!-- How many log files should we keep? -->
      <maxSizeRollBackups value="2" />
      <!-- Roll to a new file when current one hits 1MB -->
      <maximumFileSize value="1MB" />
      <staticLogFileName value="true" />
      <!-- The format of each line in the log -->
      <layout type="log4net.Layout.PatternLayout">
              <param name="ConversionPattern" value="%d [%t] %-5p %c %m%n"/>
      </layout>
    </appender>
    <!-- Set root logger level to INFO and appender to LogFileAppender -->
    <root>
      <level value="INFO" />
      <appender-ref ref="LogFileAppender" />
    </root>
  </log4net>
</configuration>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 

Also Read: – .NET vs JAVA in 2022, which one to choose?

Make use of Logging Levels

The ability to disregard useless messages for a certain scenario is one of the most valuable aspects of a logging library like log4net. ALL, DEBUG, INFO, WARN, ERROR, FATAL, OFF are the log levels offered by Log4net, in order of priority.

The ALL setting logs everything, whereas the OFF setting logs nothing. These log levels can be assigned to each logger in the configuration file. Any communication with a priority lower than the configured level will be disregarded.

The benefit is that you can set the logging level directly in your configuration file. You don’t have to edit the code to change the types of log messages you care about; customized logs only require a separate config file.

Create various appenders in your config file to specify an output location for your messages based on the logging level. This configuration, for example, establishes two loggers: one that sends all messages at or above INFO to info.log, and another that sends all messages at or above ERROR to error.log.


<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
  </configSections>
  <log4net>
   <!-- In log4net, output destinations are known as appenders —>
    <!-- Roll the file when it reaches 1MB -->
    <appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender">
      <!-- Specify which file to write to -->
      <param name="File" value="myLoggerFile.log"/>
      <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
      <appendToFile value="true" />
      <rollingStyle value="Size" />
      <!-- How many log files should we keep? -->
      <maxSizeRollBackups value="2" />
      <!-- Roll to a new file when current one hits 1MB -->
      <maximumFileSize value="1MB" />
      <staticLogFileName value="true" />
      <!-- The format of each line in the log -->
      <layout type="log4net.Layout.PatternLayout">
              <param name="ConversionPattern" value="%d [%t] %-5p %c %m%n"/>
      </layout>
    </appender>
    <!-- Set root logger level to INFO and appender to LogFileAppender -->
    <root>
      <level value="INFO" />
      <appender-ref ref="LogFileAppender" />
    </root>
  </log4net>
</configuration>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 

Each file is written by a separate logger, so you need to use both inside of your code:


ILog Log = LogManager.GetLogger(typeof(LogTest));
ILog ErrorLog = LogManager.GetLogger("error");
Log.Info("Debug message");
ErrorLog.Error("Error message"));

While employing many loggers for tiny codebases is reasonable, things quickly get out of hand as the program develops in size and the number of loggers grows. Adding filters is a far more manageable method.

.NET Core Best Practice #2: Dependency Injection

Let’s take a look at the dependency injection .net core best practices. The following are some of the basics that you need to keep in mind.

Dependency Injection Technique

Constructor Injection

Constructor Injection is used to declare and obtain dependencies of a service construction. For better understanding, let’s take a look at the example.


public class ProductService
{
    private readonly IProductRepository _productRepository;    
public ProductService(IProductRepository productRepository)
    {
        _productRepository = productRepository;
    }    public void Delete(int id)
    {
        _productRepository.Delete(id);
    }
} 

Best practice for Constructor Injection

  • Explicitly define required dependencies in the service function Object() { [native code] }. As a result, the service cannot be built without its dependencies.
  • Assign Injected dependency to a field/property that is read-only.

Property Injection

Property injection is not supported by the standard dependency injection container in.NET Core. However, you can use another container that supports the Injection feature. Let’s have a look at an example:


using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; namespace MyApp
{
    public class ProductService
    {
        public ILogger<ProductService> Logger { get; set; } private readonly IProductRepository _productRepository;        public ProductService(IProductRepository productRepository)
        {
            _productRepository = productRepository;            Logger = NullLogger<ProductService>.Instance;
        }        public void Delete(int id)
        {
            _productRepository.Delete(id);            Logger.LogInformation(
                $"Deleted a product with id = {id}");
        }
    }
}

Product service is declaring a logger property with a public setter. Dependency injection container. Dependency Injection container can set the logger if it is available.

Best Practice for Property Injection

  • Only use property injection for dependencies that aren’t required. That means your service will function properly even if these requirements aren’t given.
  • If feasible, use the null object pattern and always check for null when utilizing dependencies.

Service Locator

Service locator pattern is another way of obtaining dependencies. For example:


public class ProductService
{
    private readonly IProductRepository _productRepository;
    private readonly ILogger<ProductService> _logger;    public ProductService(IServiceProvider serviceProvider)
    {
        _productRepository = serviceProvider
          .GetRequiredService<IProductRepository>();        _logger = serviceProvider
          .GetService<ILogger<ProductService>>() ??
            NullLogger<ProductService>.Instance;
    }    public void Delete(int id)
    {
        _productRepository.Delete(id);
        _logger.LogInformation($"Deleted a product with id = {id}");
    }
}

Product Support IServiceProvider is injected and used to resolve dependencies. If the specified dependency has never been registered, GetRequiredService throws an exception. GetService, on the other hand, simply returns null in this case.

When you resolve services within the function Object() { [native code] }, they are released.

Best Practice for Service Locator

  • If feasible, avoid using the service locator pattern (if the service type is known in the development time) because it obfuscates the interdependencies. This means that it’s difficult to notice the dependencies while building a service instance. This is particularly helpful for unit testing, where you might want to simulate some service dependencies.
  • If possible, resolve dependencies in the service function Object() { [native code] }. Resolving in a service method complicates and increases the risk of error in your application. In the next parts, I’ll go over the issues and remedies.

Singleton Services

Next on the list is singleton services. Singleton services are generally designed to keep an application state.  A cache is a good example:



public class FileService
{
    private readonly ConcurrentDictionary<string, byte[]> _cache;    public FileService()
    {
        _cache = new ConcurrentDictionary<string, byte[]>();
    }    public byte[] GetFileContent(string filePath)
    {
        return _cache.GetOrAdd(filePath, _ =>
        {
            return File.ReadAllBytes(filePath);
        });
    }
}

File service simply caches file contents to reduce disk reads. This service should be registered as a singleton, and otherwise, caching will not work as expected.

Best Practice for singleton service

  • If the service has access to a state, it should do it in a thread-safe way. Because all requests use the same instance of the service simultaneously, to ensure thread safety, use ConcurrentDictonary instead of Dictionary.
  • Singleton services should not be used for scoped or transient services. Because transient services may not be thread-safe by design if you must utilize them, make sure to employ multi-threading.
  • Singleton services are the most common source of memory leaks. They are not released or disposed of until the application is completed. As a result, if they instantiate classes (or inject) but do not release or dispose of them, they will remain in memory until the application is finished. Make careful to release/dispose of them at the appropriate time. See the section above on Resolving Services in a Method Body.
  • If you cache data (in this case, file contents), you should have a system in place to update or invalidate the cached data when the original data source changes (when a cached file changes on the disc, for this example).

.NET Core Best Practice #3: Security

This section will cover some of the security holes in a .NET core application. Let’s start by listing down some of the DOTNET core best practices for security:

.NET Security

Submit Sensitive Data Using Encryption

Never send sensitive information to the server in the form itself, such as passwords or credit card numbers. Before transferring data to the server, attackers can smell your data. When it comes to encryption algorithms, always use hashing algorithms like md5 or SHA256. On the client-side, make sure to utilize AES or DES, and on the server, make sure to use jQuery.

Submit Sensitive Data Using Encryption

Always use SSL

Secure Socket Layer (SSL) is an acronym for Secure Socket Layer. It encrypts communication between the client and the server using a very strong key. So, in your Asp.Net Core Application’s Startup. cs, you can choose to always use Secure Policy for Cookies.

Secure Socket Layer

Cross-Site Scripting

Hackers exploit Input Fields to send malicious scripts to steal user credentials and other important data in XSS Attacks. Let’s imagine our application includes an Add Product Form. The attacker creates a new product and adds a JavaScript snippet to the product description field. Hackers nasty script will run when our application displays that product on the product page with a description and will collect data for what he planned.

So, how can you protect your app from cross-site scripting attacks? The best practices listed below can help you secure your online application.

  • Only keep validated data in your database and use Regular Expressions on both the client and server sides.
  • HTML Encoding with Razor aids the execution of such scripts.
  • URL Encoding can also perform XSS, so use UrlEncoder to check and encode URL parameters.

cross site scripting

LINQ Can Defend Against SQL Injection.

SQL Injection attacks are one of the most methods of attack used to penetrate users’ data for years.  In this method, the attacker will put some condition or specific special characters in the field, which causes the execution query to change. For better understanding, let’s take a look at the example.

The example mentioned above is the most basic example for SQL Injection attacks. So now the question arises how to secure against SQL Injection?

Sql injection

Follow the below mentioned best practices:

  • Use Entity Framework Core.
  • Use parameterized queries.
  • Validate your inputs on the Server-side.
  • Using Stored Procedures is a good idea.

Secure Against SQL Injection

.NET Core Best Practice #4: Web API

The following are some points to keep in mind for best practices for dotnet core web API.

.NET Web API

Startup Class and the Server Configuration

The configuration services method for registering services and the configuration method for adding middleware components to the application’s pipeline are found in the starting class. As a result, it’s best to maintain the configuration service mechanism as clean and dependable as feasible. Of course, we must write the code to register the services within that method, but we may do so in a maintainable and reusable manner by utilizing the Extension method. Let’s have a look at how to register CORS incorrectly.


public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddPolicy("CorsPolicy",
            builder => builder.AllowAnyOrigin()
            .AllowAnyMethod()
            .AllowAnyHeader());
    });
}        

Although this will work fine, imagine the size of this method after registering dozens of services.The better way is to create the extension class with the static method.


public static class ServiceExtensions
{
    public static void ConfigureCors(this IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy("CorsPolicy",
                builder => builder.AllowAnyOrigin()
                .AllowAnyMethod()
                .AllowAnyHeader());
        });
    }
}

And then just to call this extension method upon the IServiceCollection type:


public void ConfigureServices(IServiceCollection services)
{
    services.ConfigureCors();
}

Environment Based Settings

While we work on it, our application is in the development environment. On the other hand, our application will be in the production environment as soon as it is released. As a result, keeping the settings of each environment separate is always a smart idea.

It’s really simple to do so in.NET Core. You must navigate to the appsetting.json file, which will be expanded to reveal the app settings. The file Development.json contains information about the development process. All of the parameters in this file will be used in the development environment. Another file, app settings, should be included. To use it in a production environment, type production.json. We can keep different app settings files in this location, and the.NET core will provide us with the appropriate settings based on the environment our application is in.

In Search of a Web App Development Company?

We at Aglowid offers end-to-end custom web app development solutions for Startups, SMBs, agencies, and enterprises

Data Access Layer (DAL)

The DAL is implemented inside the main project and initiated in every controller in many cases. This is not something we should do. We should always construct a separate service while working with DAL. This is critical for the.NET core project since we may register DAL as a separate service inside the IOC (Inversion of Control) container when we have it as a separate service. The IOC is a built-in part of the.NET core, and by registering DAL as a service inside it, we may use it in any class via function Object() { [native code] } injection. The interface should always be the basis for repository logic, and keeping it general will enable reusability.


public class RepoService
{
    private IRepository _repository;

    public RepoService(IRepository repository)
    {
        _repository = repository;
    }
}

Controllers

There shouldn’t be business logic inside it. The controller should always be as clean as possible.  So, our controllers  should be responsible for accepting the service instance through the constructor injection and organizing HTTP action methods (GET, POST, PUT, DELETE, PATCH..)


public class OwnerController: Controller
{
    private ILoggerManager _logger;
    private IRepoService _repoService;

    public OwnerController(ILoggerManager logger, IRepoService repoService)
    {
        _logger = logger;
        _repoService = repoService;
    }
    [HttpGet]
    public IActionResult GetAllOwners()
    {            
    }
    [HttpGet("{id}", Name = "OwnerById")]
    public IActionResult GetOwnerById(Guid id)
    {           
    }
    [HttpGet("{id}/account")]
    public IActionResult GetOwnerWithDetails(Guid id)
    {
    }
    [HttpPost]
    public IActionResult CreateOwner([FromBody]OwnerForCreationDto owner)
    {        
    }
    [HttpPut("{id}")]
    public IActionResult UpdateOwner(Guid id, [FromBody]OwnerForUpdateDto owner)
    {          
    }
    [HttpDelete("{id}")]
    public IActionResult DeleteOwner(Guid id)
    {         
    }
}

.NET Core Best Practice #5: Performance

The following section provides guidelines for performance best practices for .NET core.

dotnet core performance

Use asynchronous database operations with entity framework core.

The new feature of the C# language allows you to use keywords like async and await to implement asynchronous programming. Asynchronous programming allows concurrent code execution and avoids blocking the execution when conducting intensive I/O tasks such as connecting to external sources such as web apps.

Asynchronous extension methods in the Entity Framework core can be utilized to take advantage of asynchronous programming. The asynchronous extension method is now accessible for the vast majority of LINQ methods you use. You can use DB context to do database operations, such as getting data from a table.

Following is the example of asynchronous data access:

public List<Post> Posts()  {    return db.Posts.ToList();   }

Which can be re-written to:

public async Task<List<Post>> Posts()  {   return await db.Posts.ToListAsync();   }

Where DB is an object of DbContext of your application.

Monitoring and Optimizing queries

Monitoring is the most important aspect of any performance improvement project, and this is yet another recommendation for entity framework core and data access in your application. A new feature called simple logging is available starting with entity framework core 5.0. It has capabilities that report SQL statements generated by your LINQ queries and the time it takes for SQL queries to execute.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.LogTo(Console.WriteLine);

Additional configuration options, such as EnableSensitiveDataLogging and EnableDetailedErrors, make it easier to look at complex SQL, execution statistics, and detailed failures.

Return IEnumerable<T> or IAsyncEnumerable<T>

When an action returns an IEnumerable <T>, the serializer creates a synchronous collection iteration. As a result, calls are blocked, and there is a risk of thread pool starvation. Before returning the enumerable, use ToListAsync to avoid synchronous enumeration. Starting with.NET Core 3.0, you can use IAsyncEnumerable <T> instead of IEnumerable <T> to enumerate asynchronously.

Pool HTTP connections with HttpClientFactory

Even though HttpClient implements the IDisposable interface, it is intended to be reused. HttpClient instances that are closed leave sockets open in the TIME_WAIT state for a brief time. The app’s available sockets may be exhausted if a code path that produces and disposes of HttpClient objects is often used. As a remedy to this problem, HttpClientFactory was added in ASP.NET Core 2.1. To improve performance and reliability, it pools HTTP connections. The following are some of the .net core best practices for HTTP performance.

  • Directly creating and disposing of HttpClient objects is not recommended.
  • To get HttpClient objects, use HttpClientFactory.
  • Use HttpClientFactory to implement robust HTTP requests

.Net core best Practices for HTTP Performance

.NET Core Best Practice #6: Caching

Let’s take a look at the section explaining guidelines for .net core best practices for caching.

Caching in .NET

Caching with IMemoryCache and IDistributedCache

In.NET core, the standard interfaces IMemoryCache and IDistributed Cache have built-in mechanisms for caching data.

IMemoryCache

The mechanism is very similar to IMemoryCache.Runtime.Caching.MemoryCache is a NET 4 component. The user interface is quite simple.



public interface IMemoryCache: IDisposable
{
    bool TryGetValue(object key, out object value);
    ICacheEntry CreateEntry(object key);
    void Remove(object key);
}

This is only half of the story since there are various extension methods available, making the API considerably more user-friendly and robust.


public static class CacheExtensions
{
    public static TItem Get(this IMemoryCache cache, object key);
    
    public static TItem Set(this IMemoryCache cache, object key, TItem value, MemoryCacheEntryOptions options);

    public static bool TryGetValue(this IMemoryCache cache, object key, out TItem value);
    ...
}

You can register IMemoryCache in ConfigurationService using:

services.AddMemoryCache();

IDistributed Cache

For web scenarios, you will want to make IDistributedCache instead of IMemoryCache.


public interface IDistributedCache
{
    byte[] Get(string key);
    Task<byte[]> GetAsync(string key);

    void Set(string key, byte[] value, DistributedCacheEntryOptions options);
    Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options);

    void Refresh(string key);
    Task RefreshAsync(string key);

    void Remove(string key);
    Task RemoveAsync(string key);
}

The interface provides similar functionality to IMemoryCache, but there is some notable difference.

  • Additional async methods
  • Refresh methods (which just reset sliding expirations without retrieving data as far as I can tell)
  • Byte-based rather than object-based (though extension methods add the ability to use string values).

IMemoryCache vs IDistributedCache

DistributedCacheEntryOptions, like MemoryCacheEntryOptions, supports absolute and sliding expiration. However, token-based expiration is not supported. This makes adding cache dependencies much more difficult, and if you need this capability, you’ll have to create your solution.

The IDistributedCache interface is now available in three Microsoft implementations. There’s a local, in-memory version for development and versions for Redis and SQL Server.

Microsoft provides a local in-memory version of IDistributedCache. Extensions. Caching. The MVC package has already been brought in memory. If you need to manually add it, you can do so by typing:

services.AddDistributedMemoryCache();

The Microsoft.Extensions.Caching.Redis NuGet package contains the Redis implementation. Simply add the following to ConfigureServices after referencing the NuGet package:


services.AddDistributedRedisCache (option =>

{

option.Configuration = "your connection string";

option.InstanceName = "your instance name";

});

Caching JS, CSS, Images, etc

The static files middleware is used to provide images and other static files. A typical initial registration in the Configure function. This is how cs appears:

app.UseStaticFiles();

This code will allow static files to be served, but not in the most efficient manner. Because no-cache headers are used by default, the browser will repeatedly request these files, delaying your site and adding to the load on your server.

The good news is that enabling browser caching is as simple as changing the static file registration code. We’ve set caching to a year here:


app.UseStaticFiles(new StaticFileOptions
{
    OnPrepareResponse = (context) =>
    {
        var headers = context.Context.Response.GetTypedHeaders();

        headers.CacheControl = new CacheControlHeaderValue
        {
            Public = true,
            MaxAge = TimeSpan.FromDays(365)
        };
    }
});

.NET Core Best Practice #7:Exception Handling

A Well-designed app handles exceptions and errors to prevent app crashes. This section describes .NET core best practices for handling and creating exceptions.

DotNet Exception Handling

Use try/catch/finally blocks to recover from errors or release resources.

Try/catch blocks should be used around code that could potentially create an exception to recover your code. Always order exceptions in catch blocks from most derived to least derived. An exception is the root of all exceptions. A catch clause preceded by a catch clause for a base exception class does not handle more derived exceptions. If your code is unable to recover from an exception, don’t capture it. If feasible, enable methods farther up the call stack to recover.

Using statements or, finally, blocks, clean up the resources that have been assigned. When exceptions are thrown, it is preferable to use statements to clean up resources automatically. To clean up resources that don’t implement IDisposable, use finally blocks. Even when exceptions are thrown, code in a finally clause is usually always performed.

Without throwing exceptions, handle common situations.

Consider handling scenarios that are likely to occur but may cause an exception in a way that avoids the exception. You’ll get an InvalidOperationException if you try to close a connection that has already been closed. You can avoid this by using an if statement to check the state of the connection before closing it.


if (conn.State != ConnectionState.Closed)
{
    conn.Close();
}

You can catch the InvalidOperationException exception if you don’t check the connection state before closing it.


try
{
    conn.Close();
}
catch (InvalidOperationException ex)
{
    Console.WriteLine(ex.GetType().FullName);
    Console.WriteLine(ex.Message);
}

The approach to choose is determined by to how frequently the event is expected occur.

  • If the event infrequently occurs if the event is exceptional and indicates a mistake, use exception handling (such as an unexpected end-of-file). In normal circumstances, less code is performed when you employ exception handling.
  • If the event occurs frequently and may be regarded as part of normal operation, look for error conditions in the code. Because you avoid exceptions when you check for common error scenarios, less code is run.

Use the predefined .NET exception types.

When a preset exception class does not apply, create a new one. Consider the following scenario:

  • If a property set or method call isn’t appropriate for the object’s present state, throw an InvalidOperationException exception.
  • If improper parameters are given, throw an ArgumentException exception or one of the preset classes derived from ArgumentException.

have a unique app Idea?

Hire Certified Developers To Build Robust Feature, Rich App And Websites.

Conclusion

That’s all there is to it! These were some of the.NET core best practices categories. I hope these suggestions will assist you in improving the performance of your.NET Core application. The primary goal was to familiarise you with some of the most effective strategies for developing.NET code projects. Try to implement the majority of these.NET core recommended practices to assist you in improving the performance of your next project. If you face any problems while putting this list together or making adjustments, our professional team can assist you with answers. .NET core is one of our strong skills after almost a decade in the industry. Let’s talk about your requirements.

Get in Touch!

Saurabh Barot

Saurabh Barot is co-founder and Experienced Chief Technology Officer at Aglowid IT Solutions. His deep industry experience in Angular, React, Ruby on Rails, PHP, Mean Stack and just to name few, has helped the company reach to a new level. He believes that technology is all about exploring new possibilities. Whatever a mind can think (exception can't be ruled out), technology brings it into reality.

Related Posts