.NET Layer Relationship Management

I am really recreating the default architecture of my projects. In this post, I want you to not only help me, but to think, tell and improve the presented methods. This can be a very rewarding task. This default architecture is free to use for everyone. So here is the description: Basically MVC is our point of view. We follow OOP and layer programming concepts. We will also use SQL Server and EF DB First . So here is what I have done so far: Ive created 4 layers in my solution:

  • Domain: domain classes only
  • DAL: responsible for data access, containing UOWs and repositories and data access checks.
  • BL: Unique Responsible Business Logic and Unique DAL Boss.
  • User Interface: It's Not That Important! (how even it can be a console application!)

Ive implemented BL using common functions like getAll and getById , and fund functions. funtss.these will be called DAL generic funcs, which is also ready. Now the important question is: is it right to implement DAL functions as GENERIC? Please consider this scenario:

From the UI, we send the model to action, and in action we call BL Func ( BL.insert (model)). The BL.insert function would call something like DAL.Add (model) (notifying BL one day will call DAL and tell it what it wants). Now the DAL.Add function should insert 3 records into 3 different tables (the model is transferred from the user interface layer). I think this cannot be done with the DAL GENERIC functions. So, what is the correct and standard way to implement layers and relationships with a note on separation of concerns?

In my action, I would:

 [HttpPost] public ActionResult Insert() { Bl.EntityBase.Article.Insert(new Article()); return RedirectToAction("Index"); } 

in BL I would:

 public void Insert(T obj) { Da.Repository<T>().Insert(obj); } 

And in my DAL, I have:

 public virtual void Insert(T entity) { DbEntityEntry dbEntityEntry = Db.Entry(entity); if (dbEntityEntry.State != EntityState.Detached) { dbEntityEntry.State = EntityState.Added; } else { Set.Add(entity); } } 
+5
source share
3 answers

IF the model that you pass to the DAL through your BL is a view model, then the logic will be in your DAL, otherwise you should not go through a model that will perform 3 inserts for 3 different tables.

+1
source

You should implement a repository template with generic repositories in the DAL. To add an abstraction, you need to use the IRepository interface, and its implementation must be entered using a dependent converter. You can also achieve the same in BL (Service) using the interface.

Here is a good discussion: The difference between a repository and a service level?

You can also improve it with the Unit Of Work template.

Here is a complete snippet of code that shows how we can achieve abstraction between multiple layers:


Repository

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

Uow

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

Service


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

controller


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

}


Hope this code helps many users and you, of course.

+1
source

Before we begin, it might make sense to separate the concepts of contracts with layers. You specified 4 layers, but they can also be described as 3 levels and one contract scheme (domain contract scheme), which is an end-to-end split-up situation containing problems for DAL, BL, and potentially higher levels.

  • Domain: domain classes only
  • DAL: responsible for data access, containing UOWs and repositories and data access checks.
  • BL: Unique Responsible Business Logic and Unique DAL Boss.
  • User Interface: It's Not That Important! (how even it can be a console application!)

As for typical N-tier SOA architectures of the LOB type, where services can be consumed by other teams or clients, it usually makes sense to strive to increase three contractual schemes.

The first are the schemes of contracts between consumers and the services you want to provide. They may be versions. These schemes may be case-specific and provide โ€œrepresentationsโ€ of the base object's scheme in various forms of normalization and composition. These schemes may also include metadata support to support specific case-specific requirements, such as a collection of search lists and specifying specific user permissions.

The second is the domain domain scheme in which all your current one works. This is always relevant and represents a logical diagram in an object-oriented design.

The third is the physical diagram in which your data is located. It is always relevant and contains your data in any suitable design dictated by storage technology, whether in a relational design or otherwise. Please note: although in the general case the physical scheme and the logical scheme may look the same or even identical, sometimes there are limitations in the physical storage technology that may require the physical scheme to be different. For example, in the MS Sql Server table there should not be more than 8060 bytes per row, and in some cases one logical object data object can be stored in several physical tables connected to each other, if MS Sql Server is used.

Based on these three schemes, you can then create your own solution to solve the problems associated with supporting these schemes.

To manage storage and physical scheme management, select a technology / storage strategy (for example: Sql Server, Oracle, MySql, xml, MongoDb, etc.)

To address I / O to a physical schema and transfer it from a physical schema to an entity schema, you enter an IDataAccess interface.

To eliminate business rules that must always be followed around an entity or set of objects, regardless of the option used, you enter the IBusiness level interface.

To address rules that are specific use cases that you do not want to repeat between multiple clients, and to translate between an entity schema and a service contract schema, you enter the IApplicationService level interface.

To address the external and external objects of the contract scheme transport, you enter the corresponding classes of the host controller (ApplicationServiceController, ApplicationServiceWCFHost or ApplicationService.asmx, etc.).

Here is a set of interfaces contained in a common namespace strategy that you can implement to provide you with abstractions for entity schemas, as well as for accessing data and business layers.

AcmeFrameworkContracts.dll

 public class Response {} public class Response<TResponse> : Response where TResponse : Response<TResponse> {} 

AcmeFramework.dll

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

Use it like this:

UserDomain.dll

links: 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

links: 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) { ... } } } 

You can add up to UOW if you are so addicted. You can also expose services and hosts, more logical entities, and not use a specific case, as you consider necessary, but remember to not place the domain schema classes in your contract library. Thus, you can develop contracts regardless of the underlying implementations. And you can subclass Entity<TEntity, TDataObject, TDataObjectList, TIBusiness, TIDataAccess> into a domain contract library such as UserDomain.Contracts.dll and leave implementations in domain libraries such as UserDomain.dll if you want this isolation .

+1
source

All Articles