用于将匿名类型投影到视图模型上的 Automapper 查询

Posted

技术标签:

【中文标题】用于将匿名类型投影到视图模型上的 Automapper 查询【英文标题】:Automapper query for projecting an anonymous type onto a viewmodel 【发布时间】:2015-04-21 02:36:27 【问题描述】:

我有一个非常基本的控制器,它执行 Linq to Entities 查询,我希望能够使用 AutoMapper 将结果投影到视图模型上 - 但是我收到了错误:

cannot convert from 'System.Collections.Generic.List<AnonymousType#1>' to 'System.Collections.Generic.IList<tb.Models.Tour>':

控制器:

var tours2 = (from t in db.Tours
                     join d in db.TourDates on t.TourId equals d.TourId
                     join c in db.TourCategories on t.TourCategoryId equals c.TourCategoryId
                     where d.Date == dte && t.TourCategoryId == id
                     select new
                    
                        Id = t.TourId,
                        TourName = t.TourName,
                        TourCategoryId = t.TourCategoryId,
                        Bookings = db.Bookings.Where(b => d.TourDateId == b.TourDateId).Count()                            
                    ).ToList();

        Mapper.CreateMap<IList<Tour>, IList<ToursAvail2VM>>();
        IList<ToursAvail2VM> toursvm = Mapper.Map<IList<Tour>, IList<ToursAvail2VM>>(tours2);

视图模型:

 public class ToursAvail2VM
 
    public int Id  get; set; 
    public int TourCategoryId  get; set; 
    public string TourName  get; set; 
    public int Bookings  get; set; 
 

如何将结果列表投射到我的 toursvm 类上?

谢谢你的建议,马克

【问题讨论】:

【参考方案1】:

对于这个例子,我会使用 AutoMapper 的 LINQ projection capabilities。

// This would be somewhere in initialization
Mapper.CreateMap<Tour, ToursAvail2VM>()
    .ForMember(d => d.Bookings, opt => opt.MapFrom(src => 
        src.TourDates.Sum(td => td.Bookings.Count()));

var toursvm = (from t in db.Tours
                 join d in db.TourDates on t.TourId equals d.TourId
                 join c in db.TourCategories on t.TourCategoryId equals c.TourCategoryId
                 where d.Date == dte && t.TourCategoryId == id
                )
              .Project().To<ToursAvail2VM>()
              .ToList();

我假设您在此处的模型中包含导航属性,以允许您直接从 Tour 导航到 TourDates 到 Bookings。

【讨论】:

我有一个没有导航属性的模型(CRM 数据库)。所以我最终唯一的结果总是一个匿名类型,其属性由它们的源表命名,如:new hrrm, hrec [human res.记录 mgmgt,hr 员工联系]。有没有一种优雅的方法可以在此类返回类型上使用带有自动映射器的投影?原因很简单:不想避免手动重复映射(其他人也会处理代码)。谢谢。【参考方案2】:

您的 linq 查询返回的是匿名类型,而不是 Tour。您需要明确指定要返回的类,否则 AutoMapper 无法处理。

但是,由于您使用的是 Linq to Entities,因此不能直接指定 ToursAvail2VM。您可以选择匿名类型,然后立即选择ToursAvail2VM,如下所示:

IList<ToursAvail2VM> toursvm =
                (from vm in
                    (from t in db.Tours
                     join d in db.TourDates on t.TourId equals d.TourId
                     join c in db.TourCategories on t.TourCategoryId equals c.TourCategoryId
                     where d.Date == dte && t.TourCategoryId == id
                     select new
                    
                        Id = t.TourId,
                        TourName = t.TourName,
                        TourCategoryId = t.TourCategoryId,
                        Bookings = db.Bookings.Where(b => d.TourDateId == b.TourDateId).Count()                            
                    ).ToList()
                select new ToursAvail2VM
                    
                         Id = vm.Id,
                         TourName = vm.TourName,
                         TourCategoryId = vm.TourCategoryId,
                         Bookings = vm.Bookings
                    ).ToList();

【讨论】:

嗨史蒂夫 - 感谢您的建议。如果我添加“选择新的 ToursAvail2VM” - 运行它时会出错,并显示以下消息:无法在 LINQ to Entities 查询中构造实体或复杂类型“tb.Models.ToursAvail2VM”。这就是我创建匿名类型的原因。再次感谢,马克 啊,我错过了 Linq to Entities 部分。这总是增加一点乐趣。已更新。 嗨,史蒂夫 - 再次感谢您。不幸的是,由于某种原因它仍然不喜欢它:The entity or complex type 'tb.Models.ToursAvail2VM' cannot be constructed in a LINQ to Entities query.我可能错过了大多数其他人真正基本的东西:) 谢谢! 呸。我认为你需要一个临时的 .ToList () 。当我回到电脑上时,我会仔细看看。 (在我的手机自动取款机上。) 一件小事 - 在这种情况下我会使用 AutoMapper 的 LINQ 投影,它摆脱了 Select 投影的东西。

以上是关于用于将匿名类型投影到视图模型上的 Automapper 查询的主要内容,如果未能解决你的问题,请参考以下文章

OPENGL若干重要基础概念

具有匿名类型模型类的剃刀视图。有可能的?

当类型为dynamic的视图模型遭遇匿名对象

模型视图投影 - 如何在世界中移动对象

OpenTK - 模型视图投影问题

剔除适用于从视图投影矩阵中提取平面,但不适用于投影矩阵