Current Path : /www/sites/www.coderblog.in/index/
Url:

NameSizeOptions
App_DataDIRnone
backupDIRnone
cgi-binDIRnone
cssDIRnone
imgDIRnone
metaDIRnone
wp-adminDIRnone
wp-contentDIRnone
wp-includesDIRnone
.htaccess3.35 KBDEL
.htaccess.bk1.84 KBDEL
404.html0.13 KBDEL
ads.txt1.07 KBDEL
favicon.ico2.19 KBDEL
index.php0.40 KBDEL
license.txt19.44 KBDEL
php.ini0.58 KBDEL
readme.html7.25 KBDEL
service.php0.00 KBDEL
web.config2.87 KBDEL
wp-activate.php7.21 KBDEL
wp-blog-header.php1.21 KBDEL
wp-comments-post.php2.27 KBDEL
wp-config-sample.php3.26 KBDEL
wp-config.php2.98 KBDEL
wp-cron.php5.49 KBDEL
wp-links-opml.php2.44 KBDEL
wp-load.php3.84 KBDEL
wp-login.php50.21 KBDEL
wp-mail.php8.52 KBDEL
wp-settings.php29.38 KBDEL
wp-signup.php33.71 KBDEL
wp-trackback.php4.98 KBDEL
xmlrpc.php3.13 KBDEL
asp.net core – Coder Blog https://www.coderblog.in Join the coding revolution! Learn, share, and grow together! Tue, 03 Jun 2025 04:02:30 +0000 en-US hourly 1 https://wordpress.org/?v=6.8.1 How to use Quartz.NET for job scheduling on ASP.NET Core https://www.coderblog.in/2025/06/how-to-use-quartz-net-for-job-scheduling-on-asp-net-core/ https://www.coderblog.in/2025/06/how-to-use-quartz-net-for-job-scheduling-on-asp-net-core/#comments Tue, 03 Jun 2025 04:02:25 +0000 https://www.coderblog.in/?p=1262 1. Introduction Quartz.NET is a powerful library that help to create the schedule job on .Net project, you can

<p>The post How to use Quartz.NET for job scheduling on ASP.NET Core first appeared on Coder Blog.</p>

]]>
1. Introduction

Quartz.NET is a powerful library that help to create the schedule job on .Net project, you can setup and execute the schedule job on IIS, that will be very convenient for a web application. But there is a problem for running schedule job on IIS, if there is no access for a long time in a website, IIS will be stop running the schedule job, but don’t worry, I will let you know how to fix this issue, and I will show you how to create a setting to control the schedule job.

2. Install Quartz.NET

You can easy to install Quartz.NET from NuGet Package in Visual Studio, just search Quartz in Manage NuGet Packages and choose Quartz.NET to install.

Or you also can execute below command to install if you are using Visual Studio Code

Install-Package Quartz

3. Usage

Actually, you can find the detail usage from Quartz.NET official website, so today, I will show you how to use Quartz.NET as a background service in ASP.NET Core project, and you can easy setup it in your app.settings

  1. Create Schedule Model

We need to create the model for app.settings mapping

//JobSettings.cs

namespace API.Schedules
{
    /// <summary>
    /// Schedule Job Settings Section in appsettings.json
    /// </summary>
    public class JobSettings
    {
        public List<JobDetail> Jobs { get; set; }
        public JobSettings()
        {
            Jobs = new List<JobDetail>();
        }
    }

    /// <summary>
    /// Schedule Job Detail
    /// </summary>
    public class JobDetail
    {
        /// <summary>
        /// The type name of the job's class for reference
        /// </summary>
        /// <value></value>
        public required string TypeName { get; set; }
        /// <summary>
        /// The unique key of the job
        /// </summary>
        /// <value></value>
        public required string JobKey { get; set; }
        /// <summary>
        /// Set the schedule with cron expressions
        /// </summary>
        /// <value></value>
        public string? CronSchedule { get; set; }
        /// <summary>
        /// Schedule interval in seconds
        /// </summary>
        /// <value></value>
        public int? IntervalSeconds { get; set; }
        /// <summary>
        /// Schedule interval in minutes
        /// </summary>
        /// <value></value>
        public int? IntervalInMinutes { get; set; }
        /// <summary>
        /// Schedule interval in hours
        /// </summary>
        /// <value></value>
        public int? IntervalInHours { get; set; }
        /// <summary>
        /// Whether the job is active
        /// </summary>
        /// <value></value>
        public bool IsActive { get; set; }
    }
}
  1. Create the Schedule Service

Create the schedule service for register background service

//ScheduleService.cs

using Quartz;

namespace API.Schedules
{
    public static class ScheduleService
    {
        public static void RegisterBackgroundServices(this IServiceCollection services, JobSettings jobSettings)
        {
            // Register the job settings
            jobSettings.Jobs.Where(s => s.IsActive).ToList().ForEach(setting =>
            {
                // Use a Scoped container to create jobs. This is necessary to inject services into the job.
                services.AddQuartz(options =>
                {
                    // Register the job with TypeName
                    Type? _f = Type.GetType(setting.TypeName);
                    if (_f != null)
                    {
                        var jobKey = JobKey.Create(setting.JobKey);

                        if (!string.IsNullOrEmpty(setting.CronSchedule))
                            //trigger with cron expressions
                            options.AddJob(_f, jobKey).AddTrigger(trigger => trigger.ForJob(jobKey).WithCronSchedule(setting.CronSchedule).StartNow());
                        else if (setting.IntervalSeconds.HasValue)
                            //trigger with IntervalSeconds
                            options.AddJob(_f, jobKey).AddTrigger(trigger => trigger.ForJob(jobKey)
                                .WithSimpleSchedule(s => s.WithIntervalInSeconds(setting.IntervalSeconds.Value).RepeatForever()));
                        else if (setting.IntervalInMinutes.HasValue)
                            //trigger with minutes
                            options.AddJob(_f, jobKey).AddTrigger(trigger => trigger.ForJob(jobKey)
                                .WithSimpleSchedule(s => s.WithIntervalInMinutes(setting.IntervalInMinutes.Value).RepeatForever()));
                        else if (setting.IntervalInHours.HasValue)
                            //trigger with hours
                            options.AddJob(_f, jobKey).AddTrigger(trigger => trigger.ForJob(jobKey)
                                .WithSimpleSchedule(s => s.WithIntervalInHours(setting.IntervalInHours.Value).RepeatForever()));
                    }
                });
            });
            // Add the Quartz services
            services.AddQuartzHostedService(options =>
            {
                options.WaitForJobsToComplete = true;
                options.AwaitApplicationStarted = true;
            });
        }
    }
}

and register schedule job settings in Program.cs file

builder.Services.Configure<JobSettings>(builder.Configuration.GetSection("JobSetting"));

builder.Services.RegisterBackgroundServices(builder.Configuration.GetSection("JobSetting").Get<JobSettings>() ?? new JobSettings());
  1. Add the settings

We can add the JobSetting section into app.settings for easy to control the schedule jobs

  "JobSetting": {
    "Jobs": [
      {
        "TypeName": "API.Schedules.CronBgJob, API",
        "JobKey": "CronScheduler",
        "CronSchedule": "0 30 9,14,16,23,7 * * ?", //trigger the job every day at 9:30,14:30...
        "IsActive": true
      },
      {
        "TypeName": "API.Schedules.SimpleBgJob, API",
        "JobKey": "SimpleScheduler",
        "IntervalInMinutes": 10,  //trigger the job in every 10 minutes
        "IsActive": true
      }
    ]
  },

There are two jobs of the above settings, one for cron job with cron expressions, you can find the details how to use it, another is a simple job, it will trigger in every 10 minutes when the application is startup.

Of course, you can also set the interval with seconds or hours in the settings, because we already handled these in the schedule job service.

  1. Create the jobs

Now we can create the jobs. First we create a cron job below

//CronBgJob.cs

using Quartz;
namespace API.Schedules
{
    [DisallowConcurrentExecution]
    public class CronBgJob : IJob
    {    
        private readonly ILogger<CronBgJob> _logger;
    
        public CronBgJob(ILogger<CronBgJob> logger)
        {
            _logger = logger;
        }
    
        public Task Execute(IJobExecutionContext context)
        {
            _logger.LogDebug("this is a cron job ");
            Console.WriteLine("testing cron job log in schedule");
            return Task.CompletedTask;
        }
    }
}

Create a simple job

//SimpleBgJob.cs

using Quartz;
namespace API.Schedules
{
    [DisallowConcurrentExecution]
    public class SimpleBgJob : IJob
    {    
        private readonly ILogger<SimpleBgJob> _logger;
    
        public SimpleBgJob(ILogger<SimpleBgJob> logger)
        {
            _logger = logger;
        }
    
        public Task Execute(IJobExecutionContext context)
        {
            _logger.LogDebug("this is a simple job ");
            Console.WriteLine("testing simple job log in schedule");
            return Task.CompletedTask;
        }
    }
}

As you can see, there is no any difference with these jobs except the class name, so the main point is the app.settings to define the job’s type and how to trigger them.

4. Setup the IIS

By default IIS will recycle and stop the app pools from time to time, that’s mean if you start the Quartz with a web application, the scheduler might get disposed later on due to site inactivity. But you are using IIS8, you can setup the IIS to let it keep running to resolve this issue.

1) Install the Application Initialization module in IIS

2) Go to IIS website advanced settings, set the Preload Enabled to True

The `Preload Enabled` setting along with the start mode setting can be used to ‘warm up’ your web application.

3) Go to the application pool advanced settings, set the Start Mode to AlwaysRunning

When you set the startMode property of your application pool to AlwaysRunning a worker process is spawned as soon as IIS starts up and does not wait for the first user request.

5. Conclusion

Quartz.NET is a great library for handle schedule job of .Net project, you can use the cron expressions to set the schedule, and there are still many features you can find in the official guideline, you just need to pay attention to the IIS recycle issue, it will stop your job, but you can also fix it base on the above settings.

In the end, if you enjoyed this article, please follow me here on Medium for more stories about .Net Core, Angular, and other Tech!

Loading

<p>The post How to use Quartz.NET for job scheduling on ASP.NET Core first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2025/06/how-to-use-quartz-net-for-job-scheduling-on-asp-net-core/feed/ 8
How to Use Runtime Messages in Asp.net Core https://www.coderblog.in/2024/11/how-to-use-runtime-messages-in-asp-net-core/ https://www.coderblog.in/2024/11/how-to-use-runtime-messages-in-asp-net-core/#comments Tue, 19 Nov 2024 15:27:49 +0000 https://www.coderblog.in/?p=1251 1. Introduction When you are creating the APIs, you should need to handle many messages and return them

<p>The post How to Use Runtime Messages in Asp.net Core first appeared on Coder Blog.</p>

]]>
1. Introduction

When you are creating the APIs, you should need to handle many messages and return them to the client, for example, if there is an error or can’t find the data, you need to return the corresponding messages, you can hardcode these messages in the API functions, but just thinking about if your API in the UAT and maybe users want to change the messages again and again, and you will need to find these messages in codes and re-publish them again and again.

If we put all of the messages in the XML files and can be updated in runtime, then we don’t need to change and re-publish the source code, that’s would be great, right?

Ok, let’s start!

2. Define the XML file

As we want to put the messages into XML file, we need to define the XML format, we can base on the XML attribute to get the node’s value, so the format as below

