我对EF中的导航属性及其初始化存在疑问。我创建了一个小案例来澄清我要处理的问题。抱歉,我的帖子太长了,但我想明确一点
.
在我的InMemoryRepository类下,我创建了2个列表,分别是“ Tickets”和“ TicketResponses”。静态方法“种子”填充了这两个列表。您会注意到,我没有初始化对象“ t1”的“响应”集合。此外,您可以找到查找方法“ ReadTicket”,该方法根据其ID返回票证;方法“ ReadTicketRepsonse”,也根据其ID返回票证响应。
在我的TicketManager类下,我有一个InMemoryRepository类的实例,并且还定义了两个方法,即“ GetTicket”和“ GetTicketResponse”。两种方法都使用InMemoryRepository的实例根据ID返回票证或票证响应。
在我的Program类下,我创建了TicketManager类的实例。我正在使用该实例来获取ID = 1的票证。从该票证中询问姓名,并且还要对票证中的“响应”集合进行计数。后者将提供System.NullReferenceException,因为我在使用“种子”方法创建票证时并未初始化“响应”集合。
现在,我将执行相同的操作,但是将使用实体框架,而不是在内存列表中使用。
我创建了一个DbContext(EfTestPartFourDbContext),其中有2个DbSet(Tickets和TicketResponses)。此外,我有一个EfTestPartFoutDbInitializer类,其中包含一个“ Seed”方法,EfTestPartFourDbContext类用于填充其DbSet(仅用于测试)。
我创建了一个与InMemoryRepository完全相同的Repository类,不同的是它不再具有2个列表(Tickets和TicketResponses),而是一个EfTestPartFourDbContext对象,可在“ ReadTicket”和“ ReadTicketResponse”方法中使用。此外,不再存在“种子”方法,因为播种现在由EfTestPartFourDbInitializer类完成。
现在,TicketManager类不再使用InMemoryRepository的实例,而是使用Repository类的实例。
我的程序类也保持不变。当您运行它时,当我对故障单中的“响应”集合进行计数时,我不再获得System.NullReferenceException。
所以现在我的问题是:为什么这不抛出NullReferenceExcption?我也没有在播种期间初始化票证的“回复”集合。 EF是否以某种方式初始化了它的导航属性?
非常感谢你!
在我的InMemoryRepository类下,我创建了2个列表,分别是“ Tickets”和“ TicketResponses”。静态方法“种子”填充了这两个列表。您会注意到,我没有初始化对象“ t1”的“响应”集合。此外,您可以找到查找方法“ ReadTicket”,该方法根据其ID返回票证;方法“ ReadTicketRepsonse”,也根据其ID返回票证响应。
InMemoryRepository:
public class InMemoryRepository : IRepository
{
private static readonly List<Ticket> Tickets = new List<Ticket>();
private static readonly List<TicketResponse> TicketResponses = new List<TicketResponse>();
static InMemoryRepository()
{
Seed();
}
public Ticket ReadTicket(int tickedId)
{
return Tickets.Find(t => t.TicketId == tickedId);
}
public TicketResponse ReadTicketResponse(int ticketResponseId)
{
return TicketResponses.Find(tr => tr.TicketResponseId == ticketResponseId);
}
private static void Seed()
{
Ticket t1 = new Ticket()
{
TicketId = 1,
Name = "Ticket 1"
};
Tickets.Add(t1);
TicketResponse tr1 = new TicketResponse()
{
TicketResponseId = 1,
Name = "Ticket response 1",
Ticket = t1
};
TicketResponses.Add(tr1);
}
}
在我的TicketManager类下,我有一个InMemoryRepository类的实例,并且还定义了两个方法,即“ GetTicket”和“ GetTicketResponse”。两种方法都使用InMemoryRepository的实例根据ID返回票证或票证响应。
TicketManager:
public class TicketManager : ITicketManager
{
private readonly IRepository _repository = new InMemoryRepository();
public Ticket GetTicket(int ticketId)
{
return _repository.ReadTicket(ticketId);
}
public TicketResponse GetTicketResponse(int ticketResponseId)
{
return _repository.ReadTicketResponse(ticketResponseId);
}
}
在我的Program类下,我创建了TicketManager类的实例。我正在使用该实例来获取ID = 1的票证。从该票证中询问姓名,并且还要对票证中的“响应”集合进行计数。后者将提供System.NullReferenceException,因为我在使用“种子”方法创建票证时并未初始化“响应”集合。
Program:
class Program
{
private static readonly ITicketManager TicketManager = new TicketManager();
static void Main(string[] args)
{
Ticket ticket = TicketManager.GetTicket(1);
Console.WriteLine(ticket.Name);
Console.WriteLine(ticket.Responses.Count);
}
}
现在,我将执行相同的操作,但是将使用实体框架,而不是在内存列表中使用。
我创建了一个DbContext(EfTestPartFourDbContext),其中有2个DbSet(Tickets和TicketResponses)。此外,我有一个EfTestPartFoutDbInitializer类,其中包含一个“ Seed”方法,EfTestPartFourDbContext类用于填充其DbSet(仅用于测试)。
EfTestPartFourDbContext:
internal class EfTestPartFourDbContext : DbContext
{
public DbSet<Ticket> Tickets { get; set; }
public DbSet<TicketResponse> TicketResponses { get; set; }
public EfTestPartFourDbContext()
{
EfTestPartFourDbInitializer.Initialize(this, true);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlite("Data Source = EfTestPartFour.db")
.UseLoggerFactory(LoggerFactory.Create(b => b.AddDebug()));
}
}
EfTestPartFourDbInitializer:
internal static class EfTestPartFourDbInitializer
{
public static void Initialize(EfTestPartFourDbContext context, bool dropCreateDatabase)
{
if (dropCreateDatabase)
{
context.Database.EnsureDeleted();
}
if (context.Database.EnsureCreated())
{
Seed(context);
}
}
private static void Seed(EfTestPartFourDbContext context)
{
Ticket t1 = new Ticket()
{
TicketId = 1,
Name = "Ticket 1"
};
context.Tickets.Add(t1);
TicketResponse tr1 = new TicketResponse()
{
TicketResponseId = 1,
Name = "Ticket response 1",
Ticket = t1
};
context.TicketResponses.Add(tr1);
context.SaveChanges();
foreach (var entry in context.ChangeTracker.Entries())
{
entry.State = EntityState.Detached;
}
}
}
我创建了一个与InMemoryRepository完全相同的Repository类,不同的是它不再具有2个列表(Tickets和TicketResponses),而是一个EfTestPartFourDbContext对象,可在“ ReadTicket”和“ ReadTicketResponse”方法中使用。此外,不再存在“种子”方法,因为播种现在由EfTestPartFourDbInitializer类完成。
Repository:
public class Repository : IRepository
{
private readonly EfTestPartFourDbContext _context = new EfTestPartFourDbContext();
public Ticket ReadTicket(int tickedId)
{
return _context.Tickets.Include(t=>t.Responses).FirstOrDefault(t=>t.TicketId == tickedId);
}
public IEnumerable<TicketResponse> ReadResponsesOfTicket(int tickedId)
{
return _context.TicketResponses.Where(tr => tr.Ticket.TicketId == tickedId);
}
public TicketResponse ReadTicketResponse(int ticketResponseId)
{
return _context.TicketResponses.Find(ticketResponseId);
}
public IEnumerable<TicketResponse> ReadTicketResponses()
{
return _context.TicketResponses;
}
}
现在,TicketManager类不再使用InMemoryRepository的实例,而是使用Repository类的实例。
TicketManager:
public class TicketManager : ITicketManager
{
private readonly IRepository _repository = new Repository();
public Ticket GetTicket(int ticketId)
{
return _repository.ReadTicket(ticketId);
}
public TicketResponse GetTicketResponse(int ticketResponseId)
{
return _repository.ReadTicketResponse(ticketResponseId);
}
}
我的程序类也保持不变。当您运行它时,当我对故障单中的“响应”集合进行计数时,我不再获得System.NullReferenceException。
所以现在我的问题是:为什么这不抛出NullReferenceExcption?我也没有在播种期间初始化票证的“回复”集合。 EF是否以某种方式初始化了它的导航属性?
非常感谢你!