c# - 在 EF6 中重新加载导航属性

标签 c# linq entity-framework-6 dbcontext

我已经阅读了很多关于使用 Context.Entry(entity).Collection(p=>p.Property).Load() 重新加载导航属性的 stackoverflow 问题,但在我的例子中它没有'给我从数据库中更新的值。

LazyLoading 和 ProxyCreation 选项设置为它们的默认值,我读过它们在默认情况下处于打开状态。

我有一个实体测试对象,我使用以下方法使用所有相关属性的预加载从数据库中获取了它:

var test = Repository.GetById(testId, null, true, new Expression<Func<Test,object>>[] {
    bt=>bt.Baselining,
    ct=>ct.Baselining.BaselineTestCase,
    dt=>dt.Baselining.BaselineTestCase.Baseline,
    ft=>ft.Baselining.Transaction,
    gt=>gt.Baselining.Transaction>Select(x=>x.Fields)
}); 

public virtual T GetById<T>(int id, Func<T,bool> where = null, bool trackChanges = false, params Expression<Func<T,object>>[] includeProps) 
{
    T item = null;
    IQuerable<T> dbQuery = Context.Set<T>();

    if(includeProps != null)
        foreach(Expression<Func<T,object>> navProp in includeProps)
            dbQuery = dbQuery.Include<T,object>(navProp);

    if(where == null)
    {    
        if(!trackChanges) item = dbQuery.AsNoTracking().FirstOrDefault(t=>t.Id == id);
        else item = dbQuery.FirstOrDefault(t=>t.Id == id);
    }
    else
    {
        if(!trackChanges) item = dbQuery.AsNoTracking().Where(where).FirstOrDefault(t=>t.Id == id);
        else item = dbQuery.Where(where).FirstOrDefault(t=>t.Id == id);
    }
    return item;
}

我正在尝试使用

重新加载 Baselining.Transaction.Fields
Repository.Reload(test);
Repository.Reload(test.Baselining.BaselineTestCase);
Repository.Reload(test.Baselining.BaselineTestCase.Baseline);
Repository.ReloadNavigationProperties(test.Baselining, x=>x.Transaction);
foreach(var tq in test.Baselining.Transaction)
    Repository.ReloadNavigationProperties(tq, x=>x.Fields);

存储库方法是这样的:

public virtual void ReloadNavigationProperties(TEntity,TElement>(TEntity entity, Expression<Func<TEntity,ICollection<TElement>>> navProp) where TEntity : class where TElement : class
{
    Context.Entry(entity).Collection(navProp).Load();
}

public virtual T Reload<T>(T entity) where T : class {
    Context.Entry(entity).Reload();
    return entity;
}

我已经使用 SQL Profiler 调试了上面的 ReloadNavigationProperties 执行,它确实进入了数据库并生成了一个带有连接的 SQL 查询,但即使在重新加载后我的对象也没有更新。

在使用上述方法重新加载导航属性失败后,我采用了这种简单的方法,它有效...

foreach(var tq in test.Baselining.Transaction)
    foreach(var tqfs in tq.Fields) 
        Repository.Reload(tqfs);

因为每个 TransactionQuery 我有大约 100 个 TransactionQueryFields,这个嵌套的 foreach 会重新加载每个字段 100 次,它非常非常慢,应该很慢......

这是实体

public class Test 
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int id {get;set;}
    public virtual Baselining Baselining {get;set;}
    public virtual ICollection<TestExecutionResult> TestCaseResults {get;set;} = new List<TestExecutionResult>();
}

public class Baselining 
{
    public Baselining() {}
    [Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int id {get;set;}
    public DateTime BaseliningDate {get;set;}
    public virtual BaselineTestCase BaselineTestCase {get;set;}
    public virtual ICollection<TransactionQuery> Transaction {get;set;} = new List<TransactionQuery>();
}

public class BaselineTestCase 
{
    public BaselineTestCase() {}
    [Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int id {get;set;}
    public virtual Baseline Baseline {get;set;}
    public virtual ICollection<Baselining> Baselinings {get;set;} = new List<Baselining>();
    public Baselining LastBaselining {
        get {return Baselinings.OrderBy(x=>x.BaseliningDate).LastOrDefault(); }
    }
}

public class TransactionQuery : TransactionItem 
{
    public TransactionQuery() {}
    [Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
    public override int id {get;set;}
    public virtual Baselining Baselining {get;set;}
    public virtual ICollection<TransactionQueryField> Transaction {get;set;} = new List<TransactionQueryField>();
}

public class TransactionQueryField : TransactionItem 
{
    public TransactionQueryField() {}
    [Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
    public override int id {get;set;}
    public bool Compare {get;set;}
    public bool Critical {get;set;}
}

public abstract class TransactionItem : BaseEntity<int> {
    public string TestResultCode {get;set;}
}

public abstract class BaseEntity<T> {
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public virtual T Id {get;set;}
}

我想了解为什么预先加载导航属性没有加载更新的数据。因为根据我的理解,如果我急于加载任何东西,它应该往返数据库并获取更新的数据。但即使通过重新加载进行预加载,它也不会给我更新的值。

如果我没有正确解释它,我很抱歉,尽管我将不胜感激任何关于如何更快地加载我的字段的帮助,这可能是一个单一的数据库往返。

最佳答案

这只是一个假设,但我认为您的导航属性未重新加载的原因是被跟踪的实体。 我已经经历过类似的事情以及关于 Load 方法的 doc说:

Loads the collection of entities from the database. Note that entities that already exist in the context are not overwritten with values from the database.

当您调用 GetById 时,您将参数 trackChanges 设置为 true。从现在开始,所有预先加载的实体都会被跟踪。 正如我所说,我不确定这是否是问题所在,但您可以尝试将所有跟踪实体的状态设置为 EntityState.Detached

关于c# - 在 EF6 中重新加载导航属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54322597/

相关文章:

linq - 在 Entity Framework Where 子句中使用枚举

c# - 延迟某个 Action 一段时间

c# - 从 UWP 应用程序运行外部 exe,解决沙盒模式

c# - 更改/访问矩阵元素的最佳/最快方法

c# - 如何向 WebMatrix Database.Query 添加参数?

c# - Xamarin 工作簿 : Use of namespaces

c# - 在 C# 中检查两个 List<T> 列表是否相等的最佳方法是什么

c# - Entity Framework 6 - 调用 SaveChanges() 时未设置

entity-framework - EF 6数据库第一个: How to update stored procedures?

c# - 一对多和递归关系 - 要设置的强制值