<?xml version="1.0" encoding="utf-8" ?>
<Messages>
 <Item id="NoDataFound">There is no data found!</Item>
 <Item id="InvalidDateFormat">The date value is invalid!</Item>
</Messages>

We can split the XML file based on each function, this can be easier to manage and you can find the message quickly, for example:

Common.xml
User.xml

3. May the key of messages

There is an id attribute in our XML file, we need to get the message based on this id, but I don’t want to hardcode it so we need to create mapping keys below

public static class MsgKey
{
    public static class Common
    {
        public const string NoDataFound = "NoDataFound";
        public const string InvalidDateFormat = "InvalidDateFormat";
    }

    public static class User
    {
        public const string NoUserFound = "NoUserFound";
        public const string UserExist = "UserExist";
        public const string UserInvalid = "UserInvalid";
    }
}

4. Create the message helper

We need to handle the message from different XML files, so we need to load all files in runtime

//load specify XML files based on the function or section
List<string> xmlFiles = files.Split(',').ToList();
foreach (var file in xmlFiles)
{
    //handle the XML file one by one
    LoadMessages(file);
}

Handle the XML file

private void LoadMessages(string xmlFile)
{
    var filePath = Path.Combine(Directory.GetCurrentDirectory(), $"Messages\\{xmlFile}.xml");
    var xdoc = XDocument.Load(filePath);
    var items = xdoc.Descendants("Item")
              .Select(item => new
              {
                  Id = item.Attribute("id").Value,
                  Value = item.Value
              });
    foreach (var msg in items)
    {
        var id = msg.Id;
        var message = msg.Value;
        if (id != null && !Msg.ContainsKey(id))
        {
            Msg[id] = message;
        }
    }
}

the completed codes below

using System.Xml.Linq;

public class MessageHelper
{
    public MessageHelper() { }

    public Dictionary<string, string> Msg { get; private set; }

    public void Init(string files)
    {
        Msg = new Dictionary<string, string>();

        List<string> xmlFiles = files.Split(',').ToList();
        foreach (var file in xmlFiles)
        {
            LoadMessages(file);
        }
    }

    private void LoadMessages(string xmlFile)
    {
        var filePath = Path.Combine(Directory.GetCurrentDirectory(), $"Messages\\{xmlFile}.xml");
        var xdoc = XDocument.Load(filePath);

        var items = xdoc.Descendants("Item")
                  .Select(item => new
                  {
                      Id = item.Attribute("id").Value,
                      Value = item.Value
                  });

        foreach (var msg in items)
        {
            var id = msg.Id;
            var message = msg.Value;
            if (id != null && !Msg.ContainsKey(id))
            {
                Msg[id] = message;
            }
        }
    }
}

5. Usage

1) Register the message helper in program.cs

builder.Services.AddScoped<MessageHelper>();

2) Create a testing API controller MessageTesterController, inject the message helper and pass the XML file name

protected readonly ILogger<MessageTesterController> _logger;
private MessageHelper _messageHelper;
public MessageTesterController(ILogger<MessageTesterController> logger, MessageHelper messageHelper)
{
    this._logger = logger;
     _messageHelper = messageHelper;
     //pass the XML file name in Message folder
     _messageHelper.Init("Common,User");
}

Use the key to get the message:

var noDataFoundMsg = _messageHelper.Msg[MsgKey.Common.NoDataFound];
var userInvalidMsg = _messageHelper.Msg[MsgKey.User.UserInvalid];

The completed codes below:

namespace MyDemo.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class MessageTesterController : ControllerBase
    {
        protected readonly ILogger<MessageTesterController> _logger;

        private MessageHelper _messageHelper;

        public MessageTesterController(ILogger<MessageTesterController> logger, MessageHelper messageHelper)
        {
            this._logger = logger;
            _messageHelper = messageHelper;
            _messageHelper.Init("Common,User");
        }

        /// <summary>
        /// Get the dynamic messages
        /// </summary>
        /// <returns></returns>
        [HttpGet("get-message")]
        public async Task<ActionResult<Object>> GetMessage()
        {
            var apiResult = new ApiResult<Object>();

            try
            {
                var noDataFoundMsg = _messageHelper.Msg[MsgKey.Common.NoDataFound];
                var userInvalidMsg = _messageHelper.Msg[MsgKey.User.UserInvalid];

                _logger.LogDebug("noDataFoundMsg ================ {0}", noDataFoundMsg);
                _logger.LogDebug("userInvalidMsg ================ {0}", userInvalidMsg);

                var returnObj = new
                {
                    noDataFound = noDataFoundMsg,
                    userInvalid = userInvalidMsg
                };

                apiResult.Data = returnObj;

                return Ok(apiResult);

            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "MessageTester Exception");
                apiResult.Success = false;
                apiResult.Message = ex.Message;
                return StatusCode(500, apiResult);
            }
        }

    }
}

And you can find the result in the log file:

6. Conclusion

Put the messages in XML files can be easy to manage, and you can update the messages in runtime, this can save you time to re-build and publish your project again and again, also, you can handle the internationalization with this approach, please feel free to let me know if you have any other ideas 🙂

And you can find the completed source code and demo project on my github.

Loading

<p>The post How to Use Runtime Messages in Asp.net Core first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2024/11/how-to-use-runtime-messages-in-asp-net-core/feed/ 18
How to support multiple appsettings in asp.net 8 https://www.coderblog.in/2024/07/how-to-support-multiple-appsettings-in-asp-net-8/ https://www.coderblog.in/2024/07/how-to-support-multiple-appsettings-in-asp-net-8/#comments Mon, 29 Jul 2024 13:36:04 +0000 https://www.coderblog.in/?p=1232 1. Introduction Most of time, we need to publish a .Net Core project to difference environment with corresponding

<p>The post How to support multiple appsettings in asp.net 8 first appeared on Coder Blog.</p>

]]>
1. Introduction

Most of time, we need to publish a .Net Core project to difference environment with corresponding configuration, so we should something config files like below

appsettings.dev.json
appsettings.uat.json
appsettings.demo.json
appsettings.prod.json

by default, .Net program only get the configuration value from appsettings.json, so how can we read the specified configuration from these files? and how to make sure only one environment configuration file to be copied to the publish folder?

2. What I want to do

There are many tutorials shows you how to use ASPNETCORE_ENVIRONMENT to handle multiple environments, for this approach you need to define or update ASPNETCORE_ENVIRONMENT when you launch or publish the project, but I just want to publish the project with a command and don’t need to change anything (including any config files).

Because I want to deploy the project to difference server and don’t want to change their configuration or environment anymore, this can make sure the deployment source code is stable and avoid to update to wrong environment variables.

So I suppose the publish flow should be just executed the publish command and then copy the release folder to server.

3. Setup multiple environments configuration file

For do that, we need to load the specified environment config file with below in Program.cs

builder.Configuration.AddJsonFile('appsettings.dev.json', optional: true, reloadOnChange: true);

and use the precompiled command to load difference items

#if DEV
builder.Configuration.AddJsonFile('appsettings.dev.json', optional: true, reloadOnChange: true);
#elif DEMO
builder.Configuration.AddJsonFile('appsettings.demo.json', optional: true, reloadOnChange: true);
#elif UAT
builder.Configuration.AddJsonFile('appsettings.uat.json', optional: true, reloadOnChange: true);
#elif PROD
builder.Configuration.AddJsonFile('appsettings.prod.json', optional: true, reloadOnChange: true);
#endif

but wait, for using these precompiled constants, we need to define the constants in the project *.csproj file

  <PropertyGroup Condition=" '$(Configuration)' == 'demo'">
    <DefineConstants>DEMO</DefineConstants>
  </PropertyGroup>

  <PropertyGroup Condition=" '$(Configuration)' == 'dev'">
    <DefineConstants>DEV</DefineConstants>
  </PropertyGroup>

  <PropertyGroup Condition=" '$(Configuration)' == 'uat'">
    <DefineConstants>UAT</DefineConstants>
  </PropertyGroup>

  <PropertyGroup Condition=" '$(Configuration)' == 'prod'">
    <DefineConstants>PROD</DefineConstants>
  </PropertyGroup>

We can define custom constant base on the condition, and the $(Configuration) value is passed from the build command (I will show you later).

In this case, we will find several appsettings files(all environments file) in publish folder, but we should only need one file for current environment, so we need to continue to add below code in *.csproj file

  <ItemGroup Condition=" '$(Configuration)' == 'dev'">
    <Content Remove="appsettings.demo.json" />
    <Content Remove="appsettings.uat.json" />
    <Content Remove="appsettings.prod.json" />
  </ItemGroup>
  <ItemGroup Condition=" '$(Configuration)' == 'demo'">
    <Content Remove="appsettings.dev.json" />
    <Content Remove="appsettings.uat.json" />
    <Content Remove="appsettings.prod.json" />
  </ItemGroup>
  <ItemGroup Condition=" '$(Configuration)' == 'uat'">
    <Content Remove="appsettings.demo.json" />
    <Content Remove="appsettings.dev.json" />
    <Content Remove="appsettings.prod.json" />
  </ItemGroup>
  <ItemGroup Condition=" '$(Configuration)' == 'prod'">
    <Content Remove="appsettings.demo.json" />
    <Content Remove="appsettings.dev.json" />
    <Content Remove="appsettings.uat.json" />
  </ItemGroup>

As you see, we will remove other useless appsettings file base on the condition, so it will only copy the current environment config file to publish folder.

Ok, finally, we can use below command to publish the project base on difference environment

dotnet publish -c demo -r win-x64 -p:PublishReadyToRun=true 

The main point here is -c parameter, this is the $(Configuration) what we use in csproj file.

For example, the above command will only generate below config files in publish folder

appsettings.json
appsettings.demo.json

4. Conclusion

We should use multiple environments configuration for our project, we can simple to use a flag in appsettings.json for that, for example evn:uat, but in this case, we need to change the appsettings.json every time when publish the project, and also need to handle more logic in the source code, to avoid to update the wrong settings in other environments, I suggest to use a publish command for handle difference environment, after executed the command, then will generate the source code to corresponding publish folder, and then we just need to copy these file to server.

Loading

<p>The post How to support multiple appsettings in asp.net 8 first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2024/07/how-to-support-multiple-appsettings-in-asp-net-8/feed/ 11
How to do Unit Testing for the Core Api Project with Repository https://www.coderblog.in/2023/08/how-to-do-unit-testing-for-the-core-api-project-with-repository/ https://www.coderblog.in/2023/08/how-to-do-unit-testing-for-the-core-api-project-with-repository/#comments Sun, 13 Aug 2023 01:48:40 +0000 https://www.coderblog.in/?p=964 This is post 8 of 8 in the series “Create a website with ASP.NET Core and Angular” In

<p>The post How to do Unit Testing for the Core Api Project with Repository first appeared on Coder Blog.</p>

]]>

This is post 8 of 8 in the series “Create a website with ASP.NET Core and Angular”

In this series, I will show you how to create the web API with .Net core and communicate with Angular in frontend, and I will keep to update it!

  1. How to setup VS Code environment for ASP.NET Core Project
  2. Use code first to connect database in .Net Core API
  3. Use the Repository pattern in .Net core
  4. Create .Net Core Api controller for CRUD with repository
  5. Use the Serilog in .Net Core
  6. Create Angular Project and Communicate with .Net Core
  7. Create Paging Data from .Net Core Api
  8. How to do Unit Testing for the Core Api Project with Repository

