У меня есть пара таблиц с отношением Account
ко многим Attribute
.Таблица Account
содержит несколько наборов записей из дискретных источников, и они имеют отношения через различные стратегии, включающие связанные с ними Attribute
s.Ожидаемый ввод будет случайным Attribute.Name
значениями из одной или нескольких групп Account
, я выполню необходимые объединения обратно в ту же таблицу.
Во время выполнения я хочу итеративно сгенерировать запрос сдеревья выражения.У меня проблема в том, что тип сущностей меняется и постоянно расширяется (все свойства являются строковыми), поэтому я не знаю, как к этому подойти.Построение запроса в SQL тривиально, в аналогичных задачах, использующих Dapper
с необработанными операторами SQL, я использую универсальный тип сущности со свойствами с именами от p0
до p100
и использую отражение и / или AutoMapper
для получения конечного требуемогоgraph.
Для данного Attribute.Name
может быть несколько значений, поэтому я ожидаю увидеть последующие наборы результатов для этого экземпляра значения (т. е. одна и та же учетная запись работает для двух компаний).
Чем минимальный рабочий пример отличается от фактического
В рабочей модели я не буду знать атрибуты, необходимые до времени выполнения.Я жестко запрограммировал «giveName», «sn» и «company», чтобы репро скомпилировалось.
В рабочей модели я добавлю предикат для первого 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
};
}
}
}