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

How should I disable Entity Framework table reference(foreign) list from each objects?

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


Table 人:

PersonId

人名


Table学生:

学号

PersonId(参考table Person FK)

学生号


现在每次我在 EF5 中获取 Persons 集合时:

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

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

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

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

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

现在很好,因为 AllPersons.student 集合将永远是 null

但现在我发现:如果我把 Person 和 Student 放在一起:

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:每次获取客户端数据需要保存时,都会显示模型状态异常,直到我设置为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<Person> PersonsCache; // global cache
public List<Student> 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();
}

它们会自动链接到 ObjectContext 那里 EntityKey。根据您想对 PersonsStudents 做什么,您可以 Detach 它们来自 ObjectContext :

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);
    }
}

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

明确地 select 您想要从数据库中 return 的内容。

使用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<MyClass> 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; }
}

Is there anyway to don't let the reference include in any time in this situation?

解决这个问题的方法似乎很简单:不要映射关联。删除 Student 集合。我不能再多说了。

如果您使用的是 4.5+

,请使用 [IgnoreDataMember] 修饰任何属性

https://msdn.microsoft.com/en-us/library/system.runtime.serialization.ignoredatamemberattribute(v=vs.110).aspx

听起来你也在尝试做 table 继承,这是 EF

的一个不同问题

http://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/implementing-inheritance-with-the-entity-framework-in-an-asp-net-mvc-application

http://www.entityframeworktutorial.net/code-first/inheritance-strategy-in-code-first.aspx

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

这在上面提到了一点,但要正确地做到这一点,你只需要 select 一个匿名类型。

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

然后,当您要更新此 collection/object 时,我建议您使用 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<Student>
    {
        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);
  }
}

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

问题

正如您所说,当您同时加载 Parent 和 Child 列表时,即使禁用了 LazyLoading,然后查看 parent.Childs 您会看到 child 项也被加载了。

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

发生了什么事?为什么 child 包含在 parent 中?

parent 实体下的 child 是您使用 db.YourChildSet.ToList(); 加载的 ;事实上 Entity Framework 再也不会为 parent 加载 childs 但由于 edmx 中 parent 和 child 之间的关系,它们被列在那里。


这会影响性能吗?

根据child只加载一次的事实,加载数据对性能没有影响。


但是为了序列化或者别的什么,我怎样才能去掉它?

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

方案一:

使用 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。

方案二:

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

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

其他资源

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

我将重点解决您的第三个问题,因为这似乎是您最紧迫的问题。然后我会尝试对其他两个问题给出一些提示。

您应该注意两个 Entity Framework 功能:

  1. 当您将数据加载到上下文中时,Entity Framework 将尝试在对象关联的任何地方连接它们。这称为关系修正。您无法阻止 EF 这样做。因此,如果您分别加载 PersonsStudentsPersonStudents 集合 包含学生,即使您没有t Include() 他们

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

我了解到您想将每个实体的数据缓存在单独的集合中。两个简单的修改将使这更快:

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

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

public List<Person> PersonsCache;
public List<Student> 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的原因是你会得到轻对象并且你永远不会无意中触发延迟加载(抛出上下文不再可用的异常)。

现在您将拥有互不相关且获取速度与使用 EF 一样快的缓存对象。如果这不够快,您将不得不求助于其他工具,例如 Dapper。

顺便说一下,您的第一个代码片段和问题描述...

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

There is also has AllPersons.student collection will include in the result;

...建议 Entity Framework 自发地执行预先加载(学生),而无需您 Include-ing 他们。我必须假设您的代码片段不完整。 EF 永远不会自动执行预先加载。 (除非,也许,你有一些古怪和错误的查询提供者)。

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

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

我有完全相同的情况。 我所做的就是在请求 Persons.ToList() 之前请求 Student.ToList() 我不必禁用延迟加载。只需要先加载引用其他 table 的 table ,然后您可以加载其他 table 并且第一个 table 结果已经在内存中并且没有得到"fixed" 包含所有参考文献。