我创建了一个应用程序,它将多个Visual Studio项目用于ASP.NET MVC应用程序的各个层。我有一个数据访问层,业务逻辑层和表示层。
解决方案如下所示:
我使用的存储库看起来像这样:
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;
}
...
}
问题在于我将书籍连接到书架,并通过另一个业务逻辑层类检索书架。通过这样做,我失去了实体框架上下文。 会发生什么是应用程序不会连接到架子,但它会创建一个新架子。这意味着在此之后我会有两个同名的货架。
我该如何解决这个问题?
答案 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。你需要一个DatabaseContext
或Context
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();
}
}
你可能需要稍微调整一下......但是它的好处是