繁体   English   中英

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

[英]Entity Framework Context in a multi-tier ASP.NET MVC Application

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

解决方案如下所示:

Visual Studio解决方案

我正在使用如下所示的存储库:

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;
    }
    ...
}

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

我怎么解决这个问题?

而不是遮盖每个存储库后面的单独的数据库上下文对象,而是公开一个本身类似于数据库上下文并跨越所有存储库的中心对象。 类似于“工作单元”对象。

这里一个很老的例子 ,但是模式仍然成立。

本质上,目标是允许使用代码的代码执行以下操作:

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内部。您可能需要进行调整以满足您的需求。)

此设计中的工作单元背后的思想是,您有一个对象来协调实际的数据库交互,并且存储库挂在该对象上。 这使您可以与Entity Framework DB Context公开的数据进行完全交互,并且最后只进行一次保存(或回滚)。 当然,这样做的另一个好处是,当您的逻辑从一个存储库切换到另一个存储库时,您不必提交半完成的更改,因此整个过程都封装在一种隐式事务中。

这里的问题是,bll对象的每个实例(例如BookBllShelfBLL都有自己的IRepositoryDal副本。 这又意味着每个人都有自己的DatabaseContext副本。 因此BLL的每个实例都有其自己的DBContext。 您需要有一个Context或一个unit of wor 基本上有几个选项可用,但对体系结构的更改最少。

您的IrepositoryDal必须是IDisposable,因为

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();
    }

上面接口的实现不需要做太多更改,进一步,您还可以将DbContext作为构造函数中的依赖项。 但是那是以后的事。 目前

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();
        }
    }

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

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;
        }

    }

您执行的代码变成

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);
            }
        }
    }

希望这对Edit有所帮助-您的项目结构正确,接口和实现已分离。 因此,对于ServiceLayerInterfaces,它有多种实现方式,一种是针对EF,另一种可能是将来的实现。 您不需要通用存储库。 EF DBContext类已经提供了工作单位等, 在最后评论之后将有进一步的编辑帮助。

 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();

    }
}

然后在您的呼叫代码中

static void Main(string[] args)
        {

            using (var factory = new ServiceFactory())
            {
                var shelf = new Shelf { Location = "some location" };
                var book = new Book() { Name = "some book" };
                factory.ShelfService().AddShelf(shelf);
                book.ShelfId = shelf.Id;
                factory.BookService.AddBook(book);
            }
        }

您可能需要对其进行一点调整。。。。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM