[英]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查询技巧不适用于条件运算符,因为BillingAddress
和ShippingAddress
是导航属性,并且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.