【问题标题】:Simple Convention Automapper for two-way Mapping (Entities to/from ViewModels)用于双向映射的简单约定自动映射器(到/从 ViewModels 的实体)
【发布时间】:2010-12-19 09:31:54
【问题描述】:

更新:这些东西已经演变成一个不错的项目,请参阅http://valueinjecter.codeplex.com


看看这个,我刚刚写了一个简单的自动映射器,它从一个对象具有相同名称和类型的属性中获取值并将其放入另一个对象中,您可以为您可能需要的每种类型添加异常(ifs、switch)

那么告诉我你怎么看?

我这样做是为了做这样的事情:

Product –> ProductDTO

ProductDTO –> Product

就这样开始了:

我在 Inputs/Dto/ViewModels 中为 DropDowns 使用“对象”类型,因为我向 html 发送了一个 IEnumerable 并且我收到了一个返回的选定键的字符串数组

 public void Map(object a, object b)
    {
        var pp = a.GetType().GetProperties();
        foreach (var pa in pp)
        {
            var value = pa.GetValue(a, null);

            // property with the same name in b
            var pb = b.GetType().GetProperty(pa.Name);
            if (pb == null)
            {
                //no such property in b
                continue;
            }

            if (pa.PropertyType == pb.PropertyType)
            {
                pb.SetValue(b, value, null);
            }

        }
    }

更新: 实际用法:
构建方法(输入 = Dto):

        public static TI BuildInput<TI, T>(this T entity) where TI: class, new()
        {
            var input = new TI();
            input = Map(entity, input) as TI;
            return input;
        }

        public static T BuildEntity<T, TI, TR>(this TI input)
            where T : class, new()
            where TR : IBaseAdvanceService<T>
        {               
            var id = (long)input.GetType().GetProperty("Id").GetValue(input, null);
            var entity = LocatorConfigurator.Resolve<TR>().Get(id) ?? new T();
            entity = Map(input, entity) as T;
            return entity;
        }

        public static TI RebuildInput<T, TI, TR>(this TI input)
            where T: class, new()
            where TR : IBaseAdvanceService<T>
            where TI : class, new()
        {

                return input.BuildEntity<T, TI, TR>().BuildInput<TI, T>();
            }

在控制器中:

    public ActionResult Create()
    { 
        return View(new Organisation().BuildInput<OrganisationInput, Organisation>());
    }

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Create(OrganisationInput o)
    {
        if (!ModelState.IsValid)
        {
            return View(o.RebuildInput<Organisation,OrganisationInput, IOrganisationService>());                
        }
        organisationService.SaveOrUpdate(o.BuildEntity<Organisation, OrganisationInput, IOrganisationService>());
        return RedirectToAction("Index");
    }

真正的Map方法

public static object Map(object a, object b)
        {
            var lookups = GetLookups();

            var propertyInfos = a.GetType().GetProperties();
            foreach (var pa in propertyInfos)
            {
                var value = pa.GetValue(a, null);

                // property with the same name in b
                var pb = b.GetType().GetProperty(pa.Name);
                if (pb == null)
                {
                    continue;
                }

                if (pa.PropertyType == pb.PropertyType)
                {
                    pb.SetValue(b, value, null);
                }
                else if (lookups.Contains(pa.Name) && pa.PropertyType == typeof(LookupItem))
                {
                    pb.SetValue(b, (pa.GetValue(a, null) as LookupItem).GetSelectList(pa.Name), null);
                }
                else if (lookups.Contains(pa.Name) && pa.PropertyType == typeof(object))
                {
                    pb.SetValue(b, pa.GetValue(a, null).ReadSelectItemValue(), null);
                }
                else if (pa.PropertyType == typeof(long) && pb.PropertyType == typeof(Organisation))
                {
                    pb.SetValue(b, pa.GetValue<long>(a).ReadOrganisationId(), null);
                }
                else if (pa.PropertyType == typeof(Organisation) && pb.PropertyType == typeof(long))
                {
                    pb.SetValue(b, pa.GetValue<Organisation>(a).Id, null);
                }
            }

            return b;
        }

【问题讨论】:

  • 离题——我喜欢你对“问题”的开场白:“看看这个……”!哈哈。我想过这样做,但没有球
  • @cottsak 是的,开场真的很好 :D,从没见过这样的
  • @ChuckNorris - 您能否指出为什么不建议将 Automapper(最初)用于 ViewModel模型映射?我意识到这是本次讨论中的一个非常晚的评论,许多事情可能在 Automapper 和 ValueInjector 中都取得了进展,但尽管如此,如果您对为什么要避免使用 Automapper 有一些值得注意的观点,请告诉我们。
  • @FairDune 我这样做了 ^ 因为我的类很简单,我认为不需要为每一对编写配置,一个简单的约定就可以了
  • @ChuckNorris 谢谢。我同意。在为 ValueInjecter 帖子搜索 SO 之后,我得到了很多答案。我现在正在使用 ValueInjecter。