1. Introduction

We have created a .Net Core Api project before, this post will discuss how to do the unit testing for the project.

First question, why should we do the unit testing?

Here are some key reasons why unit testing is important and worth investing:

Finds bugs early – Unit tests help catch bugs early in the development cycle, when they are less expensive to fix.

Improves design – Writing unit tests forces you to think about how your code will be used and drive more modular, reusable code.

Facilitates change – Good unit tests allow developers to refactor code quickly and verifiably. Tests help ensure changes don’t break existing functionality.

Reduces risks – Tests provide a safety net to catch edge cases and errors. They build confidence that refactoring and changes won’t introduce hard-to-find bugs.

Documents code – Unit tests document how production code should be used. They are live examples of how to call various methods.

Enables automation – Automated testing catches issues quickly and frees developers from manual testing. Tests can be run on every code change.

Improves collaboration – Unit tests enable multiple developers to collaborate on code using a shared suite of tests to ensure changes don’t cause issues.

Avoids technical debt – Lack of tests creates a technical debt that slows future development. Writing tests avoid this debt.

2. Create the Unit Testing Project

As I have mentioned before, I like to use VS Code, so I will also base on VS Code and xUnit Test to demonstrate how to do it! 🙂

If you have installed the VS Code extension vscode-solution-explorer then you can easy to create a xUnit Test project from the solution explorer. Just right click the solution and “Add a new project”

then use the xUnit Test Project template, and input the project name MyDemo.Test, then will be created a xUnit Test project

3. Mock Framework

We need to use a mock framework for creating fake objects that simulate the behavior of real objects for testing. So we can install Moq from Nuget for our testing project

4. Create the testing cases

4.1. Mock the Repository

Before we start, we need to create the mock object for repository. Because the mock framework needs to inject into the function logic for testing, so we must use the interfaces programming for the project (like a repository pattern, so this is one of the benefits of using a repository pattern :)).

We can create a MockIUserRepository class to handle user repository testing

//MyDemo.Test/Mocks/MockIUserRepository.cs

using System.Linq.Expressions;
using Moq;
using MyDemo.Core.Data.Entity;
using MyDemo.Core.Repositories;

namespace MyDemo.Test.Mocks
{
    public class MockIUserRepository
    {
        //todo...
    }
}

because we use IQueryable for GetAll in our repository method

public IQueryable<T> GetAll() => _context.Set<T>().AsNoTracking();

so we also need to return the same object for a mock object, then we create the user data as below

public static IQueryable<User> GetUsers()
{
    var users = new List<User>() {
    new User() {
        Id = 100,
        Name = "Unit Tester 1",
        Email = "unit.tester1@abc.com",
        IsActive = true,
        CreatedAt = DateTime.Now.AddDays(-2),
        UpdatedAt = DateTime.Now.AddDays(-2)
    },
    new User() {
        Id = 200,
        Name = "Unit Tester 2",
        Email = "unit.tester2@abc.com",
        IsActive = true,
        CreatedAt = DateTime.Now.AddDays(-1),
        UpdatedAt = DateTime.Now.AddDays(-1)
    }
    }.AsQueryable();

    return users;
}

then we create the mock instance for IUserRepository

var mock = new Mock<IUserRepository>();

setup the mock instance to return a list of users when the GetAll method is called

//get the fake user data
var users = GetUsers(); 
//setup the GetAll and return the user data
mock.Setup(u => u.GetAll()).Returns(()=> users);

setup the GetItemWithConditionAsync and GetWithConditionAsync methods, because these methods need to pass an expression parameter for query data, so we also need to use the Expression<Func<User, bool> for mock the parameter, and this is an async method, so need to use ReturnsAsync for return the value

mock.Setup(u => u.GetItemWithConditionAsync(It.IsAny<Expression<Func<User, bool>>>())).
        ReturnsAsync((Expression<Func<User, bool>> expr) => users.FirstOrDefault(expr));

mock.Setup(u => u.GetWithConditionAsync(It.IsAny<Expression<Func<User, bool>>>())).
                ReturnsAsync((Expression<Func<User, bool>> expr) => users.Where(expr));

the complete codes as below

//MyDemo.Test/Mocks/MockIUserRepository.cs

using System.Linq.Expressions;
using Moq;
using MyDemo.Core.Data.Entity;
using MyDemo.Core.Repositories;

namespace MyDemo.Test.Mocks
{
    public class MockIUserRepository
    {
        public static Mock<IUserRepository> GetMock()
        {
            var mock = new Mock<IUserRepository>();

            var users = GetUsers();

            mock.Setup(u => u.GetAll()).Returns(()=> users);

            mock.Setup(u => u.GetItemWithConditionAsync(It.IsAny<Expression<Func<User, bool>>>())).
                ReturnsAsync((Expression<Func<User, bool>> expr) => users.FirstOrDefault(expr));

            mock.Setup(u => u.GetWithConditionAsync(It.IsAny<Expression<Func<User, bool>>>())).
                ReturnsAsync((Expression<Func<User, bool>> expr) => users.Where(expr));

            return mock;
        }

        public static IQueryable<User> GetUsers()
        {
            var users = new List<User>() {
            new User() {
                Id = 100,
                Name = "Unit Tester 1",
                Email = "unit.tester1@abc.com",
                IsActive = true,
                CreatedAt = DateTime.Now.AddDays(-2),
                UpdatedAt = DateTime.Now.AddDays(-2)
            },
            new User() {
                Id = 200,
                Name = "Unit Tester 2",
                Email = "unit.tester2@abc.com",
                IsActive = true,
                CreatedAt = DateTime.Now.AddDays(-1),
                UpdatedAt = DateTime.Now.AddDays(-1)
            }
            }.AsQueryable();
            return users;
        }
    }
}

4.2. GetUsers testing case

Ok, now we can create the first testing case to get all user data. Get the mock instance and call the repository GetAll method

[Fact]
public void Test1_GetUsers()
{
    //get the mock instance
    var mockUserRepository = MockIUserRepository.GetMock();
    //call the repository to get user data
    var users = mockUserRepository.Object.GetAll();

    //test the result

    //the users should not be null
    Assert.NotNull(users);
    //the users should be an IQueryable<User> object
    Assert.IsAssignableFrom<IQueryable<User>>(users);
    //should be only 2 items in the user list (this is the fake data that we created)
    Assert.Equal(2, users.Count());
}

VS Code can be very helpful to add the actions on the testing case method, you can click the Run Test or Debug Test to run the test

if everything is ok, you will see the testing result in terminal

4.3. Get user by Id testing case

This time we want to test the method GetUser of UserController. We need to create the user controller instance first, as you know there is a constructor for receiving two parameters in the user controller, that’s means we also need to pass these when new it

public UserController(IUserRepository userRepository, ILogger<User> logger)
{
    this._userRepository = userRepository;
    this._logger = logger;
}

but how should we pass the repository and logger objects? Ok, we can also mock them:

//get the user repository mock instance
var mockUserRepository = MockIUserRepository.GetMock();
//create the ILogger mock instance
var logger = Mock.Of<ILogger<User>>();

now we can new a user controller instance

var userController = new UserController(mockUserRepository.Object, logger);

call the GetUser from controller, because the fake user id is 100 and 200, so we try to pass 100 to get a user

//get the user data by id
var result = userController.GetUser(100);

//convert the result to ObjectResult so that we can check the status codes
var objResult = result.Result.Result as ObjectResult;

//convert the result value to our define's ApiResult object, and we can get the user data
var apiResult = objResult.Value as ApiResult<User>;

//test the result values
Assert.NotNull(result);
Assert.Equal(StatusCodes.Status200OK, objResult.StatusCode);
Assert.IsAssignableFrom<User>(apiResult.Data);
Assert.Equal("Unit Tester 1", apiResult.Data.Name);

the complete codes as below

[Fact]
public void Test2_GetUserById()
{
    var mockUserRepository = MockIUserRepository.GetMock();

    //mock the logger
    var logger = Mock.Of<ILogger<User>>();

    var userController = new UserController(mockUserRepository.Object, logger);

    var result = userController.GetUser(100);
    var objResult = result.Result.Result as ObjectResult;
    var apiResult = objResult.Value as ApiResult<User>;

    Assert.NotNull(result);
    Assert.Equal(StatusCodes.Status200OK, objResult.StatusCode);
    Assert.IsAssignableFrom<User>(apiResult.Data);
    Assert.Equal("Unit Tester 1", apiResult.Data.Name);
}

4.4. Create user testing case

You should know the logic and concept, so I just show you the codes below

[Fact]
public void Test3_CreateUser()
{
    var mock = MockIUserRepository.GetMock();
    //mock the logger
    var logger = Mock.Of<ILogger<User>>();
    var userController = new UserController(mock.Object, logger);

    //call the Post method to create a user
    var result = userController.PostUser(new User()
    {
        Id = 300,
        Name = "new user",
        Email = "newTester@abc.com",
        IsActive = true,
        CreatedAt = DateTime.Now,
        UpdatedAt = DateTime.Now
    });

    var objResult = result.Result.Result as ObjectResult;
    var apiResult = objResult.Value as ApiResult<User>;

    Assert.NotNull(result);
    Assert.Equal(StatusCodes.Status200OK, objResult.StatusCode);
    Assert.Equal(true, apiResult.Success);
    Assert.Equal(300, apiResult.Data.Id);
}

4.5. Update user testing case

The codes as below

[Fact]
public void Test4_UpdateUser()
{
    var mock = MockIUserRepository.GetMock();
    //mock the logger
    var logger = Mock.Of<ILogger<User>>();
    var userController = new UserController(mock.Object, logger);

    var result = userController.PutUser(new User()
    {
        Id = 100,
        Name = "update user",
        Email = "updateTester@abc.com",
        IsActive = true,
        CreatedAt = DateTime.Now,
        UpdatedAt = DateTime.Now
    });

    var objResult = result.Result.Result as ObjectResult;
    var apiResult = objResult.Value as ApiResult<User>;

    Assert.NotNull(result);
    Assert.Equal(StatusCodes.Status200OK, objResult.StatusCode);
    Assert.Equal(true, apiResult.Success);
    Assert.Equal("update user", apiResult.Data.Name);
}

4.6. Delete user testing case

Because there is no user data returned by the delete function, so we just need to check the success status from the Api

[Fact]
public void Test5_DeleteUserById()
{
    var mock = MockIUserRepository.GetMock();
    //mock the logger
    var logger = Mock.Of<ILogger<User>>();
    var userController = new UserController(mock.Object, logger);

    var result = userController.DeleteUser(100);
    var objResult = result.Result as ObjectResult;
    var apiResult = objResult.Value as ApiResult<Object>;

    Assert.NotNull(result);
    Assert.Equal(StatusCodes.Status200OK, objResult.StatusCode);

    //check the success whether is true
    Assert.Equal(true, apiResult.Success);
}

In the end, we can run all tests on the class level

and find the result below

5. Summarize

We learned how to do the unit test with the xUnit Test and how to mock the data. There is an important thing you should know if you want to mock data for testing, you must use the interface programming and dependency injection, so if you call the get user method directly from a help method, then you can’t mock it for testing, you can put the method into service and implement from an interface.

The other thing that needs to note is that must use the same parameter type with your method when you setup a mock instance.

Please let me know if you have any ideas for that! 😀

