Построение динамического запроса LINQ с несколькими объединениями и неизвестными выборами - PullRequest
0 голосов
/ 22 декабря 2018

У меня есть пара таблиц с отношением Account ко многим Attribute.Таблица Account содержит несколько наборов записей из дискретных источников, и они имеют отношения через различные стратегии, включающие связанные с ними Attribute s.Ожидаемый ввод будет случайным Attribute.Name значениями из одной или нескольких групп Account, я выполню необходимые объединения обратно в ту же таблицу.

Во время выполнения я хочу итеративно сгенерировать запрос сдеревья выражения.У меня проблема в том, что тип сущностей меняется и постоянно расширяется (все свойства являются строковыми), поэтому я не знаю, как к этому подойти.Построение запроса в SQL тривиально, в аналогичных задачах, использующих Dapper с необработанными операторами SQL, я использую универсальный тип сущности со свойствами с именами от p0 до p100 и использую отражение и / или AutoMapper для получения конечного требуемогоgraph.

Для данного Attribute.Name может быть несколько значений, поэтому я ожидаю увидеть последующие наборы результатов для этого экземпляра значения (т. е. одна и та же учетная запись работает для двух компаний).

Чем минимальный рабочий пример отличается от фактического

  1. В рабочей модели я не буду знать атрибуты, необходимые до времени выполнения.Я жестко запрограммировал «giveName», «sn» и «company», чтобы репро скомпилировалось.

  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
                };
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...