我有一对表,它们之间有一个Account
到许多Attribute
的关系。 Account
表包含来自不同来源的几组条目,并且它们通过涉及其相关Attribute
的各种策略具有关联。预期输入将是来自Attribute.Name
组中一个或多个Account
组的随机Dapper
值,我将执行所需的联接返回同一表。
在运行时,我想用表达式树迭代生成查询。我的问题是实体类型发生变化并不断扩展(所有属性都是字符串),因此我不知道该如何处理。在SQL中构建查询是微不足道的,在使用p0
和原始SQL语句的类似问题中,我使用属性为p100
到AutoMapper
的通用实体类型,并使用反射和/或{{ 1}},以获得最终所需的图形。
对于给定的Attribute.Name
,可能会有多个值,因此我希望看到该值实例的后续结果集(即,同一帐户可用于两家公司)。
最小工作示例与实际示例有何不同
在工作模型中,直到运行时我才知道所需的属性。我已经硬编码了“ giveName”,“ sn”和“ company”,以便repro可以编译。
在工作模型中,我将在第一个Account
上下文实例上添加谓词,以仅过滤具有Attribute
且具有给定名称和值的帐户。
在工作模型中,我将添加Account
上下文的另一个实例,以基于具有给定名称和值的Attribute
查找上面未包括的帐户。然后,我将其加入到初始查询中。
在工作模型中,如果用户要求特定的属性,我可以使用另一组离散帐户重复上述联接。
我不清楚如何定义查询返回类型,以便它可以不断扩展范围。返回类型为不和Account
,它以一个开始,但每个结果集将包含一个或多个相关帐户。
// Microsoft.EntityFrameworkCore 2.1.4
// Microsoft.EntityFrameworkCore.InMemory 2.1.4
// System.ComponentModel.Annotations 4.5.0
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using Microsoft.EntityFrameworkCore;
public class Account
{
public int Id { get; set; }
public string Name { get; set; }
public List<Attribute> Attributes { get; set; }
}
public class Attribute
{
public int Id { get; set; }
public string Name { get; set; }
public string Value { get; set; }
public Account Account { get; set; }
[ForeignKey(nameof(AccountId))]
public int AccountId { get; set; }
}
public class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options) : base(options) { }
public DbSet<Account> Accounts { get; set; }
public DbSet<Attribute> Attributes { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Account>()
.HasIndex(p => p.Name)
.IsUnique();
modelBuilder.Entity<Attribute>()
.HasOne(p => p.Account)
.WithMany(p => p.Attributes)
.IsRequired();
modelBuilder.Entity<Attribute>()
.HasIndex(p => p.Name);
modelBuilder.Entity<Attribute>()
.HasIndex(p => p.Value);
}
}
class Program
{
static void Main()
{
var options = new DbContextOptionsBuilder<MyContext>()
.UseInMemoryDatabase("db")
.Options;
using (var context = new MyContext(options))
{
context.Accounts.AddRange(
new Account
{
Name = "John Doe",
Attributes = new List<Attribute>
{
new Attribute { Name = "giveName", Value = "John" },
new Attribute { Name = "sn", Value = "Doe" },
new Attribute { Name = "company", Value = "My Company" },
}
}, new Account
{
Name = "Jane Doe",
Attributes = new List<Attribute>
{
new Attribute { Name = "giveName", Value = "Jane" },
new Attribute { Name = "sn", Value = "Doe" },
new Attribute { Name = "company", Value = "Another Company" },
}
});
context.SaveChanges();
}
using (var context = new MyContext(options))
{
var query =
from account in context.Accounts
join attr1 in context.Attributes
on new{ accountId = account.Id, name = "giveName" } equals new { accountId = attr1.AccountId, name = attr1.Name }
into attr1Grp
join attr2 in context.Attributes
on new { accountId = account.Id, name = "sn" } equals new { accountId = attr2.AccountId, name = attr2.Name }
into attr2Grp
join attr3 in context.Attributes
on new { accountId = account.Id, name = "company" } equals new { accountId = attr3.AccountId, name = attr3.Name }
into attr3Grp
from a1 in attr1Grp.DefaultIfEmpty()
from a2 in attr2Grp.DefaultIfEmpty()
from a3 in attr3Grp.DefaultIfEmpty()
select new
{
Account = account.Name,
GivenName = a1.Value,
SurName = a2.Value,
Company = a3.Value
};
}
}
}