如何使用 automapper 9.0.0.0 基于句柄 属性 自动映射两个列表
How can I automap two lists based on a handle property with automapper 9.0.0.0
我正在尝试使用基于句柄 属性 的自动映射器自动映射两个列表。类似于内部联接。 automapper 9.0.0.0 可以实现吗?
public class MyObject
{
public int Handle { get; set; }
public string Name { get; set; }
}
public class MyObjectExtension
{
public int Handle { get; set; }
public string Description{ get; set; }
}
public class MyRichObject
{
public int Handle { get; set; }
public string Name { get; set; }
public string Description{ get; set; }
}
//and here my mapper usage:
IEnumerable<MyObject> MyObjects;
IEnumerable<MyObjectExtension> MyObjectExtensions;
IEnumerable<MyRichObject> MyRichObjects;
// mapping to a new object
MyRichObjects= Mapper.Map<IEnumerable<MyRichObject>(MyObjects);
// adding MyObjectExtension properties by mapping to the existing RichObject
MyRichObjects= Mapper.Map<IEnumerable<MyObjectExtension>, IEnumerable<MyRichObject>>(MyObjectExtensions, MyRichObjects);
最后一段代码有效,但它可能将两个列表中的元素一一映射,我想根据 Handle
属性.[=14= 映射它们]
下面是我如何在我的 NInjectDependencyResolver class 中为 NInject 添加 Automapper 绑定,但我该如何设置 cfg.AddCollectionMappers()??;
// AutoMapper mapping
kernel.Bind<MapperConfiguration>()
.ToSelf()
.WithConstructorArgument<Action<IMapperConfigurationExpression>>(
cfg => new Mappers.AutoMapperConfiguration(cfg));
//.InRequestScope()
kernel.Bind<IConfigurationProvider>().ToMethod(ctx => ctx.Kernel.Get<MapperConfiguration>());
kernel.Bind<IMapper>().ToMethod(maper => kernel.Get<MapperConfiguration>().CreateMapper()).InSingletonScope();
kernel.Bind<IExpressionBuilder>().ToConstructor(ctx => new ExpressionBuilder(kernel.Get<MapperConfiguration>()));
AutoMapper 9 删除了静态映射,因此问题的代码只有在 Mapper
是一个 IMapper
属性 或持有映射器实例引用的字段时才有效。
正如the AutoMapper docs所说:
When mapping to an existing collection, the destination collection is cleared first. If this is not what you want, take a look at AutoMapper.Collection.
库的 Github 页面显示,通过句柄匹配需要在映射器配置中添加 Collections
并用一行指定对象等效性:
cfg.AddCollectionMappers();
...
cfg.CreateMap<MyObjectExtension, MyRichObject>()
.EqualityComparison( (oe, ro) => oe.Handle == ro.Handle);
之后,您可以直接调用 Map
,无需任何修改:
mapper.Map(MyObjectExtensions, MyRichObjects);
这与 LINQ 连接非常相似。事实上,mapping implementation is quite similar to Enumerable.Join's implementation - 这两种方法都会创建目标的 Lookup
table 以在迭代源之前加快查找速度。 AutoMapper 更进一步,并使用匹配的源属性更新目标对象。
请注意 destination
必须是 ICollection<T>
。它不能是 IEnumerable<T>
,因为该接口不允许修改。
另一种方法是在 MyObjects
和 MyObjectExtensions
之间使用 LINQ 连接:
var richObjects=myObjects.Join(myObjectsExtensions,
o => o.Handle,
oe => oe.Handle,
(o,oe)=>new MyRichObject {
Handle = o.Handle,
Name = o.Name,
Description = oe.Description
})
.ToArray();
重要
如果数据已经在内存中,所有这些都有意义。对于存储在数据库中的数据,far 更快、更便宜(也更容易)执行带有 JOIN 的 SQL 语句,该语句将直接 return 最终对象。该查询可以由像 EF (Core) 这样的 ORM 生成,也可以由像 Dapper 这样的微型 ORM 直接执行。
我认为,就您而言,最好使用 Linq。
例如:
List<MyObject> listMyObject = new List<MyObject>();
listMyObject.Add(new MyObject() { Handle = 1, Name = "FirstName" });
listMyObject.Add(new MyObject() { Handle = 2, Name = "SecondName" });
listMyObject.Add(new MyObject() { Handle = 3, Name = "ThirdName" });
List<MyObjectExtension> listMyObjectExtensions = new List<MyObjectExtension>();
listMyObjectExtensions.Add(new MyObjectExtension() { Handle = 1, Description = "FirstDescription" });
listMyObjectExtensions.Add(new MyObjectExtension() { Handle = 2, Description = "SecondDescription" });
listMyObjectExtensions.Add(new MyObjectExtension() { Handle = 3, Description = "ThirdDescription" });
IEnumerable<MyObject> MyObjects = listMyObject.AsEnumerable<MyObject>();
IEnumerable<MyObjectExtension> MyObjectExtensions = listMyObjectExtensions.AsEnumerable<MyObjectExtension>();
IEnumerable<MyRichObject> MyRichObjects;
MyRichObjects = from myObject in MyObjects
join myObjectExtension in MyObjectExtensions on myObject.Handle equals myObjectExtension.Handle
select new MyRichObject { Handle = myObject.Handle, Name = myObject.Name, Description = myObjectExtension.Description };
foreach (var MyRichObject in MyRichObjects)
{
System.Diagnostics.Debug.WriteLine($"Id: \"{MyRichObject.Handle}\". Name: {MyRichObject.Name} Description: {MyRichObject.Description}");
}
Return:
Id: "1". Name: FirstName Description: FirstDescription
Id: "2". Name: SecondName Description: SecondDescription
Id: "3". Name: ThirdName Description: ThirdDescription
我正在尝试使用基于句柄 属性 的自动映射器自动映射两个列表。类似于内部联接。 automapper 9.0.0.0 可以实现吗?
public class MyObject
{
public int Handle { get; set; }
public string Name { get; set; }
}
public class MyObjectExtension
{
public int Handle { get; set; }
public string Description{ get; set; }
}
public class MyRichObject
{
public int Handle { get; set; }
public string Name { get; set; }
public string Description{ get; set; }
}
//and here my mapper usage:
IEnumerable<MyObject> MyObjects;
IEnumerable<MyObjectExtension> MyObjectExtensions;
IEnumerable<MyRichObject> MyRichObjects;
// mapping to a new object
MyRichObjects= Mapper.Map<IEnumerable<MyRichObject>(MyObjects);
// adding MyObjectExtension properties by mapping to the existing RichObject
MyRichObjects= Mapper.Map<IEnumerable<MyObjectExtension>, IEnumerable<MyRichObject>>(MyObjectExtensions, MyRichObjects);
最后一段代码有效,但它可能将两个列表中的元素一一映射,我想根据 Handle
属性.[=14= 映射它们]
下面是我如何在我的 NInjectDependencyResolver class 中为 NInject 添加 Automapper 绑定,但我该如何设置 cfg.AddCollectionMappers()??;
// AutoMapper mapping
kernel.Bind<MapperConfiguration>()
.ToSelf()
.WithConstructorArgument<Action<IMapperConfigurationExpression>>(
cfg => new Mappers.AutoMapperConfiguration(cfg));
//.InRequestScope()
kernel.Bind<IConfigurationProvider>().ToMethod(ctx => ctx.Kernel.Get<MapperConfiguration>());
kernel.Bind<IMapper>().ToMethod(maper => kernel.Get<MapperConfiguration>().CreateMapper()).InSingletonScope();
kernel.Bind<IExpressionBuilder>().ToConstructor(ctx => new ExpressionBuilder(kernel.Get<MapperConfiguration>()));
AutoMapper 9 删除了静态映射,因此问题的代码只有在 Mapper
是一个 IMapper
属性 或持有映射器实例引用的字段时才有效。
正如the AutoMapper docs所说:
When mapping to an existing collection, the destination collection is cleared first. If this is not what you want, take a look at AutoMapper.Collection.
库的 Github 页面显示,通过句柄匹配需要在映射器配置中添加 Collections
并用一行指定对象等效性:
cfg.AddCollectionMappers();
...
cfg.CreateMap<MyObjectExtension, MyRichObject>()
.EqualityComparison( (oe, ro) => oe.Handle == ro.Handle);
之后,您可以直接调用 Map
,无需任何修改:
mapper.Map(MyObjectExtensions, MyRichObjects);
这与 LINQ 连接非常相似。事实上,mapping implementation is quite similar to Enumerable.Join's implementation - 这两种方法都会创建目标的 Lookup
table 以在迭代源之前加快查找速度。 AutoMapper 更进一步,并使用匹配的源属性更新目标对象。
请注意 destination
必须是 ICollection<T>
。它不能是 IEnumerable<T>
,因为该接口不允许修改。
另一种方法是在 MyObjects
和 MyObjectExtensions
之间使用 LINQ 连接:
var richObjects=myObjects.Join(myObjectsExtensions,
o => o.Handle,
oe => oe.Handle,
(o,oe)=>new MyRichObject {
Handle = o.Handle,
Name = o.Name,
Description = oe.Description
})
.ToArray();
重要
如果数据已经在内存中,所有这些都有意义。对于存储在数据库中的数据,far 更快、更便宜(也更容易)执行带有 JOIN 的 SQL 语句,该语句将直接 return 最终对象。该查询可以由像 EF (Core) 这样的 ORM 生成,也可以由像 Dapper 这样的微型 ORM 直接执行。
我认为,就您而言,最好使用 Linq。 例如:
List<MyObject> listMyObject = new List<MyObject>();
listMyObject.Add(new MyObject() { Handle = 1, Name = "FirstName" });
listMyObject.Add(new MyObject() { Handle = 2, Name = "SecondName" });
listMyObject.Add(new MyObject() { Handle = 3, Name = "ThirdName" });
List<MyObjectExtension> listMyObjectExtensions = new List<MyObjectExtension>();
listMyObjectExtensions.Add(new MyObjectExtension() { Handle = 1, Description = "FirstDescription" });
listMyObjectExtensions.Add(new MyObjectExtension() { Handle = 2, Description = "SecondDescription" });
listMyObjectExtensions.Add(new MyObjectExtension() { Handle = 3, Description = "ThirdDescription" });
IEnumerable<MyObject> MyObjects = listMyObject.AsEnumerable<MyObject>();
IEnumerable<MyObjectExtension> MyObjectExtensions = listMyObjectExtensions.AsEnumerable<MyObjectExtension>();
IEnumerable<MyRichObject> MyRichObjects;
MyRichObjects = from myObject in MyObjects
join myObjectExtension in MyObjectExtensions on myObject.Handle equals myObjectExtension.Handle
select new MyRichObject { Handle = myObject.Handle, Name = myObject.Name, Description = myObjectExtension.Description };
foreach (var MyRichObject in MyRichObjects)
{
System.Diagnostics.Debug.WriteLine($"Id: \"{MyRichObject.Handle}\". Name: {MyRichObject.Name} Description: {MyRichObject.Description}");
}
Return:
Id: "1". Name: FirstName Description: FirstDescription
Id: "2". Name: SecondName Description: SecondDescription
Id: "3". Name: ThirdName Description: ThirdDescription