Loading

<p>The post How to do Unit Testing for the Core Api Project with Repository first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2023/08/how-to-do-unit-testing-for-the-core-api-project-with-repository/feed/ 8
Use the Serilog in .Net Core https://www.coderblog.in/2023/07/use-the-serilog-in-net-core/ https://www.coderblog.in/2023/07/use-the-serilog-in-net-core/#comments Fri, 28 Jul 2023 04:10:36 +0000 https://www.coderblog.in/?p=872 This is post 5 of 8 in the series “Create a website with ASP.NET Core and Angular” In

<p>The post Use the Serilog in .Net Core first appeared on Coder Blog.</p>

]]>

This is post 5 of 8 in the series “Create a website with ASP.NET Core and Angular”

In this series, I will show you how to create the web API with .Net core and communicate with Angular in frontend, and I will keep to update it!

  1. How to setup VS Code environment for ASP.NET Core Project
  2. Use code first to connect database in .Net Core API
  3. Use the Repository pattern in .Net core
  4. Create .Net Core Api controller for CRUD with repository
  5. Use the Serilog in .Net Core
  6. Create Angular Project and Communicate with .Net Core
  7. Create Paging Data from .Net Core Api
  8. How to do Unit Testing for the Core Api Project with Repository

In the previous article, I introduced how to create Api Controller for CURD. In this time, we will take a look how to use the Serilog in the .Net core!

1. Serilog Introduction

1.1 What is Serilog?

Serilog is a popular logging framework for .NET applications. Here is a quick introduction to Serilog:

  1. Serilog is a structured logging library that allows writing log messages in JSON format. This makes the logs easy to read by machines/computers.

  2. It provides a flexible logging API that allows configuring and integrating with different storage backends like files, databases, etc.

  3. Serilog provides useful enrichers to add useful info like timestamps, thread ids, etc to log messages.

  4. It has support for structured logging, which means logging objects directly instead of just text messages. This provides more useful logging info.

1.2. Why Serilog?

  1. Structured logging – Serilog logs are structured as JSON which makes them easy to consume. This is useful for log aggregation/analysis.

  2. Multiple sinks – Serilog supports writing logs to multiple sinks like file, database, Azure services etc. This makes it flexible to route and store logs.

  3. Enrichment – Serilog provides enrichers to augment log events with useful info like timestamps, thread ids, machine names etc.

  4. Strongly typed – Serilog is strongly typed, so you get compile time safety while logging events and objects.

  5. Customizable and extensible – Serilog is highly customizable via configuration. You can write custom sinks and enrichers.

  6. Framework independent – Serilog can be used outside ASP.NET too, for example in console apps, services etc.

  7. Decoupled architecture – The logging pipeline is decoupled from the logging API surface. This makes it flexible.

  8. Structured logging – Serilog supports structured logging i.e. logging objects rather than just text messages.

  9. High performance – Serilog is optimized for performance and allocates less objects.

  10. Popular and mature – Serilog is very popular, well-supported and mature logging library.

2. Using Serilog

2.1 Install

We can easy to install Serilog from NuGet, for good to use it, I will recommend to install below packages

Serilog.AspNetCore
Serilog.Settings.Configuration
Serilog.Sinks.MSSqlServer   
Serilog.Filters.Expressions

if you don’t want to save log to database, then don’t need to install Serilog.Sinks.MSSqlServer, just install the above to the Api project.

2.2. Setup Serilog

There are two ways for setup the Serilog, but for now, I will show you how to setup in Program.cs first

Add the below codes in Program.cs, this is the base setting, it will print all of the logs in console

builder.Host.UseSerilog((context, logConfig) => logConfig
        .ReadFrom.Configuration(context.Configuration)        
        .WriteTo.Console());

But, since Serilog is so powerful, of course, We will not satisfy these basic usage! There are some requirements what I want to do:

1) We want to split the debug and error log to difference log file. So we can use the log level for filter

//for debug log
.WriteTo.Logger(l => l.Filter.ByIncludingOnly(e => e.Level == LogEventLevel.Warning))

//for error log
.WriteTo.Logger(l => l.Filter.ByIncludingOnly(e => e.Level == LogEventLevel.Error))

There are 6 logging levels can be use:

a) Verbose – Anything and everything you might want to know about a running block of code. For the most detailed logs. Useful for troubleshooting.
b) Debug – Internal system events that aren’t necessarily observable from the outside. For debugging information. Logs that are useful during development.
c) Information – The lifeblood of operational intelligence – things happen. For tracking general flow of the application. Usually high volume logs.
d) Warning – Service is degraded or endangered. For logs that indicate a potential problem. But the application can continue running.
e) Error – Functionality is unavailable, invariants are broken or data is lost. For exceptions or errors that require investigation.
f) Fatal – If you have a pager, it goes off when one of these occurs. For severe errors that cause application crash or failure.

The logging levels in Serilog have an order of precedence. Each level includes all the levels above it. So logs written at Error level will also include Fatal, Warning, Information etc.

2) Serilog support structure logging, so I want to write the log to a json file, so that we can trace more details information.

.WriteTo.File(new JsonFormatter(), "./logs/debug-logs.json")

for save to the json format, we need to use JsonFormatter under Serilog.Formatting.Json namespace

3) We also want to group all of the log by day, that’s mean there is one log file per day, so update the above code as below

.WriteTo.File(new JsonFormatter(), "./logs/debug-logs-.json",
        rollingInterval: RollingInterval.Day)

use the rollingInterval for set the interval, and the RollingInterval support below

a) Infinite: The log file will never roll; no time period information will be appended to the log file name.
b) Year: Roll every year. Filenames will have a four-digit year appended in the pattern: yyyy
c) Month: Roll every calendar month. Filenames will have yyyMM appended
e) Day: Roll every day. Filenames will have yyyyMMdd appended
f) Hour: Roll every hour. Filenames will have yyyyMMddHH appended
g) Minute: Roll every minute. Filenames will have yyyyMMddHHmm appended

4) In the end, show all of the debug message in console. we can use the restrictedToMinimumLevel in console

.WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Debug)

Ok, we combine all together, so the complete codes should be like below

builder.Host.UseSerilog((context, logConfig) => logConfig
    .ReadFrom.Configuration(context.Configuration)
    .WriteTo.Logger(l => l.Filter.ByIncludingOnly(e => e.Level == LogEventLevel.Warning)
            .WriteTo.File(new JsonFormatter(), "./logs/debug-logs-.json",
        rollingInterval: RollingInterval.Day)
            )
    .WriteTo.Logger(l => l.Filter.ByIncludingOnly(e => e.Level == LogEventLevel.Error)
            .WriteTo.File(new JsonFormatter(), "./logs/error-logs-.json",
        rollingInterval: RollingInterval.Day)
            )
    .WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Debug)
);

The setup is done, and then we can use it in our controller.

2.3. Using Serilog in controller

Add the below in UserController

protected readonly ILogger<User> _logger;

public UserController(IUserRepository userRepository, ILogger<User> logger)
{
    this._userRepository = userRepository;
    this._logger = logger;
}

and we can call the _logger for write log in anywhere, for example, we write the debug log when get all users, so add the code in GetUsers() method

[HttpGet("users")]
public async Task<ActionResult<IEnumerable<User>>> GetUsers()
{
     ...

    _logger.LogDebug("LogDebug ================  GetUsers ");

    ...
}

That’s ok, let’s try to have a look the result! We can call the GetUsers() from Swagger UI, and you will find the below debug log file has been generated and also show the log in console

I have format the json file, so looks good now, but there is a problem that you will also find there are a lot of other debug messages that what we want to care about, can we just write the messages what we write in the controller?

Yes, we can! We can use the Serilog.Filters.Expressions library to filter only the messages what we want. For this case, we can find there are some properties in the log

{
    "Timestamp": "2023-07-28T10:25:12.9690530+08:00",
    "Level": "Debug",
    "MessageTemplate": "LogDebug ================  GetUsers ",
    "Properties": {
        "SourceContext": "MyDemo.Core.Data.Entity.User",
        "ActionId": "56d17f70-be16-4491-a7fe-8832462da7f8",
        "ActionName": "MyDemo.Controllers.UserController.GetUsers (MyDemo)",
        "RequestId": "0HMSF27B8QJ5M:00000004",
        "RequestPath": "/api/users",
        "ConnectionId": "0HMSF27B8QJ5M"
    }
}

so we can filter these properties to identify which log we need, for example we can filter the SourceContext start with MyDemo, that will make sure only show the logs from our project

//create a filter query, the query syntax like the SQL 
var filterExpr = "@Properties['SourceContext'] like 'MyDemo%'";

//use the filter 
builder.Host.UseSerilog((context, logConfig) => logConfig
    .ReadFrom.Configuration(context.Configuration)
    .MinimumLevel.Debug()
    .Filter.ByIncludingOnly(filterExpr)  //use the filter here
    .WriteTo.Logger(l => l.Filter.ByIncludingOnly(e => e.Level == LogEventLevel.Debug)
            .WriteTo.File(new JsonFormatter(), "./logs/debug-logs-.json",
        rollingInterval: RollingInterval.Day)
            )
);

after that, we can find that only the message what we write in the log file

And we try to throw an exception to see whether will generate the error log file

public async Task<ActionResult<IEnumerable<User>>> GetUsers()
{
    var apiResult = new ApiResult<IEnumerable<User>>();
    _logger.LogDebug("LogDebug ================  GetUsers ");
    try
    {
        apiResult.Data = await _userRepository.GetAllAsync();

        //throw an exception 
        throw new Exception("Testing error");

        return Ok(apiResult);
    }
    catch (Exception ex)
    {
        //log the error 
        _logger.LogError(ex, "GetUsers Exception");

        apiResult.Success = false;
        apiResult.Message = ex.Message;
        return StatusCode(500, apiResult);
    }
}

after that, we can find the error log as below

2.4. Config Serilog in appsettings.json

The other way for using Serilog is config within appsettings.json. As you know, appsettings.json is the .Net core common setting file, we put the Serilog config here will be better for maintain, we can change the settings in runtime and don’t need to publish the project again. Ok, let’s do it!

1) First, we can add the simple and base config

"Serilog": {
  "MinimumLevel": "Debug",
  "WriteTo": [
    {
      "Name": "Console",
      "Args": {
        "outputTemplate": "===> {Timestamp:HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}"
      }
    }
  ]
}

this config only use the Serilog and write log in console with the specify format, and we need to add more functions

2) Create a debug log file and only show the message for MyDemo project, we need to add the using library for sinks and filters

"Serilog": {
    "Using": [ "Serilog.Sinks.File", "Serilog.Filters.Expressions" ],
     "MinimumLevel": "Debug",
     ...
}

add the name of Logger (this is the fixed name, equal .WriteTo.Logger in program) and set the filter, in the end, set where should write the log file and formatting:

{
  "Name": "Logger",
  "Args": {
    "configureLogger": {
      "Filter": [
        {
          "Name": "ByIncludingOnly",
          "Args": {
            "expression": "@Level = 'Debug' and @Properties['SourceContext'] like 'MyDemo%' " 
          }
        }
      ],
      "WriteTo": [
        {
          "Name": "File",
          "Args": {
            "path": "./logs/debug-.log",
            "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Level:u3}] {Message:lj}{NewLine}",
            "rollingInterval": "Day"
          }
        }
      ]
    }
  }
}

