前言
在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,本文就为码友网的读者们分享到这里。
如果你觉得本文对你或者他人有帮助,欢迎点个赞,谢谢!!!
版权声明:本作品系原创,版权归码友网所有,如未经许可,禁止任何形式转载,违者必究。
发表评论
登录用户才能发表评论, 请 登 录 或者 注册