多层ASP.NET MVC应用程序中的实体框架上下文

时间:2016-01-14 11:00:25

标签: c# asp.net-mvc entity-framework dbcontext

我创建了一个应用程序,它将多个Visual Studio项目用于ASP.NET MVC应用程序的各个层。我有一个数据访问层,业务逻辑层和表示层。

解决方案如下所示:

Visual Studio Solution

我使用的存储库看起来像这样:

public class RepositoryDal: IRepositoryDal
{
    private readonly DatabaseContext context;

    public RepositoryDal()
    {
        context = new DatabaseContext();
    }

    public T Add<T>(T entity) where T : Entity
    {
        return context.Set<T>().Add(entity);
    }

    public void Delete<T>(T entity) where T : Entity
    {
        context.Set<T>().Remove(entity);
    }

    public IQueryable<T> GetAll<T>() where T : Entity
    {
        return context.Set<T>();
    }

    public T GetById<T>(int id) where T : Entity
    {
        return context.Set<T>().FirstOrDefault(x => x.Id == id);
    }

    public bool HasChanges()
    {
        return context.ChangeTracker.HasChanges();
    }

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

我从业务逻辑层调用此存储库的方法:

public class BookBll: IBookBll
{
    private readonly IRepositoryDal _repositoryDal;
    public BookBll()
    {
        _repositoryDal = new RepositoryDal();
    }

    public Book AddBook(Book book)
    {
        book = _repositoryDal.Add(book);
        _repositoryDal.Save();

        return book;
    }
    ...
}

这很有效。但是,如果我这样做,它将无法正常工作:

public class ShelfBll: IShelfBll
{
    private readonly IRepositoryDal _repositoryDal;
    public ShelfBll()
    {
        _repositoryDal = new RepositoryDal();
    }

    public Shelf AddShelf(Shelf shelf)
    {
        shelf = _repositoryDal.Add(shelf);
        _repositoryDal.Save();

        return shelf;
    }
    ...
}

问题在于我将书籍连接到书架,并通过另一个业务逻辑层类检索书架。通过这样做,我失去了实体框架上下文。 会发生什么是应用程序不会连接到架子,但它会创建一个新架子。这意味着在此之后我会有两个同名的货架。

我该如何解决这个问题?

2 个答案:

答案 0 :(得分:3)

不是在每个存储库后面隐藏单独的DB Context对象,而是公开一个中心对象,它本身类似于DB Context并跨越所有存储库。类似于工作单位的东西&#34;对象

我有a pretty old example here,但模式仍然存在。

基本上,目标是允许使用代码来执行如下这样简单的操作:

using (var uow = new UnitOfWork())
{
    // all database interactions happen inside of here

    var book = new Book();
    // set a bunch of properties, etc.
    uow.BookRepository.Add(book);
    uow.ShelfRepository.Add(someShelf);
    // and so on...

    uow.Commit();
}

这个想法是存储库本身不是数据库交互的主要焦点。它们是工作单元的属性,它本身就是焦点。它可能看起来像这样:

public class UnitOfWork : DbContext, IUnitOfWork
{
    public DbSet<Book> DBBooks { get; set; }
    public DbSet<Shelf> DBShelves { get; set; }

    private IBookRepository _bookRepository;
    public IBookRepository BookRepository
    {
        get
        {
            if (_bookRepository == null)
                _bookRepository = new BookRepository(this);
            return _bookRepository;
        }
    }

    private IShelfRepository _shelfRepository;
    public IShelfRepository ShelfRepository
    {
        get
        {
            if (_shelfRepository == null)
                _shelfRepository = new ShelfRepository(this);
            return _shelfRepository;
        }
    }

    public UnitOfWork() : base("Name=MyDB") { }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // If you use fluent syntax mapping, initialize those here
        modelBuilder.Configurations.Add(new BookMap());
        modelBuilder.Configurations.Add(new ShelfMap());
    }

    public void Commit()
    {
        SaveChanges();
    }
}

任何给定的存储库,就像你拥有的存储库一样,主要只是对数据库上下文的传递:

public class BookRepository : IBookRepository
{
    private UnitOfWork _unitOfWork;

    public IQueryable<Book> Books
    {
        get { return _unitOfWork.DBBooks; }
    }