the above config is exactly the same with the settings in Program.cs

1) Create another config for error log, that will be similar with above

{
  "Name": "Logger",
  "Args": {
    "configureLogger": {
      "Filter": [
        {
          "Name": "ByIncludingOnly",
          "Args": {
            "expression": "@Level = 'Error'"
          }
        }
      ],
      "WriteTo": [
        {
          "Name": "File",
          "Args": {
            "path": "./logs/error-.log",
            "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Level:u3}] {Message:lj}{NewLine}{Exception}",
            "rollingInterval": "Day"
          }
        }
      ]
    }
  }
}

with above config, you will find generate a simple *.log file only with a message what you write in controller and there is no other addition information with json format, if you want the full json format, you can add the formatter and remove the outputTemplate

"WriteTo": [
  {
    "Name": "File",
    "Args": {
      "path": "./logs/debug-.json",
      "rollingInterval": "Day",
      "formatter": "Serilog.Formatting.Json.JsonFormatter, Serilog"
    }
  }
]

after that, we also need to update the Program.cs , remove the other Serilog settings, we only need to register Serilog here

builder.Host.UseSerilog((context, logConfig) => logConfig   
        .ReadFrom.Configuration(context.Configuration));

2.5. Log the Database SQL Statement

Sometime we also want to know what’s SQL statements have been executed, this is very helpful for tracing the data issue. We still use the appsettings.json for setup it, it’s need to add more more Logger item

actually, that’s very easy to do, just need to change the filter expression will be ok, if you want to log the SQL statements, just filter the properties event name with Microsoft.EntityFrameworkCore.Database.Command.CommandExecuting, so the config item should be as below

{
  "Name": "Logger", 
  "Args": {
    "configureLogger": {
      "Filter": [
        {
          "Name": "ByIncludingOnly",
          "Args": {
            "expression": "@Properties['EventId']['Name'] = 'Microsoft.EntityFrameworkCore.Database.Command.CommandExecuting'" 
          }
        }
      ],
      "WriteTo": [
        {
          "Name": "File",
          "Args": {
            "path": "./logs/sql-log-.sql",
            "outputTemplate": "--[{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Level:u3}]{NewLine}--{Message:lj}{NewLine}{NewLine}",
            "rollingInterval": "Day"
          }
        }
      ]
    }
  }
}

after that, try to get users and create an user, you will find there is more than a sql log file as below

3. Summarize

We have introduce Serilog with details, you can setup it by coding in Program.cs file, or config it in the appsettings.json. For easy maintain I will suggest to set the config in appsettings.json. And we tried to create 3 difference log files with the Serilog filter function, that’s very powerful and helpful for create difference logs what you need!

We have done a very base Asp.Net Core Api project with these articles, so in the next time, I will introduce how to create the client side project with Angular 🙂

Loading

<p>The post Use the Serilog in .Net Core first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2023/07/use-the-serilog-in-net-core/feed/ 7
Create .Net Core Api controller for CRUD with repository https://www.coderblog.in/2023/07/create-net-core-api-controller-for-crud-with-repository/ https://www.coderblog.in/2023/07/create-net-core-api-controller-for-crud-with-repository/#comments Thu, 27 Jul 2023 02:00:04 +0000 https://www.coderblog.in/?p=855 This is post 4 of 8 in the series “Create a website with ASP.NET Core and Angular” In

<p>The post Create .Net Core Api controller for CRUD with repository first appeared on Coder Blog.</p>

]]>

This is post 4 of 8 in the series “Create a website with ASP.NET Core and Angular”

In this series, I will show you how to create the web API with .Net core and communicate with Angular in frontend, and I will keep to update it!

  1. How to setup VS Code environment for ASP.NET Core Project
  2. Use code first to connect database in .Net Core API
  3. Use the Repository pattern in .Net core
  4. Create .Net Core Api controller for CRUD with repository
  5. Use the Serilog in .Net Core
  6. Create Angular Project and Communicate with .Net Core
  7. Create Paging Data from .Net Core Api
  8. How to do Unit Testing for the Core Api Project with Repository

In the previous article, I introduced how to use the repository pattern, at this time, I will show you how to use for CURD with the Api controller.


We need to create the controller for implement the RESTful Api, so first of all, we need to know what’s that!

1. What’s RESTful Api

REST stands for Representational State Transfer, and it is an architectural style for building web services. A RESTful API is a web service that follows the principles of REST.

A RESTful API is characterized by the following properties:

  1. Client-server architecture: Separation of concerns between the client and the server.

  2. Stateless: Each request from a client to the server contains all the information necessary for the server to understand the request. The server does not store any client context between requests.

  3. Cacheable: Clients can cache responses to improve performance.

  4. Uniform interface: A set of well-defined HTTP methods (GET, POST, PUT, DELETE, etc.) is used to interact with resources, and resources are identified by URIs.

  5. Layered system: A client can’t tell whether it is connected directly to the end server or communicating through an intermediary, such as a load balancer.

  6. Code on demand (optional): Servers can transfer executable code to clients, such as JavaScript, to be executed in the client’s context.

In practice, a RESTful API typically provides a set of endpoints that clients can use to interact with the resources exposed by the API. For example, a RESTful API for a blog might provide endpoints for creating, updating, and deleting blog posts, and for retrieving a list of blog posts or a single blog post by ID.

2. Create the Api Controller

2.1. What’s Route

Route is an attribute that can be applied to an action method in a controller to specify the URL pattern that the action method should respond to. In other words, it defines the route template for the action method.

The Route attribute can be also used to specify the route template directly on an action method, like this:

[HttpGet("users/{id}")]
public IActionResult GetUser(int id)
{
    // ...
}

In this example, the HttpGet attribute specifies that the action method should handle HTTP GET requests, and the users/{id} URL pattern specifies that the method should handle requests for URLs that match the /users/{id} pattern, where {id} is a placeholder for the ID of the user being requested.

Alternatively, you can specify the route template at the controller level using the Route attribute on the controller class, and then specify additional path segments for individual actions using the HttpGet, HttpPost, etc. attributes. Here’s an example:

[Route("api/[controller]")]
public class UserController : ControllerBase
{
    [HttpGet("{id}")]
    public IActionResult GetUser(int id)
    {
        // ...
    }

    [HttpPost]
    public IActionResult CreateUser([FromBody] User user)
    {
        // ...
    }
}

In this example, the Route attribute specifies that all action methods in the UserController should be mapped under the /api/users URL path. The HttpGet and HttpPost attributes specify the additional path segments and HTTP methods for the GetUser and CreateUser methods, respectively.

2.2 Create the Api Methods

Ok, let’s create out controller and methods. As the previous article mention, we need to create an ApiResult object for handle return data. After that, we can use C# Extensions to help to create an UserController

We need to update the constructor for setup(inject) the Repository object

//  MyDemo/Controllers/UserController.cs

[ApiController]
[Route("api")]
public class UserController : ControllerBase
{
    private readonly IUserRepository _userRepository;

    public UserController(IUserRepository userRepository)
    {
        this._userRepository = userRepository;
    }
}

For better user friendly the Api Url, I just set the controller router attribute to api, that’s mean don’t need to add the controller name in the api url.

Before start to create the Api method, I want to enhance the previous codes for register Repository service in Program.cs. We just use below

builder.Services.AddScoped<IUserRepository, UserRepository>();

for register the service, but there is a problem here, if there are multiple Repositories need to be register, then we need to add many of above for each one, to avoid not many codes in Program and try to let it clear, we can create another service expansion method for handle this:

// MyDemo.Core/Service/RepositoryService.cs

public static class RepositoryService
{
    public static IServiceCollection AddRepositories(this IServiceCollection services)
    {
        services.AddScoped<IUserRepository, UserRepository>();
        return services;
    }
}

and just call it in Program.cs

builder.Services.AddRepositories();

of course, you also need to add more repositories into this service when you need, but just can keep to program.cs clear and easy to maintain.

The first Api method is GetUsers, that’s mean we need to get more than one users, and use the HttpGet attribute for specify this is a Get method

/// <summary>
/// Get all users
/// Api Url: http://website-domain/api/users  (without controller name)
/// </summary>
/// <returns></returns>
[HttpGet("users")]
public async Task<ActionResult<IEnumerable<User>>> GetUsers()
{
    var apiResult = new ApiResult<IEnumerable<User>>();
    try
    {
        apiResult.Data = await _userRepository.GetAllAsync();
        return Ok(apiResult);
    }
    catch (Exception ex)
    {
        apiResult.Success = false;
        apiResult.Message = ex.Message;
        return StatusCode(500, apiResult);
    }
}

Create a Get method GetUser for get single user

 /// <summary>
/// Get user by id
/// Api Url: http://website-domain/api/user/id  (without controller name)
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet("user/{id}")]
public async Task<ActionResult<User>> GetUser(int id)
{
    var apiResult = new ApiResult<User>();
    try
    {
        apiResult.Data = await _userRepository.GetItemWithConditionAsync(u => u.Id == id);
        return Ok(apiResult);
    }
    catch (Exception ex)
    {
        apiResult.Success = false;
        apiResult.Message = ex.Message;
        return StatusCode(500, apiResult);
    }
}

Create a Post method PostUser for create user, we need to check the user name whether exist before create the new one

/// <summary>
/// Create a user
/// Api Url: http://website-domain/api/user  (without controller name)
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
[HttpPost("user")]
public async Task<ActionResult<User>> PostUser(User user)
{
    var apiResult = new ApiResult<Object>();
    try
    {
        var isExist = await _userRepository.UserIsExist(user.Name);
        if (isExist)
        {
            apiResult.Success = false;
            apiResult.Message = "The user has already exists!";
        }
        else
        {
            _userRepository.Create(user);
            await _userRepository.SaveAsync();
        }
        return Ok(apiResult);
    }
    catch (Exception ex)
    {
        apiResult.Success = false;
        apiResult.Message = ex.Message;
        return StatusCode(500, apiResult);
    }
}    

Create a Put method PutUser for update user

/// <summary>
/// Update the user
/// Api Url: http://website-domain/api/user  (without controller name)
/// </summary>
/// <param name="id"></param>
/// <param name="user"></param>
/// <returns></returns>
[HttpPut("user")]
public async Task<ActionResult<User>> PutUser(User user)
{
    var apiResult = new ApiResult<Object>();
    //if the id is 0, then don't allow to update it
    if (user.Id == 0)
    {
        return BadRequest();
    }
    try
    {
        //get the current DB user
        var dbUser = await _userRepository.GetItemWithConditionAsync(u => u.Id == user.Id);
        if (dbUser == null)
        {
            apiResult.Success = false;
            apiResult.Message = "Can't found the user!";
        }
        else
        {
            //update the user
            _userRepository.Update(user);
            await _userRepository.SaveAsync();
        }
        return Ok(apiResult);
    }
    catch (Exception ex)
    {
        apiResult.Success = false;
        apiResult.Message = ex.Message;
        return StatusCode(500, apiResult);
    }
}

In the end, create the Delete method DeleteUser for delete the user by id

