как передать IDbContext в DbMigrationsConfiguration

Мы реализовали шаблон Generic Repository, Unit of Work с EF5 Code First из ряда ресурсов и разработали следующие сборки.

Интерфейсы, контексты, модель, репозитории, единицы работы

В сборке контекста у меня есть папка миграции, которая содержит Configuration.cs

internal sealed class Configuration : DbMigrationsConfiguration<Context.SportsContext>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = true;
    }

    protected override void Seed(Context.SportsContext context)
    {
        //  This method will be called after migrating to the latest version.

        //  You can use the DbSet<T>.AddOrUpdate() helper extension method 
        //  to avoid creating duplicate seed data. E.g.
        //
        //    context.People.AddOrUpdate(
        //      p => p.FullName,
        //      new Person { FullName = "Andrew Peters" },
        //      new Person { FullName = "Brice Lambson" },
        //      new Person { FullName = "Rowan Miller" }
        //    );
        //
    }
}

Как вы можете видеть, эта DbMigrationsConfiguration принимает мой SportsContext, который также определен в сборке контекстов (папка «Контексты»).

 public class SportsContext : IDbContext
{
    private readonly DbContext _context;

    public SportsContext()
    {
        _context = new DbContext("SportsContext");

    }

    public void Dispose()
    {
        _context.Dispose();
    }

    public IDbSet<T> GetEntitySet<T>() where T : class
    {
        return _context.Set<T>();
    }

    public void ChangeState<T>(T entity, EntityState state) where T : class
    {
        _context.Entry(entity).State = state;
    }

    public void SaveChanges()
    {
        _context.SaveChanges();
    }
}

Это реализует IDbContext, который определен в сборке интерфейсов.

public interface IDbContext : IDisposable
{
    IDbSet<T> GetEntitySet<T>() where T : class;
    void ChangeState<T>(T entity, EntityState state) where T : class;
    void SaveChanges();
}

В моей сборке UnitsOfWork у меня есть следующий класс

public class SportUnitOfWork : IUnitofWork
{
    private readonly IDbContext _context;

    public SportUnitOfWork()
    {
        _context = new SportsContext();
    }

    private GenericRepository<Team> _teamRepository;
    private GenericRepository<Fixture> _fixtureRepository;

    public GenericRepository<Team> TeamRepository
    {
        get { return _teamRepository ?? (_teamRepository = new GenericRepository<Team>(_context)); }
    }

    public GenericRepository<Fixture> FixtureRepository
    {
        get { return _fixtureRepository ?? (_fixtureRepository = new GenericRepository<Fixture>(_context)); }
    }

    public void Save()
    {
        _context.SaveChanges();
    }

    public IDbContext Context
    {
        get { return _context; }
    }

    private bool _disposed;

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                _context.Dispose();
            }
        }
        _disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

}

Для примера я добавил класс GenericRepository в сборку Repositories.

public class GenericRepository<T> : IGenericRepository<T> where T : class
{
    private IDbContext _context;

    public GenericRepository(IDbContext context)
    {
        _context = context;
    }

    public GenericRepository(IUnitofWork uow)
    {
        _context = uow.Context;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposing) return;

        if (_context == null) return;
        _context.Dispose();
        _context = null;
    }

    public void Add(T entity)
    {
        _context.GetEntitySet<T>().Add(entity);
    }

    public void Update(T entity)
    {
        _context.ChangeState(entity, EntityState.Modified);
    }

    public void Remove(T entity)
    {
        _context.ChangeState(entity, EntityState.Deleted);
    }

    public T FindSingle(Expression<Func<T, bool>> predicate = null, params Expression<Func<T, object>>[] includes)
    {
        var set = FindIncluding(includes);
        return (predicate == null) ? set.FirstOrDefault() : set.FirstOrDefault(predicate);
    }

    public IQueryable<T> Find(Expression<Func<T, bool>> predicate = null, params Expression<Func<T, object>>[] includes)
    {
        var set = FindIncluding(includes);
        return (predicate == null) ? set : set.Where(predicate);
    }

    public IQueryable<T> FindIncluding(params Expression<Func<T, object>>[] includeProperties)
    {
        var set = _context.GetEntitySet<T>();

        if (includeProperties != null)
        {
            foreach (var include in includeProperties)
            {
                set.Include(include);
            }
        }

        return set.AsQueryable();
    }

    public int Count(Expression<Func<T, bool>> predicate = null)
    {
        var set = _context.GetEntitySet<T>();
        return (predicate == null) ? set.Count() : set.Count(predicate);
    }

    public bool Exist(Expression<Func<T, bool>> predicate = null)
    {
        var set = _context.GetEntitySet<T>();
        return (predicate == null) ? set.Any() : set.Any(predicate);
    }
}

У меня проблема в том, что класс Configuration, который наследуется от DbMigrationsConfiguration, ожидает параметр DbContext.

