European ASP.NET 4.5 Hosting BLOG

BLOG about ASP.NET 4, ASP.NET 4.5 Hosting and Its Technology - Dedicated to European Windows Hosting Customer

European ASP.NET Core Hosting :: How to Use AutoMapper in Asp.Net Core Application

clock December 10, 2019 11:46 by author Scott

This is only brief tutorial about how to use AutoMapper in Asp Net Core 3.0 application. Automapper is a very popular Object-to-Object mapping library that can be used to map objects.

How to use automapper in Asp.Net Core 3.0 application

Let’s see how to use automapper in Asp Net Core 3.0 application using a very simple example.

1. First step you need to do is please make sure you install Asp.net Core 3.0 application à Create a new project button à then choose “ASP.NET Core Web Application” template à click “Next” button and then enter your Project name. Please see the image below for further information

 

2. Install AutoMapper in Asp.Net Core 3.0 Application 

Now, in this step, we will install AutoMapper in our project. So, go to visual studio and then go to “Tools” from the menu and then select “NuGet Package Manager” and then choose “Manager NuGet Packages for Solution”. Now, go to “Browse” tab and then install this below package as you do see below in the screenshot.

Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection

3. Configure AutoMapper in Asp.net Core 3.0 Application

Go to project folder structure, and then go to Startup class and then add some changes as you do see below in the code file’s Line # 11 and Line # 26.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using AutoMapper;
namespace AspNetCore3AutoMapper
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();
            services.AddAutoMapper(typeof(Startup));
        }
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }
            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseRouting();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });

        }
    }
}

4. Create a Model Class and Data Transfer Object Class

Now, in this step , we will create two classes. One is Model class and the other one is Dto(Data Transfer Object) class. So, go to Models’ folder and then right click on the folder name and then add a new class with the name of “Employee” and then write some properties as you do see below in the code.

public class Employee
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Department { get; set; }
    }

Now, again add a new class with the name of “EmployeeDto” as you do see below in the file.

public class EmployeeDto
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Department { get; set; }
    }

5. Add relation of Employee class with EmployeeDto class

Now, in this step, we will see how to add relation between a domain class and a Dto class. So, again add a new class (E.g. AutoMapping.cs) and then write some code as you do see below in the code.

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

namespace AspNetCoreAutoMapper.Models
{
    public class AutoMapping : Profile
    {
        public AutoMapping()
        {
            CreateMap<Employee, EmployeeDto>();
        }
    }
}

Let’s understand the above code.

Line # 9: In this line, we are inheriting AutoMapping class from Profile.

Line # 13: In this line, we are mapping our Employee and EmployeeDto Classes.

6. Map Employee class with EmployeeDto Controller

We will see how to map Employee class with EmployeeDto class within the Home Controller. So, go to HomeController and then go to Index method and then write some code as you do see below in the file.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using AspNetCore3AutoMapper.Models;
using AutoMapper;

namespace AspNetCore3AutoMapper.Controllers
{
    public class HomeController : Controller
    {
        private readonly IMapper _mapper;
        public HomeController(IMapper mapper)
        {
            _mapper = mapper;
        }
        public IActionResult Index()
        {
            var empObj = new Employee();
            empObj.Id = 1;
            empObj.Name = "Scott";
            empObj.Department = "MM";
            var employeeDto = _mapper.Map<EmployeeDto>(empObj);
            return View(employeeDto);
        }
    }
}

Let’s understand the above code.

Line # 15 to 19: In this block of code, we are injecting IMapper interface. This will help us to use our configured mappings.

Line # 22 to 25: In this block of code, we are initializing Employee object.

Line # 26: In this line, we are using IMapper interface that we have injected into our constructor to call the Map method. And we are giving the type that we want to map and the object that we want to map from.

Line # 27: In this line, we are returning the EmployeeDto object to the view.

Now, go to Index view and access the returning values from the Index method as you do see below in the file.

@model EmployeeDto
@{
    ViewData["Title"] = "Home Page";
}
<div>
    <h1>ID: @Model.Id</h1>
    <h1>Name: @Model.Name</h1>
    <h1>Department: @Model.Department</h1>
</div>

Now, run your project by pressing f5 and then you will see the output

Or you can install this package by using this below command in Package Manager Console as you do see below in the screenshot.



European ASP.NET Core 3 Hosting :: Custom JSONConverter ASP.NET Core 3

clock October 17, 2019 07:17 by author Scott

With the introduction of ASP.NET Core 3.0 the default JSON serializer has been changed from Newtonsoft.Json to System.Text.Json. For projects and libraries switching to the new JSON serializer this change means more performance and the opportunity to rewrite our JsonConverters.

Serialization of concrete classes

Let's start with a simple one that can (de)serialize a concrete class Category. In our example we (de)serialize the property Name only.

public class Category
{
   public string Name { get; }

   public Category(string name)
   {
      Name = name;
   }
}

To implement a custom JSON converter we have to derive from the generic class JsonConverter<T> and to implement 2 methods: Read and Write.

public class CategoryJsonConverter : JsonConverter<Category>
{
   public override Category Read(ref Utf8JsonReader reader,
                                 Type typeToConvert,
                                 JsonSerializerOptions options)
   {
      var name = reader.GetString();

      return new Category(name);
   }