/// <summary>
/// Delete user
/// Api Url: http://website-domain/api/user/id  (without controller name)
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpDelete("user/{id}")]
public async Task<IActionResult> DeleteUser(int id)
{
    var apiResult = new ApiResult<Object>();
    try
    {
        var dbUser = await _userRepository.GetItemWithConditionAsync(u => u.Id == id);
        if (dbUser == null)
        {
            apiResult.Success = false;
            apiResult.Message = "Can't found the user!";
        }
        else
        {
            //delete the user
            _userRepository.Delete(dbUser);
            await _userRepository.SaveAsync();
        }
        return Ok(apiResult);
    }
    catch (Exception ex)
    {
        apiResult.Success = false;
        apiResult.Message = ex.Message;
        return StatusCode(500, apiResult);
    }
}

Ok, for now, we have finished the CRUD methods for RESTful API, let’s try them through swagger. There are 5 APIs show in the swagger page:

First, we try to create a user with POST method. Pass the below data to API

It will return the below information, the success flag is true and there is no error message, that’s mean the record has been created!

we can check the database

That’s cool! Ok, let try to get the data

and we got the data with our AipResult object format.

well, I try to create more data as below for test get user by id

and try to get the id = 2 items

That’s great, it’s working! The last testing to try to delete an user by id

and check the database data

Everything is fine! 🙂

3. Summarize

We talked about what’s RESTful API and how to create it. But there is one thing we still need to improve, for the complete system, we should need to handle the event/error logs, this can help us to trace the error or some outstanding issue, so in the next, we will discus how to do it! :smirk:

Loading

<p>The post Create .Net Core Api controller for CRUD with repository first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2023/07/create-net-core-api-controller-for-crud-with-repository/feed/ 7
Use the Repository pattern in .Net core https://www.coderblog.in/2023/07/use-the-repository-pattern-in-net-core/ https://www.coderblog.in/2023/07/use-the-repository-pattern-in-net-core/#comments Tue, 25 Jul 2023 03:02:24 +0000 https://www.coderblog.in/?p=835 This is post 3 of 8 in the series “Create a website with ASP.NET Core and Angular” In

<p>The post Use the Repository pattern in .Net core first appeared on Coder Blog.</p>

]]>

This is post 3 of 8 in the series “Create a website with ASP.NET Core and Angular”

In this series, I will show you how to create the web API with .Net core and communicate with Angular in frontend, and I will keep to update it!

  1. How to setup VS Code environment for ASP.NET Core Project
  2. Use code first to connect database in .Net Core API
  3. Use the Repository pattern in .Net core
  4. Create .Net Core Api controller for CRUD with repository
  5. Use the Serilog in .Net Core
  6. Create Angular Project and Communicate with .Net Core
  7. Create Paging Data from .Net Core Api
  8. How to do Unit Testing for the Core Api Project with Repository

In this post, I will talking about the Repository pattern and why should we use it.


1. Introduction

The Repository design pattern is a widely-used software design pattern that provides an abstraction layer between the business logic and the persistence layer of an application. It is used to manage the persistence of objects in a database or other data store. It separates the logic that retrieves the data and maps it to the domain model from the business logic that acts on the model. This separation of concerns allows for a more modular and flexible architecture that is easier to maintain and test.

The Repository pattern typically involves defining a set of interfaces for data access operations, such as create, read, update, and delete (CRUD) operations. These interfaces are then implemented by concrete repository classes that interact with the underlying data store.

By abstracting away the details of the data store, the Repository pattern allows the business logic to interact with the data using a uniform interface, regardless of the specific implementation of the data store. This makes it easier to switch between different data stores or to change the schema of the data store without affecting the rest of the application.

And Repository pattern is commonly used in object-oriented programming, particularly in applications that use an object-relational mapping (ORM) framework to interact with a database. However, it can also be used in other contexts, such as web services or file-based data stores.

Ok, let’s starting to try it!

2. Create Repository

From the last article, we have created an User model and connect to database, so we can create the Repository now.

For separate the logic between data access and business, it will use interface in Repository pattern, so we will create the below interface for User model, it should include the base CRUD operations.

Create a folder name Repositories under MyDemo.Core, and create an Interfaces folder

//  MyDemo.Core/Repositories/Interfaces/IUserRepository.cs

public interface IUserRepository
{
    IQueryable<User> GetAll();
    IQueryable<User> GetWithCondition(Expression<Func<User, bool>> expression);
    void Create(User entity);
    void Update(User entity);
    void Delete(User entity);
}

And then we can create the UserRepository.cs file to implement the interface

//  MyDemo.Core/Repositories/UserRepository.cs

public class UserRepository : IUserRepository
{
    protected ApplicationDbContext _context { get; set; }
    public UserRepository(ApplicationDbContext context)
    {
        _context = context;
    }
    public IQueryable<User> GetAll() => _context.Set<User>().AsNoTracking();
    public IQueryable<User> GetWithCondition(Expression<Func<User, bool>> expression) => 
        _context.Set<User>().Where(expression).AsNoTracking();
    public void Create(User entity) => _context.Set<User>().Add(entity);
    public void Update(User entity) => _context.Set<User>().Update(entity);
    public void Delete(User entity) => _context.Set<User>().Remove(entity);
}

The above codes will run well, but there is a problem in this way, what if there are many modes need to be implement the pattern? and we need to copy these almost same codes again and again? and if we want to change a method, we also need to update many of modes! So the other better way is use the Base Class for avoid the duplicate coding.

We can create an IBaseRepository for handle the common CRUD methods

//  MyDemo.Core/Repositories/Interfaces/IBaseRepository.cs

public interface IBaseRepository<T>
{
    IQueryable<T> GetAll();
    IQueryable<T> GetWithCondition(Expression<Func<T, bool>> expression);
    void Create(T entity);
    void Update(T entity);
    void Delete(T entity);
}

As you can see, we use the generic in the base interface, so that can be handle difference models, and we need to implement it :

//  MyDemo.Core/Repositories/BaseRepository.cs

public class BaseRepository<T> : IBaseRepository<T> where T : class
{
    protected ApplicationDbContext _context { get; set; }
    public BaseRepository(ApplicationDbContext context)
    {
        _context = context;
    }
    public IQueryable<T> GetAll() => _context.Set<T>().AsNoTracking();
    public IQueryable<T> GetWithCondition(Expression<Func<T, bool>> expression) => 
        _context.Set<T>().Where(expression).AsNoTracking();
    public void Create(T entity) => _context.Set<T>().Add(entity);
    public void Update(T entity) => _context.Set<T>().Update(entity);
    public void Delete(T entity) => _context.Set<T>().Remove(entity);
}

Ok, then we need to update the user repository interface

public interface IUserRepository : IBaseRepository<User>
{

}

In the end, we also need to update the implementation

public class UserRepository : BaseRepository<User>, IUserRepository
{
    public UserRepository(ApplicationDbContext context) : base(context) {            
    }
}

Just so simple, right?

We only need to inherit the base repository and will be ok! But please hold on, why we need to create the empty repository class? Why not just use one base class for handle all of the models?

There are two reasons for do that:

  1. We need to identity what model will be use and pass the instance class to the base class, although you also can pass the instance to base class directly, but we suggest create a model class and that will be easy to maintain

  2. Another important reason is maybe there will be other custom methods in a model. For example handle the user access right, this should be just for user model, it’s not a common CRUD method, so we need an user model for handle it, we can add more custom methods

3. Using the Async Programming

3.1. What’s Async Programming

In traditional synchronous programming, each operation is executed sequentially, one after the other. This means that if a long-running operation is encountered, the program will be blocked until the operation completes, which can lead to unresponsive or slow applications. Async programming is a programming paradigm that allows for non-blocking, asynchronous execution of code, enabling programs to perform multiple tasks concurrently without blocking the main thread or waiting for long-running operations to complete.

With async programming, long-running operations are executed in the background, allowing the program to continue executing other operations without waiting for the long-running operation to complete. This is achieved using asynchronous functions and callbacks, which enable non-blocking execution of code.

Async programming is particularly useful in scenarios where programs need to perform I/O operations, such as reading and writing to a file or making network requests. These operations can take a long time to complete, during which the program would be blocked in synchronous programming. With async programming, the program can continue executing other operations while the I/O operation is being performed in the background.

By using async programming, we can avoid performance bottlenecks and enhance the responsiveness of our application. So let’s try to use it!

3.2. Create Async Method

We can create the two async method in base interface

//  MyDemo.Core/Repositories/Interfaces/IBaseRepository.cs

Task<IEnumerable<T>> GetAllAsync();
Task<IEnumerable<T>> GetWithConditionAsync(Expression<Func<T, bool>> expression);
Task<T?> GetItemWithConditionAsync(Expression<Func<T, bool>> expression);

and implement it

//  MyDemo.Core/Repositories/BaseRepository.cs

public async Task<IEnumerable<T>> GetAllAsync() =>  await GetAll().ToListAsync();

public async Task<IEnumerable<T>> GetWithConditionAsync(Expression<Func<T, bool>> expression) =>  
        await GetWithCondition(expression).ToListAsync();

4. Using the Codes

Let’s create an API controller to show how to use the repository. As we need to handle the API result, so it should create a common result object for return it fo client will be better.

4.1. Create Api Result Object

First of all, the API should return the status for determine the process success or not, and then should be include the message for describe the result, in the end we need a data object for return the query data from database, so there are at least 3 fields in the ApiResult object:

//  MyDemo.Core/Data/ApiResult.cs

public class ApiResult<T>
{
    public ApiResult()
    {
        this.Success = true;
    }
    /// <summary>
    /// TRUE if the Api attempt is successful, FALSE otherwise.
    /// </summary>
    public bool Success { get; set; }
    /// <summary>
    /// Api return result message
    /// </summary>
    public string Message { get; set; } = null!;
    /// <summary>
    /// Return the data for Api
    /// </summary>
    public T? Data { get; set; }
}

We use the generic for handle any of models.

4.2. Cerate Api Controller

Create an user Api controller, we can pass the repository object through constructor

[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
    private readonly IUserRepository _userRepository;

    public UserController(IUserRepository userRepository)
    {
        this._userRepository = userRepository;
    }
}

And then create a Get method for get user data

[HttpGet("users")]
public async Task<ActionResult<IEnumerable<User>>> GetUsers()
{
    var apiResult = new ApiResult<IEnumerable<User>>();
    try
    {
        //here is how to use the repository method
        apiResult.Data = await _userRepository.GetAllAsync();
        //return the result to client side
        return Ok(apiResult);
    }
    catch (Exception ex)
    {
        //handle the exception
        apiResult.Success = false;
        apiResult.Message = ex.Message;
        return StatusCode(500, apiResult);
    }
}

The above Api controller sample only for demo how to use the Repository, so it’s simple and not complete, I will show you next time for how to create CRUD in the Api controller!

The last import thing, you need to register the repository to service before you use it, add below in program.cs

builder.Services.AddScoped<IUserRepository, UserRepository>();

5. Summarize

We now know what’s Repository pattern and how to create it, and we should use the base class for avoid the duplicate codes, also, we know how to create the Async method, that can be help to improve the performance. In the end, we also created an Api controller to try to get the data with Repository.

That’s it, please let me know if you still have any problems 🙂

Loading

<p>The post Use the Repository pattern in .Net core first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2023/07/use-the-repository-pattern-in-net-core/feed/ 8
Use code first to connect database in .Net Core API https://www.coderblog.in/2023/07/use-code-first-to-connect-database-in-net-core-api/ https://www.coderblog.in/2023/07/use-code-first-to-connect-database-in-net-core-api/#comments Fri, 21 Jul 2023 12:49:37 +0000 https://www.coderblog.in/?p=809 This is post 2 of 8 in the series “Create a website with ASP.NET Core and Angular” In

