我应该如何禁用每个对象的Entity Framework表引用(外部)列表?

我正在使用Sqlite数据库System.Data.SQLite 1.0.92这里有2个表:


表人

PERSONID

PERSONNAME


表学生

学生卡

PersonId(参考表人FK)

StudentNo


现在每次我在EF5中获得Persons Collection:

using (var ctx = new myEntities) { AllPersons = ctx.Persons.ToList(); } 

还有AllPersons.student集合将包含在结果中;

但我不需要它。 当然这只是一个例子,有很多大表有这么多的引用,因此它总是存在性能问题。

所以我试图不让它出现在我的结果中。 所以我改变它:

 using (var ctx = new myEntities) { ctx.Configuration.ProxyCreationEnabled = false; ctx.Configuration.LazyLoadingEnabled = false; AllPersons= ctx.Persons.ToList(); } 

现在很好,因为AllPersons.student集合将始终为null

但现在我发现:如果我把人和学生放在一起

 using (var ctx = new myEntities) { ctx.Configuration.ProxyCreationEnabled = false; ctx.Configuration.LazyLoadingEnabled = false; AllPersons= ctx.Persons.ToList(); AllStudents = ctx.Student.ToList(); } 

现在参考仍然包括在内。

那么在这种情况下, 有没有让参考包含在任何时间? 谢谢。


更新

对于一些朋友的要求,我解释了为什么我需要它:

1:当我将它转换为json时,它将是一个死循环。 即使我已经使用Json.net ReferenceLoopHandling ,json大小非常大以致于服务器崩溃。(如果没有引用,它只是一个非常小的json)

2:每次我获取客户端数据并需要保存时,它都会显示有关模型状态的exception,直到我将其设置为null

