Я прочитал много вопросов о stackoverflow относительно перезагрузки свойства навигации с помощью Context.Entry(entity).Collection(p=>p.Property).Load()
, но в моем случае он не дает мне обновленных значений из базы данных.
Для параметров LazyLoading и ProxyCreation установлены значения по умолчанию, которые, как я читал, по умолчанию включены.
У меня есть объект Entity Test, который я извлек из базы данных, используя активную загрузку со всеми связанными свойствами, используя метод ниже:
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;
}
Я отладил приведенное выше выполнение ReloadNavigationProperties с помощью SQL Profiler, и он действительно обращается к базе данных и также генерирует запрос SQL с соединениями, но мой объект не обновляется даже после перезагрузки.
После того, как я не смог перезагрузить свойства навигации с помощью вышеуказанного метода, я пришел к этому тривиальному подходу, который работает...
foreach(var tq in test.Baselining.Transaction)
foreach(var tqfs in tq.Fields)
Repository.Reload(tqfs);
Потому что у меня есть около 100 полей TransactionQueryField для каждого TransactionQuery, и этот вложенный 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
документ говорит:
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
.
Я никогда не использовал пакет EntityHooks, но документ довольно ясно описывает метод Load
, и я не могу придумать других способов подняться над ситуацией, кроме как играть с EntityState
или свойствами на DbCollectionEntry
.
Я читал это, но дело в том, что если я не отслеживаю изменения, то не смогу внедрить Entity Hooks, которые я настроил во время OnSave и OnLoad. Поэтому я должен отслеживать изменения в true. Хотя я пробовал Object context.Refresh, и он работает, но мне очень интересно узнать, как правильно справиться с такой ситуацией со свойствами навигации.