简体   繁体   English

使用AutoMapper在Entity Framework中嵌套查询

[英]Nested query in Entity Framework with AutoMapper

The use case is as follows: a customer account only ever has one address, either shipping or billing. 用例如下:一个客户帐户只有一个地址,即送货或帐单。 I need to merge this into one Address entity. 我需要将其合并为一个地址实体。 On top of this the address lines are separate columns in the database but have to be a IEnumerable of string in the DTO. 最重要的是,地址行是数据库中的单独列,但必须是DTO中字符串的IEnumerable。 All the while this must support ProjectTo() because OData must be implemented. 由于OData必须实现,因此始终都必须支持ProjectTo()。 How can I accomplish this without any database changes? 我如何在不更改数据库的情况下完成此任务? I can't touch it because of legacy applications. 由于遗留应用程序,我无法碰它。

The first code example works, while the second fails with: The nested query is not supported. Operation1='Case' Operation2='Collect' 第一个代码示例有效,而第二个代码示例却失败了: The nested query is not supported. Operation1='Case' Operation2='Collect' The nested query is not supported. Operation1='Case' Operation2='Collect' . The nested query is not supported. Operation1='Case' Operation2='Collect' Which is probably because it has to create sub entities. 这可能是因为它必须创建子实体。 How can I get it to work? 我如何使它工作? Is there any workaround (perhaps restructuring my entities)? 有什么解决方法(也许是重组我的实体)吗?

In this first example I add the mappings for the different address line columns to a List<string> . 在第一个示例中,我将不同地址线列的映射添加到List<string> Then when calling .Include() Entity Framework knows exactly what to do and it works. 然后,当调用.Include()时,实体框架完全知道该做什么并且可以工作。

public void Main()
{
    Mapper.Initialize(cfg =>
    {
        cfg.CreateMap<ShippingAddress, DtoShippingAddress>
            .ForMember(dto => dto.AddressLines,
                entity => entity.MapFrom(source => new List<string>
                    {
                        source.AddressLine1,
                        source.AddressLine2,
                        source.AddressLine3
                    }));

        cfg.CreateMap<BillingAddress, DtoBillingAddress>
            .ForMember(dto => dto.AddressLines,
                entity => entity.MapFrom(source => new List<string>
                    {
                        source.AddressLine1,
                        source.AddressLine2,
                        source.AddressLine3
                    }));
    });
    Mapper.AssertConfigurationIsValid();
    var context = new ClientContext();
    var result = context.CustomerAccounts.Include(i => i.ShippingAddress).Include(i => i.BillingAddress).ProjectTo<DtoCustomerAccount>();//.Dump();
}

public class CustomerAccount
{
    public int Id { get; set; }

    public ShippingAddress ShippingAddress { get; set; }

    public BillingAddress BillingAddress { get; set; }
}

public class ShippingAddress
{
    public int Id { get; set; }

    public string AddressLine1 { get; set; }

    public string AddressLine2 { get; set; }

    public string AddressLine3 { get; set; }
}

public class BillingAddress
{
    public int Id { get; set; }

    public string AddressLine1 { get; set; }

    public string AddressLine2 { get; set; }

    public string AddressLine3 { get; set; }
}

public class DtoCustomerAccount
{
    public int Id { get; set; }

    public DtoShippingAddress ShippingAddress { get; set; }

    public DtoBillingAddress BillingAddress { get; set; }
}

public class DtoShippingAddress
{
    public int Id { get; set; }

    public List<string> AddressLines { get; set; }
}

public class DtoBillingAddress
{
    public int Id { get; set; }

    public List<string> AddressLines { get; set; }
}

public class Initializer : DropCreateDatabaseAlways<ClientContext>
{       
    protected override void Seed(ClientContext context)
    {
        context.CustomerAccounts.Add(new UserQuery.CustomerAccount {ECommercePublished=true,  Articles = new []{ new Article{IsDefault=true, NationId=1, ProductId=1}}});
    }
}

public class ClientContext : DbContext
{
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
    Database.Log = s=>Debug.WriteLine(s);
        Database.SetInitializer(new Initializer());
    }

    public DbSet<CustomerAccount> CustomerAccounts { get; set; }
}

