Можно ли преобразовать 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.

Другие вопросы по тегам