首页 / C#开发 / 正文

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

5148 1 发布于: 2019-04-28 读完约需17分钟

前言

在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,本文就为码友网的读者们分享到这里。

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

版权声明:本作品系原创,版权归码友网所有,如未经许可,禁止任何形式转载,违者必究。

上一篇: SQL Server中编写自动生成指定时间段内按小时的连续的数据列表并按每个小时统计数据的SQL语句示例

下一篇: DncZeus框架用户动态权限数据流,鉴权、令牌、验证详解

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

分享扩散:

发表评论

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