The way I need it implemented, which fails: 我需要它的实现方式,但失败了:

//The nested query is not supported. Operation1='Case' Operation2='Collect'
public void Main()
{
    Mapper.Initialize(cfg =>
    {
        cfg.CreateMap<CustomerAccount, DtoCustomerAccount>
            .ForMember(dto => dto.Address,
                entity => entity.MapFrom(source =>
                    source.BillingAddress == null
                        ? new Address
                        {
                            Id = source.ShippingAddress.Id,
                            AddressLines = new [] // or new List<string>
                            {
                                source.ShippingAddress.AddressLine1,
                                source.ShippingAddress.AddressLine2,
                                source.ShippingAddress.AddressLine3
                            }
                        }
                        : new Address
                        {
                            Id = source.BillingAddress.Id,
                            AddressLines = new [] // or new List<string>
                            {
                                source.BillingAddress.AddressLine1,
                                source.BillingAddress.AddressLine2,
                                source.BillingAddress.AddressLine3
                            }
                        }));
    });
    Mapper.AssertConfigurationIsValid();
    var context = new ClientContext();
    var result = context.CustomerAccounts.ProjectTo<DtoCustomerAccount>();//.Dump();
}

public class CustomerAccount
{
    public int Id { get; set; }

    public ShippingAddress ShippingAddress { get; set; }

    public BillingAddress BillingAddress { get; set; }
}

public class ShippingAddress
{
    public int Id { get; set; }

    public string AddressLine1 { get; set; }

    public string AddressLine2 { get; set; }

    public string AddressLine3 { get; set; }
}

public class BillingAddress
{
    public int Id { get; set; }

    public string AddressLine1 { get; set; }

    public string AddressLine2 { get; set; }

    public string AddressLine3 { get; set; }
}

public class DtoCustomerAccount
{
    public int Id { get; set; }

    public DtoAddress Address { get; set; }
}

public class DtoAddress
{
    public int Id { get; set; }

    public List<string> AddressLines { get; set; }
}

public class Initializer : DropCreateDatabaseAlways<ClientContext>
{       
    protected override void Seed(ClientContext context)
    {
        context.CustomerAccounts.Add(new UserQuery.CustomerAccount {ECommercePublished=true,  Articles = new []{ new Article{IsDefault=true, NationId=1, ProductId=1}}});
    }
}

public class ClientContext : DbContext
{
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
    Database.Log = s=>Debug.WriteLine(s);
        Database.SetInitializer(new Initializer());
    }

    public DbSet<CustomerAccount> CustomerAccounts { get; set; }
}

Technically it's not an AutoMapper issue, because ProjectTo simply uses the expression provided by you. 从技术上讲,这不是AutoMapper的问题,因为ProjectTo仅使用您提供的表达式。 And in this case the EF query trick with List<string> doesn't work with the conditional operator because BillingAddress and ShippingAddress are navigation properties and EF translator is trying to use sub queries which cannot be combined with conditional operator. 在这种情况下,带有List<string>的EF查询技巧不适用于条件运算符,因为BillingAddressShippingAddress是导航属性,并且EF转换程序试图使用无法与条件运算符组合的子查询。 Hence you need to find EF query compatible way of specifying the desired projection. 因此,您需要找到与EF查询兼容的方式来指定所需的投影。

One possible way is to use conditional operator for each resultant address property: 一种可能的方法是对每个结果地址属性使用条件运算符:

.ForMember(dto => dto.Address, entity => entity.MapFrom(source => new DtoAddress
{
    Id = source.BillingAddress != null ? source.BillingAddress.Id : source.ShippingAddress.Id,
    AddressLines = new List<string>
    {
        source.BillingAddress != null ? source.BillingAddress.AddressLine1 : source.ShippingAddress.AddressLine1,
        source.BillingAddress != null ? source.BillingAddress.AddressLine2 : source.ShippingAddress.AddressLine2,
        source.BillingAddress != null ? source.BillingAddress.AddressLine3 : source.ShippingAddress.AddressLine3,
    }   
}))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM