C#/.NET应用程序开发中使用反射动态加载AutoMapper的映射配置关系

C#开发 作者: Rector 175 次阅读 · 读完约需 5 分钟 收藏本文

郑重申明:本文未经许可,禁止任何形式转载

前言

在C#/.NET应用程序开发中,经常遇到在两种不同的实体类之间相互映射的问题,比如有数据源对应的领域实体类和一个用于UI界面的数据传输对象(DTO),我们从数据源中取出数据并赋给领域实体,常规情况下,我们最后需要将领域实体的数据手动地映射到DTO,如:

领域实体对象Customer.cs

public class Customer
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

数据传输对象CustomerViewModel.cs

public class CustomerViewModel
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string FullName { get; set; }
}

查询数据并映射(模拟查询)

var customer = new Customer
{
    Id = 1,
    FirstName = "Rector",
    LastName = "Liu"
};
var viewModel = new CustomerViewModel
{
    Id = customer.Id,
    FirstName = customer.FirstName,
    LastName = customer.LastName,
    FullName = string.Format("{0} {1}", customer.FirstName, customer.LastName)
};

这样,我们就手动地将领域实体Customer的数据赋值到了数据传输对象CustomerViewModel上了。此方式从实现上来说是完全没有任何问题的,但如果一个程序中有很多实体间的映射需要处理,或者两个实体的属性很多的时候,手动赋值则需要就是一个大工作量的体力活了。

所以,为了更方便、快捷地实现两个实体之间的数据映射,机智的开发者们也是开发出了像AutoMapper这样的.NET实体映射工具。.NET开发人员只需要安装并进行几步简单的配置即可。

但是,使用AutoMapper也会遇到配置的问题,比如我们需要手动配置AutoMapper中两个实体的映射:

var configuration = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<Customer, CustomerViewModel>();
    cfg.CreateMap<CustomerViewModel, Customer>();
});
var mapper = configuration.CreateMapper();

如果新增实体的映射,我们还得往这个配置中添加配置,如:

var configuration = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<Customer, CustomerViewModel>();
    cfg.CreateMap<CustomerViewModel, Customer>();

    cfg.CreateMap<Foo, FooDto>();
    cfg.CreateMap<Bar, BarDto>();
});
var mapper = configuration.CreateMapper();

也就是说,我们每新增一种映射关系,都需要手动地往MapperConfiguration这个配置中动态添加。

在中小型的项目开发中,这样做也是可以的,但在大型项目中,手动添加映射配置效率有比较低了。

今天就为C#/.NET开发者们分享一种使用反射实现的可以动态加载AutoMapper映射配置的方法。

Heroic.AutoMapper

首先,使用Nuget搜索(Heroic.AutoMapper),然后就可以将其安装到需要使用的项目中。

PM> Install-Package Heroic.AutoMapper

当然在Github上,找到Heroic.AutoMapper

Heroic.AutoMapper兼容ASP.NET MVC 5, ASP.NET Web Api 2, 控制台应用程序以及ASP.NET Core等.NET的应用程序。

ASP.NET应用程序中

在”经典”的ASP.NET应用程序中,需要在Global.asax文件中调用HeroicAutoMapperConfigurator以加载实体映射配置:

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        HeroicAutoMapperConfigurator.LoadMapsFromAssemblyContainingTypeAndReferencedAssemblies<HomeController>();
    }
}

控制台应用程序中

在控制台应用程序中,可能的加载方式为:

using Heroic.AutoMapper;

public class Program 
{
    public static void Main() 
    {
        HeroicAutoMapperConfigurator.LoadMapsFromCallerAndReferencedAssemblies();

        Console.WriteLine("All set!");
    }
}

ASP.NET Core

在ASP.NET Core应用程序中,需要在Startup.cs启动文件中调用HeroicAutoMapperConfigurator的方法,如:

//..
using Heroic.AutoMapper;

namespace YourNamespace
{
    public class Startup
    {
        //.. snip

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseHsts();
            }

            app.UseHttpsRedirection();

            app.UseCors(x => x.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader().AllowCredentials());

            app.UseAuthentication();

            app.UseMvc();

            HeroicAutoMapperConfigurator.LoadMapsFromAssemblyContainingTypeAndReferencedAssemblies<WidgetModel>();
        }
    }
}

使用方法

以上分别是在三种不同的.NET项目中的引用和调用,这里介绍如何配置需要映射的实体。

领域模型映射到视图模型

如果需要将你的领域模型(CustomerRiskViewModel)映射到视图模型(Risk),则只需要将视图模型继承IMapFrom<T>这个泛型接口即可,如:

public class CustomerRiskViewModel : IMapFrom<Risk>
{
    public string Title { get; set; }

    public string Description { get; set; }
}

视图模型映射到领域模型

相反,如果需要将视图模型映射回领域模型,则将视图模型继承IMapTo<T>这个泛型接口即可,如:

public class AddCustomerForm : IMapTo<Customer>
{
    [Required, Display(Name = "Full Name", Prompt = "Full Name (ex: John Doe)...")]
    public string Name { get; set; }

    [Required, DataType(DataType.EmailAddress)]
    public string WorkEmail { get; set; }
}

如何添加自定义映射呢?

看到这里,你可能会问了:”你上面列举的只是两种常规的映射,如果我有自定义映射属性,是否支持呢,如果支持,又是如何配置的呢?”

读者朋友们是非常睿智的,但你能想的,这个组件的开发者也是考虑到的,如果你需要配置自定义映射属性,则继承另一个接口IHaveCustomMappings即可:

public class ProfileForm : IMapFrom<User>, IHaveCustomMappings
{
    public string FullName { get; set; }

    public string EmailAddress { get; set; }

    public void CreateMappings(IMapperConfiguration configuration)
    {
        configuration.CreateMap<User, ProfileForm>()
            .ForMember(d => d.FullName, opt => opt.MapFrom(s => s.UserName))
            .ForMember(d => d.EmailAddress, opt => opt.MapFrom(s => s.Email));
    }
}

通过使用Heroic.AutoMapper和以上的配置,我们在项目后期就无需再手动添加实体之映射配置,而是实现Heroic.AutoMapper提供的一些接口即可。

映射示例:

var customer = new CustomerAdditional
{
    Id = 1,
    Age = 20,
    FirstName = "First",
    LastName = "Last",
    CreatedAt = DateTime.Now.AddDays(1),
    Description = "Desc",
    Address = new Address
    {
        Province = "ChongQing",
        City = "ChongQing"
    }
};

var model = Mapper.Map<CustomerAdditional, CustomerViewModel>(customer);
Console.WriteLine($"Id:{model.Id},Age:{model.Age},FirstName:{model.FirstName},LastName:{model.LastName},CreatedAt:{model.CreatedAt},Description:{model.Description},Province:{model.Address.Province},City:{model.Address.City}");

是不是又方便许多了呢?

我是Rector,本文就为码友网的读者们分享到这里。

如果你觉得本文对你或者他人有帮助,欢迎点个赞,谢谢!!!

阅读了该文章的人还浏览了...

本文永久链接码友网 » C#/.NET应用程序开发中使用反射动态加载AutoMapper的映射配置关系

发布于: 2019-04-28 22:54:43
分享扩散:

发表评论

登录用户才能发表评论, 请 登 录 或者 注册