The process of building asp.net mvc simple project framework (1) fully decouple the Bll layer and the Dal layer

The process of building asp.net mvc simple project framework (1) fully decouple the Bll layer and the Dal layer

  I have been learning asp.net for nearly three months, and spent the most time on asp.net mvc, but I really have some dishes, I have to say that asp.net mvc is really deep. I am currently intern in the company and have seen the code of several projects in the company. The code of the project always stays at the level of a little understanding, and can fix some simple bugs, but the ins and outs of the project are always in the cloud. The architecture of asp.net mvc is always incomprehensible. Therefore, according to the learning video of Chuanzhi blog, I learned a simple structure. I watched the video about the construction of the actual architecture twice, only to have a little clue. I will record it here today. On the one hand, I can deepen my understanding. On the other hand, if I forget it in the future, I can remember it quickly. Of course, if my article is simple and rudimentary. I am fortunate to be seen by those in need and have some help to them. I must be very happy in my heart.

  Okay, less gossip, then I will start writing.

  In this blog, I mainly want to talk about how to use asp.net mvc + EF + spring.net to build a simple project framework. I don't understand all the content before and after. Excuse me.

Before starting to build the framework, do some preparatory work:

(1) New solution:

Create a new solution, and then divide the five folders under the solution, namely Common, Bll, Dal, Model, UI. As shown in the figure below:

(Note: My project is named IotPf.Proj , because I have done an IoT cloud platform project before, and I plan to use .net to redo it and practice it.)

(2) Create a new class library Model in the Model layer, and then add EF database entities

  I won’t be too verbose about adding entities, as I talked about in my previous blog. The results are as follows:

(3) Create new class libraries XXX.Bll and XXX.IBll in Bll, and create new XXX.Dal and XXX.IDal in Dal, as shown in the figure:

(4) Create a new application IotPf.UI under the UI

Okay, the preparations are done, let’s move on to the topic.

1. 1. create a UsersDal.cs file in IofPf.Dal, which contains a series of database operations such as adding, deleting, modifying, and checking the User table . As shown in the figure below:

The following is a list of the most basic writing about adding, deleting, modifying and checking a few of the most common operations written in UserDal, and then calling them (of course, it is just the simplest kind of call, and does not involve decoupling operations such as and):

Look at the code:

 1 public class UserDal
 2 {
 3 IotPfModel context = new IotPfModel();
 4//Use lambda expression query
 5 public IQueryable<Users> GetUsersByLambda(Expression<Func<Users,bool>> wherelambda)
 6 {
 7 return context.Users.Where(wherelambda).AsQueryable();
 8 
 9 }
10 
11//Paging query
12 #region Addition, deletion and modification
13 public Users AddUser(Users user)
14 {
15 context.Users.Add(user);
16 context.SaveChanges();
17 return user;
18}
19 
20 public bool DeleteUser(Users user)
21 {
22 context.Entry(user).State = System.Data.Entity.EntityState.Deleted;
23 return context.SaveChanges()> 0;
24 }
25 
26 public bool UpdateUser(Users user)
27 {
28 context.Entry(user).State = System.Data.Entity.EntityState.Modified;
29 return context.SaveChanges()> 0;
30}
31 #endregion
32}

The above code writes four methods for adding, deleting, modifying, and checking, among which the query method is to customize query conditions based on lambda expressions.

The above generation has the following problems: (Describe in a very popular language, if you don’t write a rookie, you can’t understand it)

