要须心地收汗马,孔孟行世目杲杲。这篇文章主要讲述AutoMapper 9.0快速上手,从老版本迁移到9.0+AutoMapper9.0和Autofac的完美结合相关的知识,希望能为你提供帮助。
.NET模型映射器AutoMapper 9.0发布了,官方宣称不再支持静态方法调用了,老版本的部分API将在升级到9.0后,直接升级包到9.0会编译报错,所以写篇文章记录下AutoMapper新版本的学习过程吧,如果还不知道AutoMapper是什么的,建议先看这篇文章:https://masuit.com/156,或者参考官方文档:https://automapper.readthedocs.io/en/latest/Getting-started.html
AutoMapper9.0快速上手
首先,我们准备两个需要用于相互转换的类:
1 2 3 4 5 6 7 8 9 10 11 12 |
public
class
Person
{
public
string
Id
{
get ;
set ;
}
public
string
Name
{
get ;
set ;
}
public
Address
Address
{
get ;
set ;
}
}
public
class
Address
{
public
string
Province
{
get ;
set ;
}
public
string
City
{
get ;
set ;
}
public
string
Street
{
get ;
set ;
}
} |
1 2 3 4 5 6 |
public
class
PersonDto
{
public
string
Id
{
get ;
set ;
}
public
string
Name
{
get ;
set ;
}
public
string
Address
{
get ;
set ;
}
} |
准备好实体模型后我们将AutoMapper9.0通过nuget安装到项目中:
文章图片
开始写映射代码吧,首先,我们需要明确源和目标类型。由于目标类型的设计一般会受其所在层的影响,比如通常情况下我们最终呈现在页面上的数据结构和我们数据库底层设计会有所不同,但只要成员的名称与源类型的成员匹配,AutoMapper就能发挥最佳效果。不知何时开始的,AutoMapper可以实现自动映射了,也就是如果需要映射的源和目标的属性和类型长得一样,都可以不用写映射配置了,比如源对象里面有一个名为“ FirstName” 的成员,则会自动将其映射到目标对象的名为“ FirstName” 成员上。
映射时,AutoMapper会忽略空引用异常。这是默认设计的。如果你觉得这样做不好,你可以根据需要将AutoMapper的方法与自定义值解析器结合使用。
明确映射关系后,使用MapperConfiguration和CreateMap 为两种类型创建映射关系。MapperConfiguration每个AppDomain通常只需要一个实例,并且应该在应用程序启动期间进行实例化。
1 | var
config
=
new
MapperConfiguration(cfg
=>
cfg.CreateMap<
Person,
PersonDto>
().ForMember(p
=>
p.Address,
e
=>
e.MapFrom(p
=>
p.Address.Province
+
p.Address.City
+
p.Address.Street)));
|
现在,映射关系做好了,我们便可以创建映射器了:
1 | var
mapper
=
config.CreateMapper();
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
static
void
Main( string []
args)
{
var
config
=
new
MapperConfiguration(cfg
=>
cfg.CreateMap<
Person,
PersonDto>
().ForMember(p
=>
p.Address,
e
=>
e.MapFrom(p
=>
p.Address.Province
+
p.Address.City
+
p.Address.Street)));
var
mapper
=
config.CreateMapper();
var
person
=
new
Person()
{
Id
=
"1" ,
Name
=
"李扯火" ,
Address
=
new
Address()
{
Province
=
"新日暮里" ,
City
=
"地牢" ,
Street
=
"van先生的家"
}
};
var
personDto
=
mapper.Map<
PersonDto>
(person);
} |
1 2 3 4 5 6 7 |
public
class
MappingProfile
:
Profile
{
public
MappingProfile()
{
CreateMap<
Person,
PersonDto>
().ForMember(p
=>
p.Address,
e
=>
e.MapFrom(p
=>
p.Address.Province
+
p.Address.City
+
p.Address.Street));
}
} |
1 | var
config
=
new
MapperConfiguration(cfg
=>
cfg.AddProfile( new
MappingProfile()));
|
由于早期版本的AutoMapper映射时我们都直接调静态方法Mapper.Map就可以了,很爽,但是,9.0版本开始,取消了静态方法的调用,这就意味着升级后的代码可能需要随处创建Mapper的实例或者使用依赖注入容器对AutoMapper的实例进行托管。
我们先创建一个.NET Core的web项目,并创建一个基于内存的DbContext:
Models:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public
class
Person
{
public
string
Id
{
get ;
set ;
}
public
string
Name
{
get ;
set ;
}
public
Address
Address
{
get ;
set ;
}
}
public
class
Address
{
public
string
Id
{
get ;
set ;
}
public
string
Province
{
get ;
set ;
}
public
string
City
{
get ;
set ;
}
public
string
Street
{
get ;
set ;
}
public
string
PersonId
{
get ;
set ;
}
public
Person
Person
{
get ;
set ;
}
} |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public
class
DataContext
:
DbContext
{
public
DataContext(DbContextOptions<
DataContext>
options)
:
base (options)
{
}
protected
override
void
OnModelCreating(ModelBuilder
modelBuilder)
{
base .OnModelCreating(modelBuilder);
modelBuilder.Entity<
Person>
().HasOne(e
=>
e.Address).WithOne(a
=>
a.Person).IsRequired( false ).OnDelete(DeleteBehavior.Cascade);
}
public
DbSet<
Person>
Persons
{
get ;
set ;
}
public
DbSet<
Address>
Address
{
get ;
set ;
}
} |
1 2 3 | var
config
=
new
MapperConfiguration(e
=>
e.AddProfile( new
MappingProfile()));
var
mapper
=
config.CreateMapper();
services.AddSingleton(mapper);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
[Route( "api/[controller]" )]
[ApiController]
public
class
ValuesController
:
ControllerBase
{
private
readonly
IMapper
_mapper;
private
readonly
DataContext
_dataContext;
public
ValuesController(IMapper
mapper,
DataContext
dataContext)
{
_mapper
=
mapper;
_dataContext
=
dataContext;
}
[HttpGet]
public
ActionResult
Get()
{
var
person
=
_dataContext.Persons.Include(p
=>
p.Address).FirstOrDefault();
return
Ok(_mapper.Map<
PersonDto>
(person));
}
} |
文章图片
迁移点2:ProjectTo的新实现AutoMapper9.0的ProjectTo方法需要传入一个MapperConfiguration对象,所以,要调用ProjectTo方法,还需要注入MapperConfiguration对象:
1 2 | var
config
=
new
MapperConfiguration(e
=>
e.AddProfile( new
MappingProfile()));
services.AddSingleton(config);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
[Route( "api/[controller]" )]
[ApiController]
public
class
ValuesController
:
ControllerBase
{
private
readonly
IMapper
_mapper;
private
readonly
MapperConfiguration
_mapperConfig;
private
readonly
DataContext
_dataContext;
public
ValuesController(IMapper
mapper,
DataContext
dataContext,
MapperConfiguration
mapperConfig)
{
_mapper
=
mapper;
_dataContext
=
dataContext;
_mapperConfig
=
mapperConfig;
}
[HttpGet]
public
ActionResult
Get()
{
var
person
=
_dataContext.Persons.Include(p
=>
p.Address).FirstOrDefault();
return
Ok(_mapper.Map<
PersonDto>
(person));
}
[HttpGet( "list" )]
public
ActionResult
Gets()
{
var
list
=
_dataContext.Persons.Include(p
=>
p.Address).ProjectTo<
PersonDto>
(_mapperConfig).ToList();
return
Ok(list);
}
} |
文章图片
安装扩展后,Startup.cs里面只需要一句话:
1 | services.AddAutoMapper(Assembly.GetExecutingAssembly());
|
同样,AutoMapper实例也可以被Autofac托管,实现属性注入!
AutoMapper与Autofac的完美结合
Autofac的好处就在于它能批量注入和属性注入,当然在这里体现的就是autofac属性注入的优势,省去了构造函数注入的麻烦,如果没装Resharper的同学有时还会忘记注入,而autofac则解决了这样的问题。
我们新建一个.NET Core的web项目,并安装好AutoMapper.Extensions.Microsoft.DependencyInjec和Autofac.Extensions.DependencyInjection这两个nuget包,因为这两个包已经包含了AutoMapper和autofac,所以不需要单独安装这两个包。
准备一个Service来模拟我们的项目分层:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public
interface
IPersonService
{
List<
PersonDto>
GetAll();
Person
Get( string
id);
}
public
class
PersonService
:
IPersonService
{
public
DataContext
DataContext
{
get ;
set ;
}
public
MapperConfiguration
MapperConfig
{
get ;
set ;
}
public
List<
PersonDto>
GetAll()
{
return
DataContext.Persons.ProjectTo<
PersonDto>
(MapperConfig).ToList();
}
public
Person
Get( string
id)
{
return
DataContext.Persons.FirstOrDefault(p
=>
p.Id
==
id);
}
} |
然后我们改造Startup.cs,让依赖注入容器使用Autofac托管:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public
IServiceProvider
ConfigureServices(IServiceCollection
services)
{
services.AddDbContext<
DataContext>
(opt
=>
opt.UseInMemoryDatabase());
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2).AddControllersAsServices().AddViewComponentsAsServices().AddTagHelpersAsServices();
var
config
=
new
MapperConfiguration(e
=>
e.AddProfile( new
MappingProfile()));
services.AddSingleton(config);
services.AddAutoMapper(Assembly.GetExecutingAssembly());
services.AddAutofac();
ContainerBuilder
builder
=
new
ContainerBuilder();
builder.Populate(services);
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsImplementedInterfaces().Where(t
=>
t.Name.EndsWith( "Service" )
||
t.Name.EndsWith( "Controller" )).PropertiesAutowired().AsSelf().InstancePerDependency();
//注册控制器为属性注入
var
autofacContainer
=
new
AutofacServiceProvider(builder.Build());
return
autofacContainer;
} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
[Route( "api/[controller]" )]
[ApiController]
public
class
ValuesController
:
ControllerBase
{
public
Mapper
Mapper
{
get ;
set ;
}
public
IPersonService
PersonService
{
get ;
set ;
}
[HttpGet]
public
ActionResult
Get()
{
return
Ok(Mapper.Map<
PersonDto>
(PersonService.Get( "1" )));
}
[HttpGet( "list" )]
public
ActionResult
Gets()
{
var
list
=
PersonService.GetAll();
return
Ok(list);
}
} |
上面都是.NET Core的代码,.NET Framework怎么办?
由于AutoMapper和autofac都是基于.NET Standard的项目,所以用法上都是大同小异,我相信你如果在项目中用了这两个库,要升级AutoMapper9.0也不难了,升级后哪些地方报错的,就按上面的步骤弄吧。
完整Demo代码下载
https://www.lanzous.com/i5qhrvg
【AutoMapper 9.0快速上手,从老版本迁移到9.0+AutoMapper9.0和Autofac的完美结合】出处:https://masuit.com/1625/ocrel
推荐阅读
- Android开发 run的时候出现waiting for debugger的情况,及解决问题
- APP自动化环境部署
- app测试专项--@松勤技术分享
- Android : 网络adb配置及有线端口占用解决方法
- android 按指定包名返回版本号
- Android Parcelable和Serializable的区别
- 如何获取到app的包名
- appium配置
- uiautomatorviewer 连接真机的时候报 com.android.ddmlib.SyncException :Remote object doesn't exits!