<p>The post Use code first to connect database in .Net Core API first appeared on Coder Blog.</p>

]]>

This is post 2 of 8 in the series “Create a website with ASP.NET Core and Angular”

In this series, I will show you how to create the web API with .Net core and communicate with Angular in frontend, and I will keep to update it!

  1. How to setup VS Code environment for ASP.NET Core Project
  2. Use code first to connect database in .Net Core API
  3. Use the Repository pattern in .Net core
  4. Create .Net Core Api controller for CRUD with repository
  5. Use the Serilog in .Net Core
  6. Create Angular Project and Communicate with .Net Core
  7. Create Paging Data from .Net Core Api
  8. How to do Unit Testing for the Core Api Project with Repository

In the previous article, I have explained how to use VS Code to setup an ASP.NET Core development environment. Now, let’s talking about how to build a database connection project! Here I have to say again that it is so comfortable to use VS CODE for development! 🙂 OK, without further ado, let’s do it!

1 Create the API project

1.1 Create Folder

Create a folder named Demo, then open it in VS Code (I usually drag the directory into VS Code) and create a folder named MyDemo.API . You can open MyDemo.API in VS Code, or drag it directly into VS CODE.

1.2 Create Solution

Click the vscode solution button on the left sidebar to open the solution panel, and then click Create New Solution to create a new solution called MyDemo.

1.3 Create Project

Right click on the solution file, select add new project, select asp.net core api, select c#, and then enter the project name, here we use MyDemo, and then you will be asked to enter the project folder name, as long as it is the same as the project The name is the same. After completion, a complete ASP.NET Core project will be created.

1.4 Run the Project

Click the Run and Debug button, it will auto create the related debug config files, click the Yes when you see the popup:

1.5 Startup the Swagger UI

By default, it will use the SSL url for startup the debug website, you need to use another Non SSL url and use the Swagger UI so that you can see the website page, for my below sample, it need to use

http://localhost:5171/Swagger

1.6 CORS Issue

After that, you may encounter the problem of CORS cross-site error as below

You can find more details for CORS error. To fix this issue, we need to add the following code in the program.cs file.

//allow cross-origin access for the api
builder.Services.AddCors(o => o.AddPolicy("AllowCrosite", policy =>
{
    policy.AllowAnyHeader()
        .AllowAnyMethod()
        .SetIsOriginAllowed(origin => true) // allow any origin
        .AllowCredentials();
}));

and below code after create the app

app.UseCors("AllowCrosite");

1.7 Create other help Projects

Now that you have established a basic API website, the next step we need the data connection project for help to process ddata.

Here we first talk about the architecture of the API project. Because this example is intended to be clear and simple, so the project hierarchy will not be too complicated. We just need to create another project for CORE and UTILITY. Among them, the CORE project for the base data MODEL and related operation logic, while the UTILITY project is some general functional components. This structure is simple and clear, and it is also suitable for general small and medium-sized projects.

Since these two projects are only the class libraries, when using vscode solution to create, just select Class Library. At the same time, in order to maintain the overall project style and format, MyDemo is added before each project in the naming, so the two project names are MyDemo.Core and MyDemo.Utility respectively.

The folder structure will looks like below

2 The Entity Framework

We will use Entity Framework as the ORM framework for data manipulation

Here is a brief talk about the way that EF creates data operations. There are two main ways, one is DB First and the other is Code First.

2.1 introduce

2.1.1 DB First

DB First is a more traditional way, that is, first design the overall database structure, and then directly create each data table in the database, and then map it through EF, and automatically create the entity category corresponding to each table in the project; this kind of The general way needs to prepare the SQL statements for table creation. The advantage is that some more complex structures can also be done with SQL. Since all SQL has been designed at the beginning, it will be clear, but The disadvantage is that it is very inconvenient to support multiple databases at the same project. It is necessary to write various types of SQL statements, and it is also a troublesome thing to migrate to a new database.

2.1.2 Code First

Another way is Code First. As the name implies, it is to write the program code first, and then generate the data table to database. The advantage of this method is that you only need to concentrate on designing the code of each entity type without writing any SQL statements, so it is very convenient to support multiple databases, and the framework will automatically generate different databases SQL statements, and what is even more surprising is that you don’t even need to create a database file, just set up the corresponding database server connection, and the framework will automatically create the entire database and related tables. This is very helpful for the operation of migrating the database. You only need a complete set of code, and it doesn’t matter where you put it to run!

In order to facilitate the migration of data, I don’t want to write so many SQL statements, so this project will use the Code First.

3. Prepare Code First

3.1 Setup the references

The newly created CORE and Utility projects here serve the API, so add references to these two in the API project.(You can also use the DCE extension for do that)

3.1.2 Create Data folder

In the MyDemo.Core project, create a Data directory, which will store all entities and related code first codes.

3.1.3 Add the EF packages

To use EF, you need to install the following packages first. We can install them directly through NuGet Gallery extension, but it should be noted that the packages need to be installed in both MyDemo.Core and MyDemo.API projects.

Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Sqlite
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools

Because I want to handle multiple database, so I will install Sqlite and SqlServer for the demo

3.1.4 Setup DB connection

The next step is to add the database connection string to the MyDemo.API\appsettings.json file. You can also put multiple database connections in here

 "ConnectionStrings": {
    "Mssql": "Server=.\\SQLEXPRESS;Database=MyDemo;User Id=sa;Password=password;Integrated Security=False;MultipleActiveResultSets=True;TrustServerCertificate=True",
    "Sqlite": "Data Source=DB\\MyDemo.db",
    "Mysql": "server=localhost;userid=root;password=yourpass;database=accountowner;"
  }

4. Startup the Code First

Now we add the code first support for entity class project:

4.1 Create an entity

Create an Entity directory under the Data directory, and then create an User.cs entity under this directory.

Tip: For now, we have already created the projects and solution, so don’t need to use the DCE tool, we can go back to file explorer so that we can use C# Extensions to help to create other c# files, it will provide the nice file templates for do that. For example we create an User.cs as below:

After that, it will create a nice User class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace MyDemo.Core.Data.Entity
{
    public class User
    {

    }
}

4.2 Table Structure:

4.2.1 Table design

Let me explain the design ideas about the tables:

Assuming that we want to create a simple user now, the attributes to be included user ID/name/password and email, which are enough for basic information, but in a normal project, there are some hidden information need to be processed, such sa user status, create and update date time, deactivation of the record, the administrator may temporarily deactivate it for some reason, for data security, we need a soft delete operation, that’s mean the record is not actually deleted, but a mark is used to indicate that the status has been deleted. The reason for this is to prevent users from directly deleting very important data.

Therefore, when designing a table, the following fixed attributes should be included: whether it is enabled, deleted, creation time and modification time.

Since every table should have these common fields, so we can create a base class for this, the name can be called BaseEntity, and ID can also be put in it, because this base class is only for inherited by others and does not need to be implemented, so you can use an abstract class, and then inherit User from BaseEntity, so that you don’t need to write these public fields every time.

using System.ComponentModel.DataAnnotations.Schema;

namespace MyDemo.Core.Data.Entity;

public abstract class BaseEntity
{    
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public long Id { get; set; }
    public bool IsActive { get; set;}
    public bool IsDeleted { get; set;}
    public DateTime CreatedAt { get; set; }
    public DateTime UpdatedAt { get; set; }
}

After establishing the attributes of the entity, you need to set the table name in the database, because sometimes the class name is not necessarily the same as the table name. To set the table name, you can use [Table("name" )], just add it to the class. At the same time, for better performance, it is better to add an index to the table. We can directly use the unique ID attribute as the index, and add [Index(nameof(Id))] to the class.

So the User.cs will be like below

[Table("User")]
[Index(nameof(Id))]
public class User : BaseEntity{  
  public string Name { get; set; }
  public string Password { get; set; }
  public string Email { get; set; }
}

Finally, we also need to do some constraints for the fields, such as the ID can’t be empty, the creation time is automatically based on the storage time of the current data, and the length of some fields, etc. These settings can be achieved through the IEntityTypeConfiguration interface.

public class UserEntityTypeConfiguration : IEntityTypeConfiguration<User>
{
    public void Configure(EntityTypeBuilder<User> builder)
    {
        builder.ToTable("User");
        builder.HasKey(x => x.Id);
        builder.Property(x => x.Id).IsRequired();
        builder.Property(x => x.CreatedAt).HasDefaultValue(DateTime.Now);
    }
}

And the completed codes should be as below

// MyDemo.Core/Data/Entity/User.cs
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace MyDemo.Core.Data.Entity;

[Table("User")]
[Index(nameof(Id))]
public class User : BaseEntity{  
  public string Name { get; set; }
  public string Password { get; set; }
  public string Email { get; set; }
}


public class UserEntityTypeConfiguration : IEntityTypeConfiguration<User>
{
    public void Configure(EntityTypeBuilder<User> builder)
    {
        builder.ToTable("User");
        builder.HasKey(x => x.Id);
        builder.Property(x => x.Id).IsRequired();
        builder.Property(x => x.CreatedAt).HasDefaultValue(DateTime.Now);
    }
}

4.3 Create DbContext

  1. When the entity is completed, it is necessary to create a DbContext for database context to process the data. We name it ApplicationDbContext here, and this class is to be inherited from DbContext
// MyDemo.Core/Data/ApplicationDbContext.cs
public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
      :base(options)
    { }
}
  1. The OnModelCreating method should be override in ApplicationDbContext, the purpose is to make the IEntityTypeConfiguration working. The last thing is to link the User entity with the specific data table, add a DbSet object, and then you can operate the tables in the database through this object. So the completed codes should be like below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;

namespace MyDemo.Core.Data.Entity;

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
      :base(options)
    { }

    public DbSet<User> Users => Set<User>();

     protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        // add the EntityTypeConfiguration classes
        modelBuilder.ApplyConfigurationsFromAssembly(
            typeof(ApplicationDbContext).Assembly
        );
    }
}

In the end, add the service that uses the database to the program.cs file of the API project. It should be noted that the added service code must be placed before creating the builder app!

// Add ApplicationDbContext and SQL Server support
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("Mssql"))
);

var app = builder.Build();

4.4 Generate EF codes

Now you can start to generate related EF migration code. However, before executing the relevant commands, you need to open the terminal in VS CODE, and then enter the root directory of the solution, because the subsequent related EF commands need to be executed here. Of course, you can also execute them in other directories, but It is necessary to modify the path of the project in the command.

Execute the following commands to generate the relevant database code migration files.

dotnet ef migrations add InitialMigrations --project .\MyDemo.Core\MyDemo.Core.csproj --startup-project .\MyDemo\MyDemo.csproj 

Then execute the command to update the code to the database.

dotnet ef database update --project .\MyDemo.Core\MyDemo.Core.csproj --startup-project .\MyDemo\MyDemo.csproj 

After that, you should find there is a new Migrations folder under the Data, and when you open the database, you should see there is a new DB MyDemo and the User table

Database

3 Summary

Now let’s summarize what we have learned.

  1. How to build a VS CODE development environment
  2. How to create a project, and have successfully created an API project
  3. Create the database and table with Code First

