我使用代码优先实体框架,其基本上下文只包含标准IDbSet集合,其中T只是一个POCO类。在我的背景下,我已经禁用了延迟加载。虽然有"导航属性"在我的模型类中,我已经从中删除了虚拟关键字。
"全部获取"存储库中的方法执行一些自定义筛选,以确保当前用户只能看到他们拥有的数据,除非他们是管理员。我有一个特殊问题,我以管理员身份登录,并且与某些记录相关联。由于我登录的实体是在上下文中加载的,即使我已禁用延迟加载,虚拟删除并且不使用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;
}
}
所以再一次,上面的问题是,如果我要将该代码作为所有者运行,无论是否为管理员,那么我拥有的那些记录将设置所有者属性而不是null。我希望实体框架摆脱我的业务而不是自动设置它。它导致下游问题,特别是在以管理员和所有者身份运行代码时,所以你得到一些记录,其中Owner = null,一些记录了Owner。这太烦人了。请让它停下来。
答案 0 :(得分:4)
此错误实际上是一项功能。即使实体彼此独立加载,实体框架也会在同一上下文中自动连接关联。
让我们假设以下内容:
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代码优先关联。
数据库:
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()
。