我实际上正在重新创建项目的默认架构。 在这篇文章中,我希望你不仅可以帮助我,还可以思考,讲述和改进现有方法。这可能是非常有用的挑战。 此默认架构可以与所有人自由共享。 所以这里是描述: 基本上 MVC 是我们的观点。我们遵循 OOP 和图层编程概念。我们还将使用 SQL Server 和 EF DB First 。 所以这是我到目前为止所做的事情: 我在我的解决方案中创建了4层:
我已经用getAll
和getById
以及crud func这样的泛型函数实现了BL。这些函数会调用DAL泛型函数,这也是准备好的。
现在一个重要的问题是:将DAL函数实现为GENERIC是否正确?
请考虑这种情况:
从UI我们发布模型到动作,在行动中我们称为BL Func(BL.insert
(模型))。 BL.Insert
func会调用DAL.Add
(模型)之类的东西(注意BL会调用DAL一次并告诉它它想要什么)。现在DAL.Add
func必须将3条记录插入3个不同的表中(模型从UI层传递)。
我认为这不能通过DAL GENERIC funcs来实现。那么,在注意到关注点分离的情况下,实现图层和关系的正确和标准方法是什么?
在我的行动中我会:
[HttpPost]
public ActionResult Insert()
{
Bl.EntityBase.Article.Insert(new Article());
return RedirectToAction("Index");
}
BL中的我会:
public void Insert(T obj)
{
Da.Repository<T>().Insert(obj);
}
在我的DAL中,我有:
public virtual void Insert(T entity)
{
DbEntityEntry dbEntityEntry = Db.Entry(entity);
if (dbEntityEntry.State != EntityState.Detached)
{
dbEntityEntry.State = EntityState.Added;
}
else
{
Set.Add(entity);
}
}
答案 0 :(得分:1)
如果您通过BL传递给DAL的模型是视图模型,则逻辑将在您的DAL中,否则您不应传递将为3个不同的表执行3次插入的模型。
答案 1 :(得分:1)
您应该在DAL中使用通用存储库实现存储库模式。 为了放置抽象,你必须使用IRepository接口,它的实现应该使用依赖解析器注入。 您也可以使用界面在BL(服务)中实现相同的功能。
以下是一个很好的讨论:Difference between Repository and Service Layer?
您还可以使用工作单元模式对其进行改进。
这是一个完整的代码片段,展示了如何在多个层之间实现抽象:
public interface IRepository<T> where T : class
{
DbContext GetContext();
IQueryable<T> GetAll();
IQueryable<T> FindBy(Expression<Func<T, bool>> predicate);
void Add(T entity);
void Delete(T entity);
void DeleteAll(IEnumerable<T> entity);
void Edit(T entity);
bool Any();
}
public class Repository<T> : IRepository<T> where T : class
{
private readonly DbContext _context;
private readonly IDbSet<T> _dbset;
public Repository(DbContext context)
{
_context = context;
_dbset = context.Set<T>();
}
public virtual DbContext GetContext()
{
return _context;
}
public virtual IQueryable<T> GetAll()
{
return _dbset;
}
public IQueryable<T> FindBy(Expression<Func<T, bool>> predicate)
{
var query = _dbset.Where(predicate).AsQueryable();
return query;
}
public virtual void Add(T entity)
{
_dbset.Add(entity);
}
public virtual void Delete(T entity)
{
var entry = _context.Entry(entity);
entry.State = EntityState.Deleted;
_dbset.Remove(entity);
}
public virtual void DeleteAll(IEnumerable<T> entity)
{
foreach (var ent in entity)
{
var entry = _context.Entry(ent);
entry.State = EntityState.Deleted;
_dbset.Remove(ent);
}
}
public virtual void Edit(T entity)
{
var entry = _context.Entry(entity);
_dbset.Attach(entity);
entry.State = EntityState.Modified;
}
public virtual bool Any()
{
return _dbset.Any();
}
}
public interface IUnitOfWork : IDisposable
{
IRepository<TEntity> GetRepository<TEntity>() where TEntity : class;
void Save();
}
public class UnitOfWork<TContext> : IUnitOfWork where TContext : DbContext, new()
{
private readonly DbContext _ctx;
private readonly Dictionary<Type, object> _repositories;
private bool _disposed;
public UnitOfWork()
{
_ctx = new TContext();
_repositories = new Dictionary<Type, object>();
_disposed = false;
}
public IRepository<TEntity> GetRepository<TEntity>() where TEntity : class
{
// Checks if the Dictionary Key contains the Model class
if (_repositories.Keys.Contains(typeof (TEntity)))
{
// Return the repository for that Model class
return _repositories[typeof (TEntity)] as IRepository<TEntity>;
}
// If the repository for that Model class doesn't exist, create it
var repository = new Repository<TEntity>(_ctx);
// Add it to the dictionary
_repositories.Add(typeof (TEntity), repository);
return repository;
}
public void Save()
{
// save all changes together
_ctx.SaveChanges();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_ctx.Dispose();
}
_disposed = true;
}
}
}
服务
public interface IService
{
IList<Users> GetUserDetails(int userId);
}
public class Service : IService
{
private readonly IRepository<Users> _userRepository;
public Service (IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
_userRepository=_unitOfWork.GetRepository<Users>();
}
public IList<Users> GetUserDetails(int userId)
{
return _userRepository.GetAll();
}
}
控制器
public class HomeController
// use the same patter which has been used in service
{
private readonly IService _service;
public HomeController(IService service)
{
_service=service;
}
public ActionResult Index()
{
return _service. GetUserDetails(/*userid*/);
}
}
希望这段代码能够帮助很多用户和你。
答案 2 :(得分:1)
在我们开始之前,将合同的概念与层分开可能是有意义的。您列出了4个图层,但也可以将它们描述为3个图层和一个合约架构(域合同架构),它是交叉剪切,包含对DAL,BL和可能更高级别图层的关注。
- 域:JUST域类
- DAL:负责访问数据,包含UOW和存储库以及与数据访问相关的验证。
- BL:独特的业务逻辑和DAL的独特老板。
- UI:这不是那么重要! (甚至它可能是一个控制台应用程序!)
醇>
关于典型的LOB风格的SOA n层体系结构,其他服务可能被其他团队或客户使用,一般来说,争取三个以上的契约模式可能是有意义的。
第一个是消费者与您希望提供的服务之间的合同模式。这些可能是版本的。这些模式可以是特定于用例的,并且以各种形式的规范化和组合提供底层实体模式的“表示”。这些模式甚至可能包括支持元数据以支持特定于用例的需求,例如查找列表的数量和指示特定于用例的用户权限。
第二个是所有当前实施操作的实体域架构。这始终是最新的,代表了面向对象设计方式的逻辑模式。
第三个是数据所在的物理架构。这始终是最新的,并且以存储技术所要求的任何适当的设计方式包含您的数据,无论是以关系设计方式还是其他方式。请注意,虽然物理架构和逻辑架构通常看起来相似甚至相同,但有时在物理存储技术中存在可能要求物理架构不同的限制。例如,MS Sql Server表中每行不应超过8060个字节,并且在某些情况下,如果使用MS Sql Server,则单个逻辑实体数据对象可以存储在多个一对一相关物理表中。
考虑到这三种模式,您可以构建解决方案以解决支持这些模式的问题。
要解决物理架构的存储和管理问题,请选择存储技术/策略(即:Sql Server,Oracle,MySql,xml,MongoDb等)
要解决物理模式的I / O并从物理模式转换为实体模式,请引入IDataAccess图层接口。
要解决必须始终围绕实体或实体集强制执行的业务规则,无论用例如何,都要引入IBusiness层接口。
要解决特定于用例的规则,您不希望在多个客户端之间重复以及在实体架构和服务契约架构之间进行转换,请引入IApplicationService层接口。
要解决外部公开服务及其合同模式传输对象的问题,请引入相应的主机控制器类( ApplicationService Controller, ApplicationService WCFHost或 ApplicationService < /em>.asmx等。)。
以下是通用命名空间策略中包含的一组接口,您可以实现这些接口,为您提供实体架构以及数据访问和业务层的抽象。
<强> AcmeFrameworkContracts.dll 强>
public class Response {}
public class Response<TResponse> : Response where TResponse : Response<TResponse> {}
<强> AcmeFramework.dll 强>
参考: AcmeFrameworkContracts.dll
namespace AcmeFramework
{
namespace Entity
{
public abstract class Entity<TEntity, TDataObject, TDataObjectList, TIBusiness, TIDataAccess, TPrimaryKey>
where TEntity : Entity<TEntity, TDataObject, TDataObjectList, TIBusiness, TIDataAccess, TPrimaryKey>
where TDataObject : Entity<TEntity, TDataObject, TDataObjectList, TIBusiness, TIDataAccess, TPrimaryKey>.BaseDataObject
where TDataObjectList : Entity<TEntity, TDataObject, TDataObjectList, TIBusiness, TIDataAccess, TPrimaryKey>.BaseDataObjectList, new()
where TIBusiness : Entity<TEntity, TDataObject, TDataObjectList, TIBusiness, TIDataAccess, TPrimaryKey>.IBaseBusiness
where TIDataAccess : Entity<TEntity, TDataObject, TDataObjectList, TIBusiness, TIDataAccess, TPrimaryKey>.IBaseDataAccess
where TPrimaryKey : IComparable<TPrimaryKey>, IEquatable<TPrimaryKey>
{
public class BaseDataObject
{
public TPrimaryKey Id;
}
public class BaseDataObjectList : CollectionBase<TDataObject>
{
public TDataObjectList ShallowClone() { ... }
}
public interface IBaseBusiness
{
TDataObjectList LoadAll();
TDataObject LoadById(TPrimaryKey id);
TDataObject LoadByIds(IEnumerable<TPrimaryKey> ids);
IQueryable<TDataObject> Query();
IQueryable<TDataObject> FindBy(Expression<Func<T, bool>> predicate);
void Delete(TPrimaryKey ids);
void Delete(IEnumerable<TPrimaryKey> ids);
void Save(TDataObject entity);
void Save(TDataObjectList entities);
ValidationErrors Validate(TDataObject entity); // <- Define ValidationErrors as you see fit
ValidationErrors Validate(TDataObjectList entities); // <- Define ValidationErrors as you see fit
}
public abstract BaseBusiness : IBaseBusiness
{
private TIDataAccess dataAccess;
protected BaseBusiness(TIDataAccess dataAccess) { this.dataAccess = dataAccess; }
}
public interface IBaseDataAccess
{
IQueryable<TDataObject> Query();
IQueryable<TDataObject> FindBy(Expression<Func<T, bool>> predicate);
void DeleteBy(Expression<Func<T, bool>> predicate);
void Save(TDataObjectList entities);
}
}
}
namespace Application
{
public interface IBaseApplicationService {}
public class BaseApplicationServiceWebAPIHost<TIApplicationService> : ApiController where TIApplicationService : IBaseApplicationService
{
private TIApplicationService applicationService;
public BaseApplicationServiceWebAPIHost(TIApplicationService applicationService) { this.applicationService = applicationService; }
}
public class BaseApplicationServiceWCFHost<TIApplicationService> where TIApplicationService : IBaseApplicationService
{
private TIApplicationService applicationService;
public BaseApplicationServiceWCFHost(TIApplicationService applicationService) { this.applicationService = applicationService; }
}
}
像这样使用它:
<强> UserDomain.dll 强>
引用: AcmeFramework.dll
namespace UserDomain
{
public class User : Entity<User, User.DataObject, User.DataObjectList, User.IBusiness, User.IDataAccess, Guid>
{
public class DataObject : BaseDataObject
{
public string FirstName;
public string LastName;
public bool IsActive { get; }
}
public class DataObjectList : BaseDataObjectList {}
public interface IBusiness : IBaseBusiness
{
void DeactivateUser(Guid userId);
}
public interface IDataAccess : IBaseDataAccess {}
public class Business
{
public Business(User.IDataAccess dataAccess) : base(dataAccess) {}
public void DeactivateUser(Guid userId)
{
...
}
}
}
public class UserPermission : Entity<UserPermission, UserPermission.DataObject, UserPermission.DataObjectList, UserPermission.IBusiness, UserPermission.IDataAccess, Guid>
{
public class DataObject : BaseDataObject
{
public Guid PermissionId;
public Guid UserId;
}
public class DataObjectList : BaseDataObjectList {}
public interface IBusiness : IBaseBusiness {}
public interface IDataAccess : IBaseDataAccess {}
public class Business
{
public Business(UserPermission.IDataAccess dataAccess) : base(dataAccess) {}
}
}
public class Permission : Entity<Permission, Permission.DataObject, Permission.DataObjectList, Permission.IBusiness, Permission.IDataAccess, Guid>
{
public class DataObject : BaseDataObject
{
public string Code;
public string Description;
}
public class DataObjectList : BaseDataObjectList {}
public interface IBusiness : IBaseBusiness {}
public interface IDataAccess : IBaseDataAccess {}
public class Business
{
public Business(Permission.IDataAccess dataAccess) : base(dataAccess) {}
}
}
}
<强> UserManagementApplicationContracts.dll 强>
参考: AcmeFrameworkContracts.dll
namespace UserManagement.Contracts
{
public class ReviewUserResponse : Response<ReviewUserResponse>
{
public class UserPermission
{
public Guid id;
public string permission; // <- formatted as "permissionCode [permissionDescription]"
}
public class User
{
public Guid id;
public string firstName;
public string lastName;
public List<UserPermissions> permissions;
}
public User user;
}
public class EditUserResponse : Response<EditUserResponse>
{
public class Permission
{
public Guid id;
public string permissionCode;
public string description;
}
public class UserPermission
{
public Guid id;
public Guid permissionId;
}
public class User
{
public Guid id;
public string firstName;
public string lastName;
public List<UserPermissions> permissions;
}
public List<Permission> knownPermissions;
public User user;
}
public interface IUserManagementApplicationService : IBaseApplicationService
{
Response EditUser(Guid userId);
Response SaveUser(EditUserResponse.user user);
Response ViewUser(Guid userId);
}
}
<强> UserManagementApplicationImplementation.dll 强>
引用: AcmeFramework.dll , AcmeFrameworkContracts.dll , UserManagementApplicationContracts.dll , UserDomain.dll
namespace UserManagement.Implementation
{
public class UserManagementApplicationService : IUserManagementApplicationService
{
private User.IBusiness userBusiness;
private UserPermissions.IBusiness userPermissionsBusiness;
private Permission.IBusiness permissionBusiness;
public UserManagementApplicationService(User.IBusiness userBusiness, UserPermission.IBusiness userPermissionsBusiness, Permission.IBusiness permissionBusiness)
{
this.userBusiness = userBusiness;
this.userPermissionsBusiness = userPermissionsBusiness;
this.permissionBusiness = permissionBusiness;
}
public Response EditUser(Guid userId)
{
...
}
public Response SaveUser(EditUserResponse.user user)
{
...
}
public Response ViewUser(Guid userId)
{
...
}
}
}
如果您愿意,可以在UOW中进行分层。您还可以根据需要公开更具逻辑实体特定的服务和主机,而不是特定于用例,但请记住不要将域架构类放在合同库中。这样,您就可以独立于底层实现来发展合同。您可以将Entity<TEntity, TDataObject, TDataObjectList, TIBusiness, TIDataAccess>
子类移动到域合约库(例如UserDomain.Contracts.dll),如果您希望隔离,则将实现保留在域库(如UserDomain.dll)中。