使用多个联接和未知选择构造动态LINQ查询

时间:2018-12-22 16:02:09

标签: c# linq entity-framework-core

我有一对表,它们之间有一个Account到许多Attribute的关系。 Account表包含来自不同来源的几组条目,并且它们通过涉及其相关Attribute的各种策略具有关联。预期输入将是来自Attribute.Name组中一个或多个Account组的随机Dapper值,我将执行所需的联接返回同一表。

在运行时,我想用表达式树迭代生成查询。我的问题是实体类型发生变化并不断扩展(所有属性都是字符串),因此我不知道该如何处理。在SQL中构建查询是微不足道的,在使用p0和原始SQL语句的类似问题中,我使用属性为p100AutoMapper的通用实体类型,并使用反射和/或{{ 1}},以获得最终所需的图形。

对于给定的Attribute.Name,可能会有多个值,因此我希望看到该值实例的后续结果集(即,同一帐户可用于两家公司)。

最小工作示例与实际示例有何不同

  1. 在工作模型中,直到运行时我才知道所需的属性。我已经硬编码了“ giveName”,“ sn”和“ company”,以便repro可以编译。

  2. 在工作模型中,我将在第一个Account上下文实例上添加谓词,以仅过滤具有Attribute且具有给定名称和值的帐户。

  3. 在工作模型中,我将添加Account上下文的另一个实例,以基于具有给定名称和值的Attribute查找上面未包括的帐户。然后,我将其加入到初始查询中。

  4. 在工作模型中,如果用户要求特定的属性,我可以使用另一组离散帐户重复上述联接。

我不清楚如何定义查询返回类型,以便它可以不断扩展范围。返回类型为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
                };
        }
    }
}

0 个答案:

没有答案