Ошибка — это ошибка 1. Тип «Contexts.Context.SportsContext» не может использоваться в качестве параметра типа «TContext» в универсальном типе или методе «System.Data.Entity.Migrations.DbMigrationsConfiguration». Неявное преобразование ссылок из «Contexts.Context.SportsContext» в «System.Data.Entity.DbContext» отсутствует.

Я могу изменить SportsContext, чтобы он также наследовался от DbContext, но тогда мне нужно добавить ссылку на EntityFramework 5 в сборке UnitsOfWork, поскольку мы хотим, возможно, изменить или удалить каждый слой без какой-либо ссылки на базовые модели, поэтому я выбрал этот шаблон. .

Поскольку мы рассматриваем возможность добавления дополнительных контекстов и моделей в будущем, мы хотели настроить архитектуру таким образом, чтобы мы могли просто добавлять контекст, модель, а затем реализовывать соответствующие интерфейсы по мере необходимости.

Веб-служба WebAPI Restful будет взаимодействовать с нашими данными через SportUnitOfWork, если я правильно понял шаблоны.

Если у кого-то есть какие-либо идеи о том, как я могу это сделать или что-то, что я делаю неправильно, пожалуйста, дайте мне знать

заранее спасибо Марк


person Mark West    schedule 22.02.2013    source источник
comment
Не похоже, что вы используете Code First. (Подразумевается вашим оператором new DbContext("SportsContext").) Если это так, то Code First Migrations все равно не будет работать.   -  person bricelam    schedule 01.03.2013
comment
Я обошел проблему, выполнив следующие действия   -  person Mark West    schedule 04.03.2013


Ответы (1)


Решил это, выполнив следующие действия

Изменил мой класс SportsContext на BaseContext, который является абстрактным

public abstract class BaseContext : IDbContext
{
    protected DbContext Context;

    public void Dispose()
    {
        Context.Dispose();
    }

    public IDbSet<T> GetEntitySet<T>() where T : class
    {
        return Context.Set<T>();
    }

    public void Add<T>(T entity) where T : class
    {
        DbEntityEntry dbEntityEntry = GetDbEntityEntrySafely(entity);
        dbEntityEntry.State = EntityState.Added;
    }

    public void Update<T>(T entity) where T : class
    {
        DbEntityEntry dbEntityEntry = GetDbEntityEntrySafely(entity);
        dbEntityEntry.State = EntityState.Modified;
    }

    public void Delete<T>(T entity) where T : class
    {
        DbEntityEntry dbEntityEntry = GetDbEntityEntrySafely(entity);
        dbEntityEntry.State = EntityState.Deleted;
    }

    public void SaveChanges()
    {
        // At the moment we are conforming to server wins when handling concurrency issues
        // http://msdn.microsoft.com/en-us/data/jj592904

        try
        {
            Context.SaveChanges();
        }
        catch (DbUpdateConcurrencyException e)
        {
            //Refresh using ServerWins
            var objcontext = ((IObjectContextAdapter) Context).ObjectContext;
            var entry = e.Entries;

            objcontext.Refresh(RefreshMode.StoreWins, entry);

            SaveChanges();
        }
    }

    private DbEntityEntry GetDbEntityEntrySafely<T>(T entity) where T : class
    {
        DbEntityEntry dbEntityEntry = Context.Entry(entity);

        if (dbEntityEntry.State == EntityState.Detached)
        {
            // Set Entity Key
            var objcontext = ((IObjectContextAdapter) Context).ObjectContext;

            if (objcontext.TryGetObjectByKey(dbEntityEntry.Entity))

            Context.Set<T>().Attach(entity);
        }

        return dbEntityEntry;
    }
}

создал в папке Context новый класс FootballContext, который наследуется от BaseContext.

public class FootballContext : BaseContext
{
    public FootballContext(string connectionstringName)
    {
        Context = new BaseFootballContext(connectionstringName);

    }
}

Создал новую папку с именем DbContexts

Здесь созданы следующие классы,

public class BaseFootballContext : DbContext
{
    public BaseFootballContext(string nameOrConnectionString) : base(nameOrConnectionString)
    {
    }

    public IDbSet<Fixture> Fixtures { get; set; }
    public IDbSet<Team> Teams { get; set; }  
}

public class MigrationsContextFactory : IDbContextFactory<BaseFootballContext>
{
    public BaseFootballContext Create()
    {
        return new BaseFootballContext("FootballContext");
    }
}

теперь мой класс конфигурации может принимать BaseFootballContext, поскольку это DbContext.

Мой класс UnitOfWork теперь может установить контекст FootballContext, поэтому ему не нужно ссылаться на EntityFramework.

Это работает и с миграциями.

Единственная проблема, с которой я столкнулся сейчас, - это выяснить, как заставить это работать в отключенной среде, поскольку у меня возникают проблемы с повторным подключением объектов и применением обновлений.

person Mark West    schedule 04.03.2013