【问题标题】:Entity Framework Loading Related Entities实体框架加载相关实体
【发布时间】:2014-10-23 19:31:40
【问题描述】:

我正在使用代码优先实体框架,其基本上下文仅由标准 IDbSet 集合组成,其中 T 只是一个 POCO 类。在我的上下文中,我禁用了延迟加载。虽然我的模型类中有“导航属性”,但我已经从它们中删除了 virtual 关键字。

存储库中的“获取所有”方法执行一些自定义过滤,以确保当前用户只能看到他们拥有的数据,除非他们是管理员。我以管理员身份登录时遇到了一个特殊问题,该问题也与某些记录相关联。由于我登录的实体是在上下文中加载的,即使我禁用了延迟加载、虚拟删除并且没有使用包含或加载,结果中与我的配置文件关联的对象也具有导航属性自动设置。

这不是我项目中的代码,只是一个示例来展示我正在做的事情的想法。它可能有拼写错误和语法错误。

public class Record
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public Owner Owner { get; set; } //No virtual keyword
    public Guid OwnerId { get; set; }
}

public class Owner
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public Collection<Record> Records { get; set; } //No virtual keyword
}

public class Context : DbContext
{
    IDbSet<Owner> Owners { get; set; }
    IDbSet<Record> Records { get; set; }
    public static Context Create()
    {
        Context context = new Context();
        context.Configuration.LazyLoadingEnabled = false; //Lazy loading disabled
        return context;
    }
}

public class Repository
{
    private Context Context { get; set; }
    public Owner CurrentOwner { get; private set; }
    public Repository()
    {
        Context = Context.Create();

        //Code here to get the application user and look up an associated "owner"
        //entity if the user is an "owner" (they could just be an administrator)
        //but the GetCurrentOwnerOrNull uses the Context to find the user
        CurrentOwner = GetCurrentOwnerOrNull();
    }

    public IQueryable<Record> GetRecords(bool asAdmin)
    {
        IQueryable<Record> records = Context.Records; //Not including or loading Owner
        if (asAdmin)
        {
           //Verify that the application user is an admin and throw exception otherwise
        }
        else
        {
            if (CurrentOwner == null)
            {
                //Throw a security exception
            }
            records = records.Where(r => r.OwnerId == CurrentOwner.Id);
        }
        return records;
    }
}

同样,上面的问题是,如果我以所有者身份运行该代码,无论是否为管理员,那么我拥有的那些记录将设置所有者属性而不是 null。我希望实体框架退出我的业务,​​而不是自动设置它。它会在下游引起问题,尤其是在以管理员和所有者身份运行代码时,因此您会返回一些带有 Owner = null 的记录和一些带有 Owner 集的记录。这很烦人。请让它停下来。

【问题讨论】:

  • 作为更新,我刚刚进行了测试,如果在 GetCurrentOwnerOrNull 的代码中创建一个新的上下文来执行该查询,然后将其处理掉,它就解决了这个问题......但我如果有人知道在实体框架中简单地关闭“功能”的另一种方法,请暂时保留问题。
  • 你需要你的上下文来持有缓存中的这些对象吗?

标签: c# entity-framework lazy-loading


【解决方案1】:

这个bug其实是一个特性。 Entity Framework 将在同一上下文中自动连接关联,即使实体是相互独立加载的。

假设如下:

public class Person
{
  public Person()
  {
    this.Pets = new List<Pet>();
  }

  public int Id { get; set; }
  public string Name { get; set; }

  public virtual ICollection<Pet> Pets { get; set; }
}

public class Pet
{
  public int Id { get; set; }
  public string Name { get; set; }
  public int PersonId { get; set; }

  public virtual Person Owner { get; set; }
}

我们还假设这些都与 DB-First 或 Attributes/Fluent API code-first 正确关联。

数据库:

Persons
Id Name
1  Erik Philips

Pets
Id Name PersonId
1  Joe  1

接下来会发生什么:

var pet = DbContext.Pets.FirstOrDefault(id => id == 1);
var person = DbContext.Persons.FirstOrDefault(id => id == 1);

Assert.AreEqual(person.Pets.Count(), 1);
Assert.IsNotNull(pet.Person);
Assert.AreEqual(pet.Person, person);

发生这种情况的原因是上下文将保留其缓存中的对象。如果您不关心持有这些对象的上下文,则需要使用AsNoTracking()

【讨论】:

  • 我尝试在上下文中将 ProxyCreationEnabled 设置为 false,但并没有解决问题。我没有尝试 AsNoTracking() 路由,因为必须确保我在每个查询中都指定了它会非常烦人,但是还有其他一些情况我们会检查所有权并且它会导致问题,我会给它一个尝试。如果只是有一个设置可以在上下文级别关闭缓存,那就太好了,但 ProxyCreationEnabled 似乎不是那个设置。
  • 好的,我尝试了 AsNoTracking(),它的作用就像一个魅力。标记为答案...谢谢!
  • 这个人很奇怪,谢谢你告诉我。我必须弄清楚为什么两者之间存在差异。我假设 ProxyCreationEnabled 设置为 false 会导致所有对象返回 AsNoTracking()。
  • ProxyCreationEnabled 设置为 false 只会强制 EF 创建普通的 POCO(因此,没有延迟加载并且序列化中出错的机会更少)。不过,这些 POCO 仍会被跟踪,并且关系修复会发挥作用。
猜你喜欢
  • 1970-01-01
  • 2011-04-09
  • 2012-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-11
  • 1970-01-01
相关资源
最近更新 更多