在MVC3中使用反射双向自动映射所有域实体到视图模型



我一直在玩和学习ASP。. Net MVC 3,我一直在使用AutoMapper来映射我的域的实体和我的视图模型。

我厌倦了为我实现的每个ViewModel单独创建一个地图。因此,我编写了一些代码来扫描程序集,并使用一些反射来创建每个所需的映射。然而,因为我不是很熟悉使用AutoMappers的最佳实践,我想我应该向每个人展示我所做的工作,并询问我的方法是否有可能反过来伤害我。

基本上我有一个类称为AutoMappingConfigurator(在Global.asax.cs中使用)如下:

public static class AutoMappingConfigurator
    {
        public static void Configure(Assembly assembly)
        {
            var autoMappingTypePairingList = new List<AutoMappingTypePairing>();
            foreach (Type t in assembly.GetTypes())
            {
                var autoMapAttribute = t
                    .GetCustomAttributes(typeof(AutoMapAttribute), true)
                    .OfType<AutoMapAttribute>()
                    .FirstOrDefault();
                if (autoMapAttribute != null)
                {
                    autoMappingTypePairingList
                .Add(new AutoMappingTypePairing(autoMapAttribute.SourceType, t));
                }
            }
            autoMappingTypePairingList
               .ForEach(mappingPair => mappingPair.CreateBidirectionalMap());
        }
    }

本质上,它所做的是扫描程序集,查找已标记为AutoMapAttribute的所有类型,并为找到的每个类型创建一个双向映射。

AutoMapAttribute是我创建的一个简单属性(基于我在网上找到的示例),我将其附加到我的ViewModel上,以指示它映射到哪个域实体。

例如

[AutoMap(typeof(Project))]
public class ProjectDetailsViewModel
{
    public int ProjectId { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}

关于双向映射,到目前为止,在我与MVC3的工作中,我发现我似乎经常需要从实体映射到HttpGet的ViewModel,从ViewModel映射到HttpPost的实体。

双向映射作为扩展方法实现,如下所示:

public static void CreateBidirectionalMap(this AutoMappingTypePairing mappingPair)
{
    Mapper.CreateMap(mappingPair.SourceType, mappingPair.DestinationType)
          .IgnoreProperties(mappingPair.DestinationType);
    Mapper.CreateMap(mappingPair.DestinationType, mappingPair.SourceType)
          .IgnoreProperties(mappingPair.SourceType);
}

关于IgnoreProperties扩展方法,我发现每当我有一个视图模型,我想忽略的属性(比如当我的视图模型有一个下拉列表,不是底层域实体的一部分),我似乎必须通过ForMember AutoMapper方法手动创建忽略。所以我创建了另一个属性来指示哪些属性被忽略,这样我在AutoMappingConfigurator中的反射代码就可以自动为我做这件事。

IgnoreProperties扩展方法作为扩展方法实现如下:

public static IMappingExpression IgnoreProperties(this IMappingExpression expression
                                                  , Type mappingType)
{
    var propertiesWithAutoMapIgnoreAttribute =
        mappingType.GetProperties()
            .Where(p => p.GetCustomAttributes(typeof(AutoMapIgnoreAttribute), true)
                         .OfType<AutoMapIgnoreAttribute>()
                         .Count() > 0);
    foreach (var property in propertiesWithAutoMapIgnoreAttribute)
    {
        expression.ForMember(property.Name, opt => opt.Ignore());
    }
    return expression;
}

所有这些都允许我按照如下方式编写ViewModel并将其自动映射:

[AutoMap(typeof(EntityClass))]
private class ViewModelClass
{
    public int EntityClassId { get; set; }
    [AutoMapIgnore]
    public IEnumerable<SelectListItem> DropDownItems { get; set; }
}
private class EntityClass
{
    public int EntityClassId { get; set; }
}

虽然到目前为止这对我来说很有效,但我担心它可能会反过来咬我一口,因为我对AutoMapper的经验水平很低。

我的问题是:

  • 这是设置AutoMapper来配置我的映射的好方法吗我的领域实体和视图模型之间?
  • 是否有一些关于AutoMapper的东西,我可能会错过,将使这是一个糟糕的方法?
  • 正在连接属性忽略反射和属性是好的主意吗?
  • 是创建我的实体和ViewModel之间的双向映射一个好主意吗?

我更喜欢单向视图模型。换句话说,当我向用户呈现数据时,我使用一个视图模型,当我处理创建时,我使用另一个视图模型(另一个用于更新等等)。

为真,这样你会得到更多的对象。这样做的好处是可以避免不需要的视图模型属性(因此不需要忽略它们)。我认为视图模型应该总是尽可能简单(普通的get/set属性,如果你有一个需要初始化的列表,也许是一个构造函数)。如果您担心对象的"公共属性"的名称和数据类型,您可以(尽管我认为您不应该)在接口或基类中定义这些。

如果您想在两个视图模型中使用特定的域模型属性,则域模型中的AutoMapIgnore属性是有问题的。我认为这也适用于你的解决方案。

最后,我看不出使用属性而不是像

这样的代码行有什么好处。
Mapper.CreateMap<SourceType, DestinationType>();

还能更简单吗?当您从视图模型映射到模型时,使用一个扩展方法忽略"未映射属性"可能是一个好主意,允许您编写

Mapper.CreateMap<SourceType, DestinationType>().IgnoreUnmappedProperties();

在我看来这比使用AutoMapIgnore属性更容易。

我看不出你的方法有什么不妥,我来回答你的问题:

  • 在我看来,我相信你设置这个的方式是一种很好的方式,可以在你的模型/dto和实体之间创建乏味的映射。
  • 我不知道关于AutoMapper的任何事情会使这成为一个糟糕的方法。
  • 使用属性来连接属性忽略是一个好主意,属性只是在MVC中使用。
  • 双向映射在大多数情况下听起来是个好主意,但我很好奇它如何与自定义映射一起工作。

一些需要考虑的事情:

  1. 它如何处理嵌套映射?
  2. 它如何处理自定义映射?
    • 可能属性呢?
  3. 双向映射vs用属性映射每一侧
    • 哪个更清晰?
    • 哪一个处理自定义/嵌套映射更好?
  4. 性能是否受到影响?
    • 很可能不会,但在使用反射时可能需要注意。

如果你的模型和实体都有基类,你可以这样做:

        var entityAssembly = typeof(BaseEntity).Assembly;
        var modelAssembly = typeof(BaseModel).Assembly;
        var modelNamespace = modelAssembly.GetTypes().Where(a => a.BaseType == typeof(BaseModel)).FirstOrDefault().Namespace;
        foreach (var entity in entityAssembly.GetTypes().Where(a=> a.BaseType == typeof(BaseEntity)))
        {
            var model = modelAssembly.GetType(String.Format("{0}.{1}{2}", modelNamespace, entity.Name, "Model"));
            if (model != null)
            {
                Mapper.CreateMap(entity, model);
                Mapper.CreateMap(model, entity);
            }
        }

这是对配置实现的双向约定,如果相应的模型不存在,那么它将跳过到下一个实体。它非常简单,但是是手动映射的另一种选择。

NB。这就假定模型和实体名称遵循某种常规命名。例如,

{EntityName}Model eq. Branch to BranchModel

希望有帮助。请注意,这是一个真正的基本实现。如果不存在模型,则代码在第3行抛出错误。

相关内容

  • 没有找到相关文章

最新更新