【问题标题】:Automapper nested mappings along with projectionAutomapper 嵌套映射和投影
【发布时间】:2017-12-21 10:11:13
【问题描述】:

关于我的博客数据模型:

public class Blog
{
    public int Id { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Post> Posts { get; set; }
}

public class Post
{
    public int Id { get; set; }
    public string Text { get; set; }
    public int BlogId { get; set; }

    public virtual Blog Blog { get; set; }
}

另外,相应的 DTO 也是这样定义的:

public class BlogDto
{
    public int Id { get; set; }
    public string Name { get; set; }

    public ICollection<PostDto> Posts { get; set; }
}

public class PostDto
{
    public int Id { get; set; }
    public string Text { get; set; }
    public int BlogId { get; set; }

    public BlogDto Blog { get; set; }
}

然后,映射被初始化:

Mapper.Initialize(cfg =>
        {
            cfg.CreateMap<Blog, BlogDto>();
            cfg.CreateMap<Post, PostDto>();
        });

最后,我尝试使用 Automapper.EF6 将 Posts 投影到 PostDtos:

List<PostDto> result = null;
using (var db = new BloggingContext())
{
    result = db.Set<Post>().ProjectToList<PostDto>();
}

但是我遇到了这个错误:

The type 'AutoMapperSample.Model.PostDto' appears in two structurally
incompatible initializations within a single LINQ to Entities query. A type
can be initialized in two places in the same query, but only if the same
properties are set in both places and those properties are set in the same
order.

这是生成的表达式:

.Call System.Linq.Queryable.Select(
.Call 

.Constant<System.Data.Entity.Core.Objects.ObjectQuery`1[AutoMapperSample.Model.Post]>(System.Data.Entity.Core.Objects.ObjectQuery`1[AutoMapperSample.Model.Post]).MergeAs(.Constant<System.Data.Entity.Core.Objects.MergeOption>(AppendOnly))
    ,
    '(.Lambda #Lambda1<System.Func`2[AutoMapperSample.Model.Post,AutoMapperSample.Model.PostDto]>))

.Lambda #Lambda1<System.Func`2[AutoMapperSample.Model.Post,AutoMapperSample.Model.PostDto]>(AutoMapperSample.Model.Post $dto)
{
    .New AutoMapperSample.Model.PostDto(){
        Id = $dto.Id,
        Text = $dto.Text,
        Blog = .If ($dto.Blog != null) {
            .New AutoMapperSample.Model.BlogDto(){
                Id = ($dto.Blog).Id,
                Name = ($dto.Blog).Name,
                Posts = .Call System.Linq.Enumerable.ToList(.Call System.Linq.Enumerable.Select(
                        ($dto.Blog).Posts,
                        .Lambda #Lambda2<System.Func`2[AutoMapperSample.Model.Post,AutoMapperSample.Model.PostDto]>))
            }
        } .Else {
            null
        }
    }
}

.Lambda #Lambda2<System.Func`2[AutoMapperSample.Model.Post,AutoMapperSample.Model.PostDto]>(AutoMapperSample.Model.Post $dto)
{
    .New AutoMapperSample.Model.PostDto(){
        Id = $dto.Id,
        Text = $dto.Text
    }
}

我需要知道结构是否错误,例如循环,或者需要考虑其他问题。

【问题讨论】:

  • 嗯,可能解决不了PostDto.BlogBlogDto.Posts之间的循环引用。如果您.Ignore()CreateMap 中的这些成员之一,它是否有效?
  • @GeorgPatscheider 是的,如果我将 IgnoreMap 属性放在 BlogDto.Posts 上,或者如果我使用 MaxDepth(1),它会起作用。但我认为应该有一种适当的方法来保留这两个导航属性。另外,我尝试了 PreserveReferences 方法,又遇到了同样的错误。

标签: entity-framework automapper


【解决方案1】:

EF 6 的投影有两个问题

1- 您不能将 Set 投射到 Customer 本身。

例如

public class Customer
{
     public int Id {get;set;}
     public string Name {get;set;}
     [NotMapped]
     public int OrdersCount {get;set;}
     public List<Order> Orders {get;set;}
}

List<Customer> customers = await DbContext.Customers.Select(cust => 
     new Customer
     {
          Id = cust.Id,
          Name = cust.Name,
          OrdersCount = cust.Orders.Count
     }).ToListAsync(); // error!

2- 你不能同时投射双方的关系


public class Customer // as like as previous example

public class Order
{
     public int Id {get;set;}
     public int CustomerId {get;set;}
     public Customer Customer {get;set;}
}

List<CustomerDto> customers = await DbContext.Customers.Select(cust => 
     new CustomerDto
     {
          Id = cust.Id,
          Name = cust.Name,
          OrdersCount = cust.Orders.Count,
          Orders = cust.Orders.Select(ord => new OrderDto {
               Id = ord.Id,
               CustomerId = ord.CustomerId,
               Customer = new CustomerDto { ... }
          }).ToList()
     }).ToListAsync(); // error!

第一个问题无法解决,但对于第二个问题,您可以编写一个忽略关联的一侧的解决方法(例如,在客户查询中返回带有订单的客户,但在订单查询中不会返回客户!)

bool MapperPropConfigurationCondition(PropertyMap p)
            {
                return (p.DestinationMember.GetCustomAttribute<ForeignKeyAttribute>() != null || p.DestinationMember.GetCustomAttribute<InversePropertyAttribute>() != null)
                       && !typeof(IEnumerable).IsAssignableFrom(p.DestinationMember.ReflectedType)
                       && typeof(IDto).IsAssignableFrom(p.DestinationMember.ReflectedType);
            }

            mapperConfigExpression.ForAllPropertyMaps(MapperPropConfigurationCondition, (p, member) =>
            {
                p.Ignored = true;
            });

最终解决方案:迁移到 EF Core 5,它拥有您需要的一切,一切顺利! ;D

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-17
    • 2017-07-20
    • 2020-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多