Допустим, я хочу иметь класс A
со многими B
со многими A
; Я могу добиться этого, создав класс AB
, добавив поле ICollection<AB> ABs
в классы A
и B
, а затем получив доступ к B
через ABs
свойство класса A
. И это работает. Но мне было интересно, есть ли способ получить доступ к связанным данным B
непосредственно из класса A
, а не через свойство ABs
.
Я бы представил себе несколько способов сделать это (ни один из которых мне не удалось приступить к работе):
public ICollection<B> Bs => this.ABs.Select(item => item.B).ToList();
, но это не так, у меня есть нулевое исключение, хотя я включаюABs
иBs
с этимcontext.As.Include(item => item.ABs).ThenInclude(item => item.B);
.свободный API ядра ef (не знаю, как это сделать)
using Microsoft.EntityFrameworkCore;
using System;
using Context;
namespace ConsoleApp1 {
class Program
{
static void Main(string[] args)
{
using var context = new ZContext();
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var A = context.As.Include(item => item.ABs).ThenInclude(item => item.B);
foreach (var a in A) {
Console.WriteLine(a.Bs);
}
return;
}
}
}
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Context
{
public class IDed
{
#region Constructors
public IDed()
{
ID = Guid.NewGuid();
}
#endregion
public Guid ID { get; set; }
}
public class A : IDed
{
public ICollection<AB> ABs { get; set; }
public ICollection<B> Bs {
get {
return ABs.Select(item => item.B).ToList();
}
}
}
public class B : IDed
{
public ICollection<AB> ABs { get; set; }
}
public class AB
{
#region Constructors
public AB()
{
}
public AB(A a, B b)
{
this.AID = a.ID;
this.BID = b.ID;
}
#endregion
public A A { get; set; } public Guid AID { get; set; }
public B B { get; set; } public Guid BID { get; set; }
}
public class ZContext : DbContext
{
public DbSet<A> As { get; set; }
public DbSet<B> Bs { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Data Source=111.db3");
}
protected override void OnModelCreating(ModelBuilder mb)
{
#region Keys
mb.Entity<AB>().HasKey(item => new { item.AID, item.BID });
#endregion
#region Relations
// ???
#endregion
var a1 = new A();
var a2 = new A();
mb.Entity<A>().HasData(a1, a2);
var b1 = new B();
var b2 = new B();
mb.Entity<B>().HasData(b1, b2);
mb.Entity<AB>().HasData(
new AB(a1, b1),
new AB(a1, b2),
new AB(a2, b1),
new AB(a2, b2)
);
}
}
}
С 1-м методом я получаю ошибку System.ArgumentNullException: 'Value cannot be null. (Parameter 'source')'
, хотя я Include
и ThenInclude
все те же поля.
Я понимаю, что класс AB
должен будет остаться в любом случае, но можно ли добиться такой конфигурации и как это сделать правильно.
1 ответ
Исключение вызвано выводом EF соглашений о сопоставлении по умолчанию. То есть при построении модели сущностей EF сталкивается со свойством A.Bs
и пытается найти для него сопоставление с базой данных. Именно в этот самый ранний момент возникает исключение. Как только вы это узнаете, исправить это легко: просто удалите свойство:
[NotMapped]
public ICollection<B> Bs
{
get
{
return ABs.Select(item => item.B).ToList();
}
}
Или же
protected override void OnModelCreating(ModelBuilder mb)
{
...
mb.Entity<A>().Ignore(a => a.Bs);
Конечно, даже без исключения свойство должно быть отменено, потому что вы не хотите, чтобы оно отражалось в ассоциации с базой данных.
Похожие вопросы
Новые вопросы
c#
C# (произносится как «see Sharp») — это высокоуровневый мультипарадигменный язык программирования со статической типизацией, разработанный Microsoft. Код C# обычно нацелен на семейство инструментов и сред выполнения Microsoft .NET, которое включает в себя .NET, .NET Framework, .NET MAUI и Xamarin среди прочих. Используйте этот тег для ответов на вопросы о коде, написанном на C#, или о формальной спецификации C#.