   public override void Write(Utf8JsonWriter writer,
                              Category value,
                              JsonSerializerOptions options)
   {
      writer.WriteStringValue(value.Name);
   }
}

The method Read is using the Utf8JsonReader to fetch a string, i.e. the name, and the method Write is writing a string using an instance of Utf8JsonWriter.

In both cases (i.e. during serialization and deserialization) the converter is not being called if the value is null so I skipped the null checks. The .NET team doesn't do null checks either, see JsonKeyValuePairConverter<TKey, TValue>.

Let's test the new JSON converter. For that we create an instance of JsonSerializerOptions and add our CategoryJsonConverter to the Converters collection. Next, we use the static class JsonSerializer to serialize and to deserialize an instance of Category.

Category category = new Category("my category");

var serializerOptions = new JsonSerializerOptions
{
    Converters = { new CategoryJsonConverter() }
};

// json = "my category"
var json = JsonSerializer.Serialize(category, serializerOptions);

// deserializedCategory.Name = "my category"
var deserializedCategory = JsonSerializer.Deserialize<Category>(json, serializerOptions);

Serialization of generic classes

The next example is slightly more complex. The property we are serializing is a generic type argument, i.e. we can't use methods like reader.GetString() or writer.WriteStringValue(name) because we don't know the type at compile time.

In this example I've changed the class Category to a generic type and renamed the property Name to Key:

public class Category<T>
{
   public T Key { get; }

   public Category(T key)
   {
      Key = key;
   }
}

For serialization of the generic property Key we need to fetch a JsonSerializer<T> using the instance of JsonSerializerOptions.

public class CategoryJsonConverter<T> : JsonConverter<Category<T>>
{
   public override Category<T> Read(ref Utf8JsonReader reader,
                                    Type typeToConvert,
                                    JsonSerializerOptions options)
   {
      var converter = GetKeyConverter(options);
      var key = converter.Read(ref reader, typeToConvert, options);

      return new Category<T>(key);
   }

   public override void Write(Utf8JsonWriter writer,
                              Category<T> value,
                              JsonSerializerOptions options)
   {
      var converter = GetKeyConverter(options);
      converter.Write(writer, value.Key, options);
   }

   private static JsonConverter<T> GetKeyConverter(JsonSerializerOptions options)
   {
      var converter = options.GetConverter(typeof(T)) as JsonConverter<T>;

      if (converter is null)
         throw new JsonException("...");

      return converter;
   }
}

The behavior of the generic JSON converter is the same as before especially if the Key is of type string.

Deciding the concrete JSON converter at runtime

Having several categories with different key types, say, string and int, we need to register them all with the JsonSerializerOptions.

var serializerOptions = new JsonSerializerOptions
                        {
                           Converters =
                           {
                              new CategoryJsonConverter<string>(),
                              new CategoryJsonConverter<int>()
                           }
                        };

If the number of required CategoryJsonConverters grows to big or the concrete types of the Key are not known at compile time then this approach is not an option. To make this decision at runtime we need to implement a JsonConverterFactory. The factory has 2 method: CanConvert(type) that returns true if the factory is responsible for the serialization of the provided type; and CreateConverter(type, options) that should return an instance of type JsonConverter.

public class CategoryJsonConverterFactory : JsonConverterFactory
{
   public override bool CanConvert(Type typeToConvert)
   {
      if (!typeToConvert.IsGenericType)
         return false;

      var type = typeToConvert;

      if (!type.IsGenericTypeDefinition)
         type = type.GetGenericTypeDefinition();

      return type == typeof(Category<>);
   }

   public override JsonConverter CreateConverter(Type typeToConvert,
                                                 JsonSerializerOptions options)
   {
      var keyType = typeToConvert.GenericTypeArguments[0];
      var converterType = typeof(CategoryJsonConverter<>).MakeGenericType(keyType);

      return (JsonConverter)Activator.CreateInstance(converterType);
   }
}

Now, we can remove all registrations of the CategoryJsonConverter<T> from the options and add the newly implemented factory.

Category<int> category = new Category<int>(42);

var serializerOptions = new JsonSerializerOptions
{
    Converters = { new CategoryJsonConverterFactory() }
};

// json = 42
var json = JsonSerializer.Serialize(category, serializerOptions);

// deserialized.Key = 42
var deserialized = JsonSerializer.Deserialize<Category<int>>(json, serializerOptions);

In the end the implementation of a custom converter for System.Text.Json is very similar to the one for Newtonsoft.Json. The biggest difference here is the non-existence of a non-generic JsonConverter but for that we've got the JsonConverterFactory.

Actually, there is a non-generic JsonConverter which is the base class of the JsonConverter<T> and the JsonConverterFactory but we cannot (and should not) use this class directly because its constructor is internal.



About HostForLIFE.eu

HostForLIFE.eu is European Windows Hosting Provider which focuses on Windows Platform only. We deliver on-demand hosting solutions including Shared hosting, Reseller Hosting, Cloud Hosting, Dedicated Servers, and IT as a Service for companies of all sizes.

We have offered the latest Windows 2016 Hosting, ASP.NET Core 2.2.1 Hosting, ASP.NET MVC 6 Hosting and SQL 2017 Hosting.


Tag cloud

Sign in