C# 实体框架加载相关实体

C# 实体框架加载相关实体,c#,entity-framework,lazy-loading,C#,Entity Framework,Lazy Loading,我使用的是代码优先实体框架,其基本上下文仅由标准IDbSet集合组成,其中T只是一个POCO类。在我的上下文中,我禁用了延迟加载。虽然我的模型类中有“导航属性”,但我已经从中删除了virtual关键字 存储库中的“Get All”方法执行一些自定义筛选,以确保当前用户只看到他们拥有的数据,除非他们是管理员。我以管理员身份登录时遇到了一个特殊问题,该问题也与一些记录相关。因为我登录的实体是在上下文中加载的,即使我禁用了延迟加载、虚拟删除并且没有使用Include或Load,结果中与我的配置文件关联

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

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

这不是我项目中的代码,只是一个展示我正在做什么的示例。它可能有打字错误和语法错误

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;
    }
}
公共类记录
{
公共Guid Id{get;set;}
公共字符串名称{get;set;}
公共所有者{get;set;}//无虚拟关键字
公共Guid所有者ID{get;set;}
}
公共类所有者
{
公共Guid Id{get;set;}
公共字符串名称{get;set;}
公共集合记录{get;set;}//无虚拟关键字
}
公共类上下文:DbContext
{
IDbSet所有者{get;set;}
IDbSet记录{get;set;}
公共静态上下文创建()
{
上下文=新上下文();
context.Configuration.LazyLoadingEnabled=false;//禁用延迟加载
返回上下文;
}
}
公共类存储库
{
私有上下文{get;set;}
公共所有者CurrentOwner{get;private set;}
公共存储库()
{
Context=Context.Create();
//在此处编写代码以获取应用程序用户并查找关联的“所有者”
//实体,如果用户是“所有者”(他们可能只是管理员)
//但是GetCurrentOwnerNull使用上下文查找用户
CurrentOwner=GetCurrentOwnerUrnull();
}
公共IQueryable GetRecords(bool asAdmin)
{
IQueryable records=Context.records;//不包括或加载所有者
如果(asAdmin)
{
//验证应用程序用户是否为管理员,否则引发异常
}
其他的
{
如果(CurrentOwner==null)
{
//抛出一个安全异常
}
记录=记录。其中(r=>r.OwnerId==CurrentOwner.Id);
}
退货记录;
}
}
同样,上面的问题是,如果我以所有者身份运行代码,不管是否是管理员,那么我拥有的那些记录将设置所有者属性而不是null。我希望实体框架退出我的业务,而不是自动设置它。它会导致下游出现问题,特别是当以管理员和所有者的身份运行代码时,因此您会返回一些owner=null的记录,以及一些owner设置的记录。真烦人。请帮我停下来。

这个bug实际上是一个特性。实体框架将自动连接同一上下文中的关联,即使实体是独立加载的

让我们假设如下:

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; }
}
以下是将会发生的情况:

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

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

作为更新,我刚刚测试过,如果在GetCurrentOwner的代码中我创建了一个新上下文来进行查询,然后处理它,它将解决此问题。。。但我现在把这个问题留待讨论,以防有人知道在实体框架中关闭“功能”的另一种方法。您是否需要您的上下文在其缓存中保留这些对象?我尝试在上下文中将ProxyCreationEnabled设置为false,但它没有解决问题。我没有尝试AsNoTracking()路由,因为必须确保在每个查询中都指定了它,这会非常烦人,但是还有一些其他实例,我们检查了所有权,它导致了一些问题,我将尝试一下。如果有一个简单的设置可以在上下文级别关闭缓存,那就太好了,但是ProxyCreationEnabled似乎不是这样的设置。好的,我尝试了AsNoTracking(),它的效果非常好。标记为答案。。。谢谢真奇怪,谢谢你告诉我。我得弄清楚为什么这两者有区别。我假设ProxyCreationEnabled设置为false会导致所有对象返回为notracking()。将
ProxyCreationEnabled
设置为false只会强制EF创建普通POCO(因此,没有延迟加载,序列化中出现错误的机会更少)。不过,这些POCO仍在跟踪中,关系修复将完成它的工作。
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);