簡體   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