(1) First of all, the first thing to do to operate the database in UserDal is to instantiate the context. At present, we are using a single EF operating database, so we instantiate the context of EF, but the way to manipulate data is more than EF. If one day our project suddenly requires the use of NH (another operating database But I haven’t learned how to manipulate the database, so if we write like this, the trouble will be big. Our project will definitely not only have one XXXDal, there are many, if the context changes, then we have to change the code in multiple XXXDal, which will affect the whole body, quite troublesome, this is the first one problem.

Solution : You can put the operation of instantiating context in a class separately, define a GetDalContext() method in the class, and then all XXXDal can call this method, if the context changes, then only need to change this one The file is enough, which is more flexible.

specific methods:

Create a new DbContextFactory.cs file in IotPf.Dal, and then write the code as follows:

1 public class DbContextFactory
2 {
3 public static DbContext GetCurrentDbContext()
4 {
5 return new IotPfModel();
6}
7}

The optimized wording is as follows:

 1 public static DbContext GetCurrentDbContext()
 2 {
 3 DbContext context = CallContext.GetData("DbContext") as DbContext;
 4 if (context == null)
 5 {
 6 context = new IotPfModel();
 7 CallContext.SetData("DbContext", context);
 8 }
 9 
10 return context;
11}

(Don’t ask me why I’m so optimized, I’m embarrassed to tell you, I don’t know what I am!)

(2) Secondly, these methods in UserDal are the most commonly used methods. These four methods are used in almost every XXXDal, so shall we write them in every XXXDal? This is obviously unreasonable, it is useless;

Solution: Commonly used public methods can be encapsulated in a base class, and all classes that need to be used inherit this base class;

specific methods:

Create a new BaseDal.cs under IotPf.Dal, and edit the code as follows:

 1 public class BaseDal<T> where T: class ,new()
 2 {
 3//Instantiate the context
 4 DbContext context = DbContextFactory.GetCurrentDbContext(); 
 5//Use lambda conditions for query
 6 public IQueryable<T> GetEntityByLambda(Expression<Func<T,bool>> wherelambda)
 7 {
 8 return context.Set<T>().Where(wherelambda).AsQueryable();
 9 }
10 
11//Addition, deletion and modification
12 #region Addition, deletion and modification
13 public T AddEntity(T entity)
14 {
15 context.Set<T>().Add(entity);
16 context.SaveChanges();
17 return entity;
18}
19 public bool DeleteEntity(T entity)
20 {
21 context.Entry<T>(entity).State = EntityState.Deleted;
22 return context.SaveChanges()> 0;
23 }
24 public bool UpdateEntity(T entity)
25 {
26 context.Entry<T>(entity).State = EntityState.Modified;
27 return context.SaveChanges()> 0;
28}
29 #endregion
30}

Then, the code in UserDal can be commented out, just let UserDal inherit BaseDal directly, and the same is true for other XXXDal:

1 public class UserDal:BaseDal<Users>

2. Write business logic layer code in IofPf.Bll

Create a new UserService.cs file in the business logic layer, and then write a method to add data:

 public class UserService
 {
        UserDal userDal = new UserDal();//This place has serious problems
        public Users AddUser(Users user)
        {
            userDal.AddEntity(user);
            return user;
        }
  }

Well, the problem has come again, and it has reached a place to be decoupled.

UserDal userDal = new UserDal();//This place has serious problems

The important thing is this sentence:

(1) First of all, the class (UserDal) in the Dal layer is directly used in the Bll layer (UserService), which makes the connection between the two layers very tight and the coupling is too high. As long as there is any change in UserDal, then the Bll layer must Make the corresponding changes.

Improvement 1: Add the interface layer IDal between the Dal layer and the Bll layer. When calling XXXDal later, use IXXXDal to call, so that the two layers are separated by the interface layer:

specific methods:

Create a new interface file IUserDal.cs under IotPf.IDal, and then let UserDal inherit IUserDal. Of course, the interface of BaseDal is the same. The following code is given as follows:

IBaseDal:

 1 public interface IBaseDal<T> where T: class,new()
 2 {
 3 IQueryable<T> GetEntityByLambda(Expression<Func<T, bool>> wherelambda);
 4 
 5 T AddEntity(T entity);
 6 
 7 bool DeleteEntity(T entity);
 8 
 9 bool UpdateEntity(T entity);
10}

IUserDal:

 public interface IUserDal:IBaseDal<Users>
{

}

UserDal and BaseDal must inherit IUserDal and IBaseDal, otherwise they won't be able to be transferred.

After using the interface, the calling process becomes like this:

IUserDal userDal = new UserDal();

In fact, the problem of writing like this is still quite serious. why? Let's look at the second question:

(2) We are sure that more than one file will use UserDal, in fact, many files will use UserDal. However, if one day, my Dal needs to be changed from UserDal written by EF to NHUserDal written by NH, how can it be adjusted? Do I have to replace new UserDal() with new in each file that uses UserDal NHUserDal()?, obviously not. Similar to the context above, we create a new class and take out the operation of new UserDal separately and put it in a separate method, so that only one place needs to be changed.

specific methods:

Create a new AbstractFactory.cs under IotPf.Factory, and then add the following code in it:

1 public class AbstractFactory
2 {
3 public static IUserDal GetCurrentUserDal()
4 {
5 return new UserDal();
6}
7}

Then the calling process is further improved as follows:

   IUserDal userDal = AbstractFactory.GetCurrentUserDal();

This will be much more flexible.

This approach is actually the concept of a factory .

However, this is still not perfect, is it very annoying, why is it so troublesome, can you write code happily? Hahaha, don't panic, trouble is trouble, but this kind of thinking is still very powerful, and there are so many benefits. Look at the problem below:

(3) Next, I will talk about the shortcomings in AbstractFactory.cs. What are the shortcomings?

Now I only wrote one GetCurrentUserDal(), right, it’s convenient to change it, but it’s impossible to have only one UserDal in a project. There will definitely be ADal, BDal, CDal and other Dal in the future. Once EF becomes NH, Isn't it necessary to change a bunch of names in this factory? This is too annoying. How do you do it? look down:

We can write the database operation method used (EF or NH or Ado.net) into a configuration file. Suppose I now have two ways to operate the database, EF and NH. One method of operating the database is written under the project EFDal, and the other is written under the NHDal project, and then the names of the operation methods under the two projects are kept consistent. If you want to use EF operation, then reference EFDal in the configuration file, if you use NH operation, then reference NHDal in the configuration file, so that you can switch freely, is it very convenient, haha, it is really slippery.

specific methods:

1. add the following code here in the web.config file under Ui:

Code:

 <add key="DalAssemblyName" value="IotPf.Dal"/>

Then add the following code under AbstractFactory.cs:

 //Configuration file settings
        public static string AssemblyName = System.Configuration.ConfigurationManager.AppSettings["DalAssemblyName"];
        public static IUserDal GetUserDal()
        {
           //Configuration file settings, moved outside the method, will be used by multiple GetXXXDal
           //string AssemblyName = System.Configuration.ConfigurationManager.AppSettings["DalAssemblyName"];
            return Assembly.Load(AssemblyName).CreateInstance(AssemblyName + ".UserDal") as IUserDal;
        }

Because the configuration file is used here, don't forget to add the following reference:

This is the concept of the so-called abstract factory.

Just like I use IotPf.Dal now, then I write IotPf.Dal in the value place in the configuration file. If I need to use IotPf.XXXDal in the future, then I will change the value to IotPf.XXXDal.

3. Let's talk about DbSession below

What is DbSession? Speaking of DbSession, we must continue from the UserService above. Let's talk about the context.SaveChanges() of the context.

We should all know that if our operation context adds, deletes, or changes the database, then after the operation is over, there needs to be a context.SaveChanges() operation to save the changes to the entity in the database. Currently, our operation is written in UserDal. However, there is a problem with this, what is the problem, let's take a look at the following code:

1 IUserDal userDal = AbstractFactory.GetUserDal();
2 public Users AddUser(Users user)
3 {
4 userDal.AddEntity(user);
5 userDal.DeleteEntity(user);
6 userDal.UpdateEntity(user);
7 return user;
8 }

In this code, three methods in UserDal are called, and three different operations are performed on the database. After each operation, the context.SaveChanges() operation will be executed once, which means that it has interacted with the database three times. Do you think that three times is a bit too much? Obviously, it can be less. In the future, if the amount of code is large, there may be more interactions, and the performance is very poor. Here, we obviously can do it with only one interaction, how to do it, we can migrate the SaveChanges() operation from UserDal to UserService, and do not write context.SaveChanges for each method in UserDal. () operation, but the single operation of adding, deleting and modifying in UserService is completed together and then submitted in a unified way. Wouldn't it be better? It looks like this:

(This code only shows the meaning of unified submission, not the way it is written in the actual program. Students who read this should not stick this code)

1 IUserDal userDal = AbstractFactory.GetUserDal();
2 public Users AddUser(Users user)
3 {
4 userDal.AddEntity(user);
5 userDal.DeleteEntity(user);
6 userDal.UpdateEntity(user);
        context.SaveChanges();//Unified submission
7 return user;
8 }

The following is a specific approach:

1. create a new class DbSession.cs under IotPf.Factory, and then write the following code in it:

 1 public class DbSession
 2 {
 3//Declare UserDal
 4 public IUserDal UserDal
 5 {
 6 get
 7 {
 8 return AbstractFactory.GetUserDal();
 9 }
10}
11//Save the entity
12 public int SaveChanges()
13 {
14 return DbContextFactory.GetCurrentDbContext().SaveChanges();
15}
16}

Did you see it? The operation of declaring UserDal is now moved to DbSession.

Then, UserService.cs now becomes the following writing:

 1 public class UserService
 2 {
 3 
 4//IUserDal userDal = AbstractFactory.GetUserDal();
 5 DbSession dbSession = new DbSession();
 6 public Users AddUser(Users user)
 7 {
 8 dbSession.UserDal.AddEntity(user);
 9 dbSession.UserDal.DeleteEntity(user);
10 dbSession.UserDal.UpdateEntity(user);
11 dbSession.SaveChanges();
12 return user;
13}
14}

The save operation is no longer controlled by the Dal layer, but submitted to the Bll layer, which is controlled by the Bll layer. Of course, don't forget to go back to the Dal layer and remove the SaveChanges operation in the original code: the code after removal is as follows:

 1 public class BaseDal<T> where T: class, new()
 2 {
 3//Instantiate the context
 4 DbContext context = DbContextFactory.GetCurrentDbContext(); 
 5//Use lambda conditions for query
 6 public IQueryable<T> GetEntityByLambda(Expression<Func<T,bool>> wherelambda)
 7 {
 8 return context.Set<T>().Where(wherelambda).AsQueryable();
 9 }
10 
11//Addition, deletion and modification
12 #region Addition, deletion and modification
13 public T AddEntity(T entity)
14 {
15 context.Set<T>().Add(entity);
16//context.SaveChanges();
17 return entity;
18}
19 public bool DeleteEntity(T entity)
20 {
21 context.Entry<T>(entity).State = EntityState.Deleted;
22//return context.SaveChanges()> 0;
23 return true;
24 }
25 public bool UpdateEntity(T entity)
26 {
27 context.Entry<T>(entity).State = EntityState.Modified;
28//return context.SaveChanges()> 0;
29 return true;
30}
31 #endregion
32}

Is it better now? The number of interactions with the database will be greatly reduced in the future, so the performance should be improved a lot, haha.

Next, we need to isolate the DbSession and Bll layers, which is this operation:

You should be familiar with the road now, first create a new interface layer IDbSession.cs, and then create a new DbSessionFactory.cs, the code is given below:

IDbSession.cs

1 public interface IDbSession
2 {
3 IUserDal UserDal {get;}
4 int SaveChanges();
5}

DbSessionFactory.cs:

 1 public static IDbSession GetCurrentDbSession()
 2 {
 3 IDbSession dbSession = CallContext.GetData("DbSession") as IDbSession;
 4 if (dbSession == null)
 5 {
 6 dbSession = new DbSession();
 7 CallContext.SetData("DbSession", dbSession);
 8 }
 9             return dbSession;
10   }

现在UserService.cs中的代码就变成这样:

1     IDbSession dbSession = DbSessionFactory.GetCurrentDbSession();
2     public Users AddUser(Users user)
3     {
4             dbSession.UserDal.AddEntity(user);
5             dbSession.UserDal.DeleteEntity(user);
6             dbSession.UserDal.UpdateEntity(user);
7             dbSession.SaveChanges();
8             return user;
9     }

写到这里,数据访问层Dal和业务逻辑成Bll之间的解耦基本已经说完了,我感觉应该还是讲的比较清楚的了哈。

这个框架搭建的前半部分就已经讲完了,后面关于业务逻辑层Bll和展示层UI之间的系列操作,我留到下一篇讲啦,哈哈,一下子写完我自己hold不住,你估计也看不下去了吧。

下面总结一下哈:

本篇内容扯了那么长,我也真是够有尿性的了,总结起来其实也就几个内容而已啦:

(1)使用接口层进行层与层之间的隔离;

(2)对重复多次使用的方法进行基类封装;

(3)使用工厂的概念,对数据库操作方法进行配置,便于应对数据库的更换,提高框架灵活性;

(4)使用DbSession将数据库保存操作的权限有Dal层提交到业务逻辑层,减少与数据库交互次数,提高代码性能;

下一篇将介绍spring.net的使用,虽然我自己也不咋会 ,但还是把自己理解的写出来吧,以写代练,加深理解,大神们不要见笑啊。

我的eMail:3074596466@qq.com

完成框架代码就不往上贴了哈,有需要的同学可以给我发邮件索取。

Reference:https://cloud.tencent.com/developer/article/1503518 asp.net mvc 简单项目框架的搭建过程(一)对Bll层和Dal层进行充分解耦 - 云+社区 - 腾讯云