我正在尝试通过实现扩展方法.ToViewModel()
来简化AutoMapper在我的项目中的使用。基本上,它只是标准调用的一个包装器,但我经常发现自己每次想要映射的东西时都必须键入多少。比较两者:
var viewModel = Mapper.Map<DomainEntityType, ViewModelType>(entity);
// or...
var viewModel = entity.ToViewModel();
我觉得第二个是甜蜜的=)我让所有实体扩展IEntity
,所有视图模型(对应一个实体)扩展IViewModel<IEntity>
,并编写以下扩展方法:
public static IViewModel<TEntity> ToViewModel<TEntity>(this TEntity entity)
where TEntity : IEntity
{
return Mapper.Map<TEntity, IViewModel<TEntity>>(entity);
}
然而,我无法做到这一点。
以下NUnit测试试图对此进行测试(虽然我不确定Assert.AreEqual
是否真的测试了我想要的东西 - 它是否需要引用相等?如果是这样,我如何测试“等效”?) 。测试失败并显示消息
Expected: <Castle.Proxies.IViewModel`1Proxy> But was: <Castle.Proxies.IViewModel`1Proxy>
[Test]
public void DomainEntityTypeMapsViewModelType()
{
var entity = new DomainEntityType();
var oldskool = Mapper.Map<DomainEntityType, IViewModel<Entity>>(entity);
var extension = inspectionObject.ToViewModel();
Assert.AreEqual(oldskool, extension);
}
除了我的测试可能测试错误的事实,我是否遗漏了关于AutoMapper如何工作的基本信息? AutoMapper是否能够正确地将某些内容映射到界面? (我确实有一个实现EntityViewModel
的类ViewModel<Entity>
,但我还没有具体告诉AutoMapper它......)
答案 0 :(得分:2)
我终于以通用的方式做到了这一点。它需要对我的对象进行一些调整,但这就是我所做的:
从那时起,我的大多数实体都继承自Entity<TId>
(通常为Entity<int>
,但id可能是另一种数据类型,例如GUID),后者又实现接口{{1 (具有单个属性IEntity<TId>
)。少数几个不继承public TId ID {get;}
的实体,至少实现Entity<TId>
。
我创建了一个新类,它覆盖了IEntity<TId>
和Equals
:
GetHashcode
现在,我的所有viewmodel(和editmodels)都继承自这个类:
public class ViewModel<TEntity, TId> : IEntity<TId>
where TEntity : IEntity<TId>
{
public TId ID { get; set; }
public override bool Equals(object obj)
{
var viewModel = obj as ViewModel<TEntity, TId>;
return viewModel != null && Equals(viewModel);
}
public bool Equals(ViewModel<TEntity, TId> other)
{
return ID.Equals(other.ID);
}
public override int GetHashCode()
{
// Not only returning ID.GetHashCode() in case I want to add more
// properties later...
var hash = 7;
hash = (hash * 17) + ID.GetHashCode();
return hash;
}
}
然后,我可以使用以下扩展方法扩展我的实体和视图模型:
public class EntityViewModel : ViewModel<EntityType, int>
{
// data properties
}
使这项工作的一个关键概念是放弃public static TViewModel To<TViewModel>(this IEntity entity) where TViewModel : class
{
return Mapper.Map(entity, entity.GetType(), typeof(TViewModel)) as TViewModel;
}
public static TEntity ToEntity<TEntity, TId>(this ViewModel<TEntity, TId> viewmodel) where TEntity : class, IEntity<TId>
{
return Mapper.Map(viewmodel, viewmodel.GetType(), typeof(TEntity)) as TEntity;
}
的泛型重载,因为我不知道我想在编译时映射到的确切类型。
我现在可以使用以下语法在类型之间来回移动:
.Map
我非常满意,因为我最初的目标是从控制器中的实现中抽象出AutoMapper已经很好地实现了。
您会注意到 IEntity 的非通用版本 - 这只是一个空接口,var entity = new EntityType();
var viewmodel = entity.To<EntityViewModel>();
var backagain = viewmodel.ToEntity();
继承了该接口。这可能被认为是枯燥的,但有一个简单的理由不使用泛型版本:如果这样做,您还必须将其指定为扩展方法的类型参数。你最终会得到IEntity<T>
而不是上面的语法,因为推断类型参数是一个全有或全无的事情。