Можно ли преобразовать ObjectSet в DbSet в Entity Framework 6?
Я работаю над обновлением приложения WPF с.Net4/EF 4.4 до.Net4.5/EF 6.1. После обновления я буду использовать DbContext (так как не было POCO-генератора для ObjectContext).
Приложение использует Repository/UnitOfWork-pattern для доступа к Entity Framework, и перед обновлением я мог бы установить для ObjectSet.MergeOption значение OverwriteChanges (в классе репозитория), но класс DbSet не имеет этой функции. Однако я знаю, что могу получить ObjectSet из DbContext с помощью IObjectContextAdapter. (См. Код ниже). Но, похоже, что установка MergeOption для созданного ObjectSet не отразится обратно на DbSet.
Поэтому мой вопрос заключается в следующем: есть ли способ преобразовать ObjectSet обратно в DbSet (сохраняя настройку MergeOption)?
Это часть класса репозитория:
public class SqlRepository<T> : IRepository<T> where T : class, IEntity
{
protected DbSet<T> dbSet;
public SqlRepository(DbContext context)
{
var objectContext = ((IObjectContextAdapter)context).ObjectContext;
var set = objectContext.CreateObjectSet<T>();
set.MergeOption = MergeOption.OverwriteChanges;
dbSet = context.Set<T>();
//I would like to do something like this: dbSet = (DbSet)set;
}
}
1 ответ
Хотя это и не является прямым ответом на ваш вопрос, я придумала решение на основе T4 для надзора EF, связанного с MergeOption, которое недоступно в DbSet. Судя по твоему вопросу это то, что ты ищешь?
В контексте по умолчанию у вас есть что-то, сгенерированное генератором T4, например:
public virtual DbSet<Person> Persons { get; set; }
public virtual DbSet<Address> Addresses { get; set; }
и т.п.
Мой подход заключается в том, чтобы отредактировать T4, добавив геттеры для каждой сущности, которые обеспечивают прямой доступ к ObjectSet как IQueryable:
public IQueryable<Person> GetPersons(MergeOption mergeOption = MergeOption.AppendOnly, bool useQueryImplentation = true)
{
return useQueryImplementation ? GetSet<Person>(mergeOption).QueryImplementation() : GetSet<Person>(mergeOption);
}
Тогда в базе DataContext
public class DataContextBase
{
/// <summary>
/// Gets or sets the forced MergeOption. When this is set all queries
/// generated using GetObjectSet will use this value
/// </summary>
public MergeOption? MergeOption { get; set; }
/// <summary>
/// Gets an ObjectSet of type T optionally providing a MergeOption.
/// <remarks>Warning: if a DataContext.MergeOption is specified it will take precedence over the passed value</remarks>
/// </summary>
/// <typeparam name="TEntity">ObjectSet entity Type</typeparam>
/// <param name="mergeOption">The MergeOption for the query (overriden by DataContext.MergeOption)</param>
protected IQueryable<TEntity> GetObjectSet<TEntity>(MergeOption? mergeOption = null) where TEntity : class
{
var set = Context.CreateObjectSet<TEntity>();
set.MergeOption = MergeOption ?? mergeOption ?? MergeOption.AppendOnly;
return set;
}
Создав метод Extension по умолчанию для IQueryable, как показано ниже, вы можете при желании добавить свои собственные значения QueryImplementation для каждой таблицы / типа, чтобы все пользователи вашей таблицы получили сортировку или включения и т. Д. (Эта часть не обязательна для ответа на вопрос, но ее полезно в любом случае)
Так, например, вы можете добавить следующее, чтобы всегда включать адреса при вызове GetPersons()
public static class CustomQueryImplentations
{
public static IQueryable<Person> QueryImplementation(this IQueryable<Person> source)
{
return source
.Include(r => r.Addresses)
.OrderByDescending(c => c.Name);
}
}
В заключение:
//just load a simple list with no tracking (Fast!)
var people = Database.GetPersons(MergeOption.NoTracking);
//user wants to edit Person so now need Attached Tracked Person (Slow)
var peson = Database.GetPersons(MergeOption.OverwriteChanges).FirstOrDefault(p => p.PersonID = 1);
//user makes changes and on another machine sometime later user clicks refresh
var people = Database.GetPersons(MergeOption.OverwriteChanges);
Или вы можете (как у меня) написать что-то вроде
Database.MergeOption = MergeOption.OverwriteChanges;
обновить загрузки объектов, используя существующие методы Get, но теперь ВСЕ будут перезаписывать присоединенные объекты
Database.MergeOption = null;
Стоит отметить, что если вы загружаете AsNoTracking до того, как вносите изменения, вам необходимо либо повторно присоединить, либо, возможно, лучше перезагрузить с помощью OverwriteChanges, чтобы убедиться, что у вас установлена последняя версия Entity.