使用AutoMapper实现.ToViewModel()

时间:2011-07-01 11:45:49

标签: generics automapper

我正在尝试通过实现扩展方法.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它......)

1 个答案:

答案 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>而不是上面的语法,因为推断类型参数是一个全有或全无的事情。