标签: c# asp.net-mvc mapping automapper automapping


【解决方案1】:

您可以添加例外(ifs、switch) 对于您可能需要的每种类型

你不会这样做的。严重地。作用于对象类型的 case 语句是糟糕的 OOP 风格。我的意思是,真的很糟糕。就像在肚子里喝着伏特加酒开车一样。它可能会工作一段时间,但最终你会遇到麻烦。

好的,Jimmy 告诉过你不要使用 AutoMapper……但我敢打赌他的意思是别的。现在,你有没有发明一些不同的东西——让吉米开心的东西? ;-) 不,您刚刚制作了自己的半卷 AutoMapper。吉米告诉你不要使用它! ;-)

所以这是我的建议:忽略 Jimmy 所说的,想想你自己.. 并使用 AutoMapper ;-)

【讨论】:

  • 嗯,是的,我可以,但是有了这些东西,我可以很容易地做我自己的约定;我目前在“Builder”类的 2 种方法中使用它:BuildEntity BuildInput,所以我自动将所有实体映射到所有输入(ViewModels),无需为每对类型指定一个映射(CreateMap)
  • 您可以使用 BuildEntity 方法在内部调用 AutoMapper CreateMap/Map。此外,您可以让它检查 CreateMap 是否已经注册,因此您可以覆盖默认映射 - 而不是硬编码到 BAD BAD 样式的开关中。我仍然看不到重新发明 AutoMapper 的理由。
  • 我已经更新了问题文本,你现在可以看到我是如何使用它的
【解决方案2】:

您可能想要添加的一件事是缓存反射位。如果您映射一个对象两次,您可能不想再次查找所有反射的东西。此外,GetValue 和 SetValue 之类的东西很慢,我改用后期绑定的委托 + Reflection.Emit 来加快速度。

【讨论】:

  • :) 好吧,我不会将特定类型映射到另一个类型,它更像是所有类型的每个属性映射
  • 我将研究如何加快速度,thanx 建议 Reflection.Emit 和后期绑定委托,不确定缓存
  • 缓存确实帮助了 AutoMapper - GetFields 和 GetProperties 调用可能是一个瓶颈 - 在 dotTrace 中运行它,您可以看到时间花在了哪里。这种类型的编码很酷,玩得开心!
  • thnx,缓存 GetProperties 是个好主意,我可以将每种类型的属性存储在缓存中,这样我就不需要每次都获取它们
【解决方案3】:

只需使用AutoMapper。这很好,但它会成长为一个迷你项目。

AM(真正的)做的一些事情是:

  • 报告您是否有无法映射到的属性
  • 展平对象
  • 提供钩子供您自定义某些方面,而不是在大的 switch 语句中
  • 出于性能原因而不是直接反射使用 Expression.Compile

但它肯定是一个有趣的空间,自动映射的想法当然很有用。

有点像1533 行与 NInject 或其朋友中的 DI - 很酷,但为什么呢?。

我认为您已经阅读了article and comments regarding 2 way mapping on Jimmy's blog

【讨论】:

  • 我打算用它来编辑实体(从我的模型映射到我的视图模型,反之亦然),我想为此使用自动映射器,但吉米告诉我不要:)
  • @Omu,很公平。但对我来说,重点是你表面上简单的“解决方案”隐藏了特殊情况的冰山,很酷,而且在完成它时,你最终会做大量的工作。 cottsak 提出的问题是你在做这个映射的目的是值得考虑的。但是你没有给我们太多关于你的真实场景是什么。据我们所知,您可能是理想的 .NET RIA 服务用户 :P Jimmy 是否说过不要向 AM 添加 2 路映射,或者他不打算这样做,因为他认为这不是一个有意义的用例?分叉可能比重新发明更好
  • 我已经更新了问题文本,你现在可以看看我是如何使用它的
  • 感谢您的链接,它(缺少 2 路映射)现在有意义了。
【解决方案4】:

只是一个想法:

如果抽象是如此容易地映射到被抽象的对象,那么人们可能想知道抽象的意义何在。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-24
    • 1970-01-01
    • 2014-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-13
    相关资源
    最近更新 更多