例:

 using (myEntities ctx = new myEntities()) { ctx.Configuration.LazyLoadingEnabled = false; ctx.Configuration.ProxyCreationEnabled = false; Person model= ThisIsAModel(); model.students = null; // This is a key, I need set the students collection references to null , otherwise it will throw exception ctx.Entry(model).State = EntityState.Modified; ctx.SaveChanges(); } 

3:这是更重要的问题。 我已经在服务器上获取了所有数据和缓存。 但是当服务器启动时,它会让加载时间很长。 (因为数据和引用很多,这是主要问题),我不知道我会再次遇到什么样的问题….

 public List PersonsCache; // global cache public List StudentsCache; // global cache using (myEntities ctx = new myEntities()) { ctx.Configuration.LazyLoadingEnabled = false; ctx.Configuration.ProxyCreationEnabled = false; // There is so many references and data, will let it very slow , when I first time get the all cache. even I only get the Person model, not other , just because some Collection has some references problem. It will very slow.... PersonsCache = ctx.Persons.ToList(); StudentsCache= ctx.Student.ToList(); } 

问题

如您所说,当您加载父列表和子列表时,即使禁用了LazyLoading,然后查看parent.Childs,您也会看到子项已加载。

 var db = new YourDbContext(); db.Configuration.LazyLoadingEnabled = false; var parentList= db.YourParentSet.ToList(); var childList= db.YourChildSet.ToList(); 

发生了什么? 为什么孩子被包括在父母身边?

父实体下的子节点是使用db.YourChildSet.ToList();加载的子db.YourChildSet.ToList(); 完全是他们自己 ; 实际上,Entity Framework从不为父级再次加载子级,但由于edmx中父级和子级之间的关系,它们会在那里列出。


这会影响Perforemance吗?

根据孩子只加载一次的事实,由于加载数据,它对perforemance没有影响。


但是为了序列化或其他原因,我怎么能摆脱它呢?

你可以使用这些解决方案:

解决方案1:

使用2个不同的YourDbContext实例:

 var db1 = new YourDbContext(); db1.Configuration.LazyLoadingEnabled = false; var parentList= db.YourParentSet.ToList(); var db2 = new YourDbContext(); db2.Configuration.LazyLoadingEnabled = false; var childList= db.YourChildSet.ToList(); 
  • 现在当你查看parent.Childs时,其中没有Child。

解决方案2:

使用Projection并根据您的意愿调整输出并使用它们。

 var db1 = new YourDbContext(); db1.Configuration.LazyLoadingEnabled = false; var parentList= db.YourParentSet .Select(x=>new /*Model()*/{ Property1=x.Property1, Property2=x.Property2, ... }).ToList(); 
  • 这种方式在序列化时没有什么烦人的。
  • 使用自定义Model类是可选的,在某些情况下建议使用。

其他资源

作为使用Entity Framework的开发人员,强烈建议您阅读这些资源:

  • entity framework4,5和6的性能注意事项
  • 连接管理

我将专注于你的第三个问题,因为这似乎是你最紧迫的问题。 然后我会尝试对其他两个问题给出一些提示。

您应该注意两个entity frameworkfunction:

  1. 将数据加载到上下文中时,Entity Framework将尝试将对象连接到任何位置。 这称为关系修正 。 你无法阻止EF这样做。 因此,如果您单独加载Persons和学生,即使您没有Include()也会包含学生的Students集合。

  2. 默认情况下,上下文会缓存从数据库中提取的所有数据。 此外,它在变更跟踪器中存储有关对象的元数据:各个属性的副本所有关联。 因此,通过加载许多对象,内部缓存会增长,但也会增加元数据的大小。 并且持续运行的关系修复过程变得越来越慢(尽管通过关闭自动更改检测可能有助于推迟它)。 总而言之, 环境变得臃肿而缓慢,就像松弛的犀牛一样。

我知道您希望将数据缓存在每个实体的单独集合中。 两个简单的修改将使这更快:

  • 通过单独的上下文加载每个集合来避免不可避免的关系修复
  • 停止缓存(在上下文中)并通过使用AsNoTracking获取数据来更改跟踪。

这样做,您的代码将如下所示:

 public List PersonsCache; public List StudentsCache; using (myEntities ctx = new myEntities()) { ctx.Configuration.ProxyCreationEnabled = false; PersonsCache = ctx.Persons .AsNoTracking() .ToList(); } using (myEntities ctx = new myEntities()) { ctx.Configuration.ProxyCreationEnabled = false; StudentsCache= ctx.Student .AsNoTracking() .ToList(); } 

关闭ProxyCreationEnabled的原因是你将获得轻量级对象,之后你永远不会无意中触发延迟加载(抛出上下文不再可用的exception)。

现在,您将拥有非相互关联的缓存对象,并且可以像使用EF一样快速获取。 如果这还不够快,你将不得不求助于其他工具,比如Dapper。

顺便说一句,你的第一个代码片段和问题描述……

 using (var ctx = new myEntities) { AllPersons = ctx.Persons.ToList(); } 

还有AllPersons.student集合将包含在结果中;

…建议entity framework自发地执行(学生)的急切加载,而不Include它们。 我必须假设您的代码段不完整。 EF永远不会自动执行急切加载。 (除非,也许,你有一些古怪和错误的查询提供者)。

至于第一个问题 ,序列化。 您应该能够以与上面所示类似的方式解决这个问题。 只需单独加载要序列化的数据并禁用代理创建。 或者,正如其他人所建议的那样,序列化视图模型或完全包含您需要的匿名类型。

至于第二个问题 ,validationexception。 我只能想象如果你默认初始化学生集合,空, Student对象,就会发生这种情况。 这些一定无效。 如果不是这种情况,我建议您询问有关此特定问题的新问题,并显示有关所涉及的类和映射的详细信息。 这个问题不应该处理。

如果实体是自动生成的,则将其复制粘贴到自己的代码中,并删除生成的关系,如子集合和外键。 或者你不需要所有这些function可能是用户轻量级框架,如dapper

通常,您的学生集合不会从数据库填充。 当你到达房产时它就会填满。 此外,如果使用ToList()方法,则Entity Framework从数据中读取数据以填充您的集合。

请检查一下。 https://msdn.microsoft.com/en-us/data/jj574232.aspx#lazy https://msdn.microsoft.com/en-us/library/vstudio/dd456846(v=vs.100).aspx

反正在这种情况下是否有任何时候不参考?

解决方案似乎很简单:不要映射关联。 删除Student集合。 我可以说不多了。

明确选择要从数据库返回的内容。

使用Select new 。 使用select new子句,您可以创建匿名类型的新对象作为查询的结果,并且不要让引用包括在内。此语法允许您构造匿名数据结构。 这些是在评估时(懒惰地)创建的。 像这样:

 using (var ctx = new myEntities()) { var AllPersons = ctx.People.Select(c => new {c.PersonId, c.PersonName}).ToList(); } 

即使您不再需要禁用延迟加载

运行上面的查询后:

结果

此查询当前使用select new { }分配匿名类型,这需要您使用var 。 如果要分配已知类型,请将其添加到select子句中:

 private IEnumerable AllPersons;//global variable using (var ctx = new myEntities()) { AllPersons = ctx.People .Select(c => new MyClass { PersonId = c.PersonId, PersonName = c.PersonName }).ToList(); } 

和:

 public class MyClass { public string PersonId { get; set; } public string PersonName { get; set; } } 

如果我理解正确的话,你只是想确保你只得到你特别要求的东西吗?

上面提到过这一点,但为了正确地做到这一点,你只想选择一个匿名类型。

 var students = from s in _context.Students select new{ StudentId, StudentNo}; 

然后,当您想要更新此集合/对象时,我建议使用GraphDiff。 GraphDiff确实可以解决断开连接的实体和更新的问题( https://github.com/refactorthis/GraphDiff

所以你的方法看起来类似于:

 void UpdateStudent(Student student){ _context.UpdateGraph(student, map => map .AssociatedEntity(c => c.Person)); _context.SaveChanges(); } 

这样,您就可以更新对象上的任何属性,断开连接或不断开连接,而不用担心关联。

这假设您正确映射了您的实体,老实说,我发现将对象声明为属性更容易,而不仅仅是ID,并使用映射文件正确映射它。

所以:

 class Person{ int Id{get;set;} string Name{get;set} } class Student{ int Id{get;set;} string StudentNo{get;set;} Person Person{get;set;} public class StudentMap : EntityTypeConfiguration { public StudentMap() { // Primary Key HasKey(t => t.Id); // Table & Column Mappings ToTable("Students"); Property(t => t.Id).HasColumnName("StudentId"); // Relationships HasRequired(t => t.Person) .HasForeignKey(d => d.PersonId); } } 

希望这是有道理的。 您不需要创建视图模型,但绝对可以。 这种方式确实可以更容易地将断开连接的项目映射回数据库。

它们在ObjectContextEntityKey自动链接。 根据您对PersonsStudents ,您可以从ObjectContext Detach它们:

 using (var ctx = new myEntities) { ctx.Configuration.ProxyCreationEnabled = false; ctx.Configuration.LazyLoadingEnabled = false; AllPersons= ctx.Persons.ToList(); foreach(var c in AllPersons) { ctx.Detach(c); } AllStudents = ctx.Student.ToList(); foreach(var c in AllStudents ) { ctx.Detach(c); } }