The next step is to create CRUD operation for the table (this is a data manipulation term in programming, which represents operations such as creating, deleting, modifying, etc., the full name is: Create, Read, Update, Delete), but before that, we need to understand a design pattern. A good design pattern can standardize the code and improve efficiency. So in the next, I will explain the Repository pattern and how to implement it :smiley:

Loading

<p>The post Use code first to connect database in .Net Core API first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2023/07/use-code-first-to-connect-database-in-net-core-api/feed/ 12
How to setup VS Code environment for ASP.NET Core Project https://www.coderblog.in/2023/07/how-to-setup-vs-code-environment-for-asp-net-core-project/ https://www.coderblog.in/2023/07/how-to-setup-vs-code-environment-for-asp-net-core-project/#comments Wed, 19 Jul 2023 04:11:48 +0000 https://www.coderblog.in/?p=800 This is post 1 of 8 in the series “Create a website with ASP.NET Core and Angular” In

<p>The post How to setup VS Code environment for ASP.NET Core Project first appeared on Coder Blog.</p>

]]>

This is post 1 of 8 in the series “Create a website with ASP.NET Core and Angular”

In this series, I will show you how to create the web API with .Net core and communicate with Angular in frontend, and I will keep to update it!

  1. How to setup VS Code environment for ASP.NET Core Project
  2. Use code first to connect database in .Net Core API
  3. Use the Repository pattern in .Net core
  4. Create .Net Core Api controller for CRUD with repository
  5. Use the Serilog in .Net Core
  6. Create Angular Project and Communicate with .Net Core
  7. Create Paging Data from .Net Core Api
  8. How to do Unit Testing for the Core Api Project with Repository

Visual Studio Code (hereinafter referred to as VS Code) is really a very powerful IDE, even better than Visual Studio! Next, let’s introduce how to use VS Code to develop .NET CORE.


Since .NET 5.0 now supports cross-platform and is mainly based on CORE, it can be developed directly using VS Code. Next, let me talk about why I use VS Code to develop: smiley:

1.Why use VS Code

VS Code is a lightweight code editor that has the following advantages over Visual Studio (hereinafter referred to as VS):

1.1 Lightweight and fast

VS Code is a lightweight application that starts quickly and uses little system resources. Compared with the complete integrated development environment of VS, the execution performance of VS Code is better.

1.2 Multi-platform support

VS Code can run on multiple operating systems such as Windows, macOS and Linux, enabling developers to maintain a consistent development environment on different platforms.

1.3 Strong scalability

VS Code provides a rich extension kit, which can freely expand functions according to development needs. Developers can install various language support, code snippets, themes and other extensions according to their own needs to make the editor more in line with personal preferences and workflow. And this is one of the reasons why I like it so much! Through various suites, development efficiency can be greatly improved, which is difficult for VS to match.

1.4 Rich theme resources

Although this is not a very hard requirement, I have to say that I use VS Code to a large extent, and this has a lot to do with it. Because of a good set of themes, the code will look very comfortable. After all, you will be working on it for a long time, so of course you should take good care of your eyes, and a good theme will also make your mood better :laughing:

1.5 Version control integration

VS Code has built-in support for Git version control, which can perform version control operations through the UI, and provides intuitive difference comparison and merge tools. This makes it more convenient for collaborative development or individual project management. Although this is also supported in VS, but base on the powerful extension for VS Code, it does a very good job in version control. For example, GitLen is more famous and easy to use.

1.6 Built-in terminal

VS Code has a built-in terminal function, allowing developers to execute commands in the editor, such as starting the server, executing tests, etc. This increases productivity by eliminating to switch to other terminal tools. This is also a feature that attracts me very much. It is because of the existence of this terminal that it is more convenient to develop .NET Core directly on it. If you also need to develop the Angular, I think you will have to use the terminal :). And when you open the terminal, it will automatically jump to the folder under the current project, which is very convenient!

1.7 Ecosystem support

VS Code is an open source project with a large user community, so there are plenty of tutorials, solutions, and resources available. Developers can easily find relevant resources and resolve issues quickly. This is the advantage of open source projects, because its strong community directly leads to its powerful functions, and the update frequency is very fast, which can continuously fix various problems.

1.8 Support multi-language development

Especially when developing .NET Core + Angular projects, it can directly support Angular well. So, there is no need to use two IDEs at the same time, which saves a lot of system resources!

2. Configure the .Net Core environment

2.1 Install the SDK

If you have not installed VS before, and have not tried .NET-related development, the first thing to do is to download the relevant SDK from Microsoft’s official website

https://dotnet.microsoft.com/en-us/download

2.2 Install the extension

In order for VS Code to support .NET Core well, the following extensions need to be installed:

1) DotNet Core Essentials

Through this extension, you can directly compile, create, and add references to the project in VS Code:

Use various .NET templates to create projects or related files, just like creating them in VS, very convenient

2) C# Extensions

This extension can provide you with templates for creating various .NET files. If you create a Class or Interface file, it will directly help you generate related file templates

3) vscode-solution-explorer

This extension can make your .NET project have the same effect as opening it in VS! You can directly view and manage your project directory in the form of a Solution, and then just right-click the corresponding project group to perform related operations

After installation, you will see a VS icon on the left. After clicking it, you can directly open the .sln solution file, and then browse

4) NuGet Gallery

This extension can help you install related packages from NuGet. Its interface is quite intuitive and convenient, and it can be installed for multiple project groups at one time, which feels easier to use than the one in VS.

OK, that’s all for the introduction of the basic kits, and I will make an other article on the useful extension of VS Code in the future. For the development of .Net Core, the several packages introduced above are completely sufficient :smiley:

3. Create a project

Next, let’s try to use VS Code to create a complete .Net Core project!

3.1 Create an empty folder

For example, create a folder named 001, then drag it directly into VS Code, and then open the terminal through the menu button above

3.2 Create a project using the DCE extension

Open the VS Code command line and enter dce new to create a new Project:

Choose DotNet Core

Then select C#, and a list of templates for various projects will appear

This time we choose ASP.NET Core Web API, directly create an API project, and then the first thing to enter is the name of the entire solution, such as I enter MyDemo here, and then enter the name of the project team, such as I Enter MyDemo.API, and then generate a complete project with the following structure

4. Test and run the project

The last step is to test whether the project we created can run normally! Here are two ways to run, one is to start directly in the terminal with the command line, and the other is to start with debugging. Now we try two ways:

4.1 Start from the command line

Open the project directory in the terminal, in my example above, it is MyDemo\MyDemo.API, and then execute the following command:

dotnet watch run

The reason for using watch that it can observe all code changes in real time. As long as there is a change, the website will be automatically updated, and the effect of the change can be seen immediately. It is very convenient, but if you use VS, you can’t do this.

After running, you will see the following interface, and the API page of Swagger will be opened automatically at the same time

Pages of the Swagger API documentation

4.2 Run in debug mode

Click the debug button on the left side, click the Run and Debug button, then several options will pop up, just choose the first .Net 5+ and .Net Core, and then will automatically create a debugger for your require with configuration file

At this time, a prompt box will appear in the lower right corner, just click “YES”

Then a configuration file for debugging will be created, then go back to the debugging window and click the small green triangle above to start it.

However, the directly opened URL cannot be accessed at this time. You need to manually enter the URL of the Swagger API (e.g http://localhost:5105 with my case), where you can directly set the breakpoints in it and then debug!

Summarize

The above is the whole process of how to use VS Code to develop .Net Core. The whole process is actually very simple and convenient, and with the powerful extensions of VS Code, it will make your subsequent development work more effective! Let’s give it a try today! :smirk:


The Chinese Version:

如何使用 Visual Studio Code 進行 ASP.NET Core 的開發

Loading

<p>The post How to setup VS Code environment for ASP.NET Core Project first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2023/07/how-to-setup-vs-code-environment-for-asp-net-core-project/feed/ 3
How to Implement Batch Upload and Processing of PDF Files with ASP.NET Core https://www.coderblog.in/2023/03/how-to-implement-batch-upload-and-processing-of-pdf-files-with-asp-net-core/ https://www.coderblog.in/2023/03/how-to-implement-batch-upload-and-processing-of-pdf-files-with-asp-net-core/#comments Sun, 05 Mar 2023 02:24:50 +0000 https://www.coderblog.in/?p=745 In this tutorial, we will walk through the steps to implement a batch upload and processing of PDF

<p>The post How to Implement Batch Upload and Processing of PDF Files with ASP.NET Core first appeared on Coder Blog.</p>

]]>
In this tutorial, we will walk through the steps to implement a batch upload and processing of PDF files with ASP.NET Core. We will allow clients to upload a ZIP file containing multiple PDF files, extract the ZIP file, read each PDF file’s field values, and finally save the values into a MSSQL database.

1. Receive the Upload File

The first step is to create an API endpoint that allows clients to upload a ZIP file containing multiple PDF files. We can use the IFormFile interface provided by ASP.NET Core to handle file uploads. Here is an example code snippet:

[HttpPost("upload")]
public async Task Upload(IFormFile file)
{
    // validate file extension and content type

    // save file to disk

    return Ok();
}

2. Extract the ZIP File

Once we have received the ZIP file, we need to extract it to a temporary directory. We can use the System.IO.Compression namespace to extract the ZIP file. Here is an example code snippet:

using System.IO.Compression;

...

var tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
ZipFile.ExtractToDirectory(zipFilePath, tempDir);

3. Read Each PDF File in ZIP

Now that we have extracted the ZIP file, we can loop through each PDF file in the directory and read its field values. We can use the iTextSharp library to read the field values. Here is an example code snippet:

using iTextSharp.text.pdf;

...

foreach (var pdfFile in Directory.GetFiles(tempDir, "*.pdf"))
{
    var pdfReader = new PdfReader(pdfFile);
    var pdfFormFields = pdfReader.AcroFields.Fields;

    foreach (var pdfFormField in pdfFormFields)
    {
        var fieldValue = pdfReader.AcroFields.GetField(pdfFormField.Key);
        // save fieldValue to database
    }
}

4. Read the PDF File Field’s Value

As mentioned in the previous step, we can use the iTextSharp library to read the field values of each PDF file. We can loop through each field and get its value using the GetField method of the AcroFields class.

5. Save the Value into MSSQL Database

Finally, we can save the field values into a MSSQL database. We can use the System.Data.SqlClient namespace to connect to the database and execute SQL commands. Here is an example code snippet:

using System.Data.SqlClient;

...

var connectionString = "Data Source=MyServer;Initial Catalog=MyDatabase;User ID=MyUsername;Password=MyPassword;";
using var connection = new SqlConnection(connectionString);
await connection.OpenAsync();

foreach (var pdfFormField in pdfFormFields)
{
    var fieldValue = pdfReader.AcroFields.GetField(pdfFormField.Key);
    var sqlCommand = new SqlCommand("INSERT INTO MyTable (FieldName, FieldValue) VALUES (@FieldName, @FieldValue)", connection);
    sqlCommand.Parameters.AddWithValue("@FieldName", pdfFormField.Key);
    sqlCommand.Parameters.AddWithValue("@FieldValue", fieldValue);
    await sqlCommand.ExecuteNonQueryAsync();
}

And that’s it! We have implemented a batch upload and processing of PDF files with ASP.NET Core.

Loading

<p>The post How to Implement Batch Upload and Processing of PDF Files with ASP.NET Core first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2023/03/how-to-implement-batch-upload-and-processing-of-pdf-files-with-asp-net-core/feed/ 10