    public BookRepository(UnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

    public void Add(Book model)
    {
        _unitOfWork.DBBooks.Add(model);
    }

    public void Remove(Book model)
    {
        _unitOfWork.DBBooks.Remove(model);
    }
}

(请注意,我的原始代码严格使用依赖注入,因此消费代码实际上从未实际看到这些实现,只有接口。因此公共DBSet<>属性仍然只在DAL内部。您可能需要调整你的需求。)

本设计中的工作单元背后的想法是,您有一个协调实际数据库交互的对象,并且存储库挂起该对象。这允许您与实体框架数据库上下文公开的数据完全交互,并且最后只有一次保存(或回滚)。当然,当您的逻辑从一个存储库切换到另一个存储库时,由于您没有提交半完成的更改,所以还有一个额外的好处,整个事情都包含在一种隐式事务中。

答案 1 :(得分:1)

这里的问题是,例如 StringBuilder sb = new StringBuilder(); String line; try(BufferedReader reader = new BufferedReader(new FileReader(newFile))) { while ((line = reader.readLine()) != null) { if (!line.isEmpty()) { //clear states boolean matchedPreviously = false; char last = line.charAt(0); sb.setLength(0); sb.append(last); for (int i = 1; i < line.length(); i++) { char c = line.charAt(i); if (!matchedPreviously && c == last) { sb.setLength(sb.length()-1); sb.append(2); matchedPreviously = true; } else matchedPreviously = false; sb.append(last = c); } System.out.println(sb.toString()); } } } catch (IOException ex) { ex.printStackTrace(); } BookBll的每个bll对象实例都有自己的ShelfBLL副本。这反过来意味着每个人都有自己的IRepositoryDal副本。所以BLL的每个intance都有自己的DBContext。你需要一个DatabaseContextContext k。基本上有一些选项,但对您的架构进行最小的更改。

你的IrepositoryDal必须是IDisposable

unit of wor

以上接口的实现不需要改变太多,进一步就可以将DbContext作为构造函数中的依赖项。但那是为了以后。现在

public interface IRepositoryDal: IDisposable
    {
        T Add<T>(T entity) where T : Entity;

        void Delete<T>(T entity) where T : Entity;

        IQueryable<T> GetAll<T>() where T : Entity;

        T GetById<T>(int id) where T : Entity;

        bool HasChanges();

        void Save();
    }

Bll类应该将IRepositoryDal作为依赖项,如

public class RepositoryDal : IRepositoryDal
    {
        private readonly DatabaseContext context;

        public RepositoryDal()
        {
            this.context = new DatabaseContext();
        }

        public T Add<T>(T entity) where T : Entity
        {
            return this.context.Set<T>().Add(entity);
        }

        public void Delete<T>(T entity) where T : Entity
        {
            this.context.Set<T>().Remove(entity);
        }

        public IQueryable<T> GetAll<T>() where T : Entity
        {
            return this.context.Set<T>();
        }

        public T GetById<T>(int id) where T : Entity
        {
            return this.context.Set<T>().FirstOrDefault(x => x.Id == id);
        }

        public bool HasChanges()
        {
            return this.context.ChangeTracker.HasChanges();
        }

        public void Save()
        {
            this.context.SaveChanges();
        }

        public void Dispose()
        {
            this.context.Dispose();
        }
    }

您的执行代码变为

public class BookBll
    {
        private readonly IRepositoryDal _repositoryDal;
        public BookBll(IRepositoryDal dal)
        {
            this._repositoryDal = dal;
        }

        public Book AddBook(Book book)
        {
            book = this._repositoryDal.Add(book);
            this._repositoryDal.Save();

            return book;
        }

    }

public class ShelfBll
    {
        private readonly IRepositoryDal _repositoryDal;
        public ShelfBll(IRepositoryDal dal)
        {
            this._repositoryDal = dal;
        }

        public Shelf AddShelf(Shelf shelf)
        {
            shelf = this._repositoryDal.Add(shelf);
            this._repositoryDal.Save();

            return shelf;
        }

    }

希望这会有所帮助 编辑 - 您的项目结构是正确的,您已经分离了接口和实现。因此,对于ServiceLayerInterfaces有多个实现,一个用于EF的实现可能是将来的其他内容。你不需要通用存储库。 EF DBContext类已经提供了一个工作单元等 在上次评论后进一步修改会有所帮助。

class Program
    {
        static void Main(string[] args)
        {

            using (var repo = new RepositoryDal())
            {
                var shelfbll = new ShelfBll(repo);
                var bookBll = new BookBll(repo);
                var shelf = new Shelf { Location = "some location" };
                var book = new Book() { Name = "some book" };
                shelfbll.AddShelf(shelf);
                book.ShelfId = shelf.Id;
                bookBll.AddBook(book);
            }
        }
    }

然后在您的通话代码中

 public class ServiceFactory: IDisposable // hide this behind an interface
{
    private DatabaseContext _context;

    private IRepositoryDal _repository;

    public ServiceFactory()
    {
         _context = new DatabaseContext();
        _repository = new RepositoryDal(_context);
    }

    public IShelfBll ShelfService()
    {
        return new ShelfBll(_repository);
    }

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

    }
}

你可能需要稍微调整一下......但是它的好处是