Home>

The levels and calling relationships of the previous items are explained.The relationship is as follows

When using a three-tier architecture,Researched the necessity of bll layer,I feel that the business logic can be implemented in the controller. There is no need to do a separate project.Another layer that is too much will affect performance.Later, I still separated the business logic.The reasons are as follows:

The business logic is written in the controller and the code looks confusing.Over time, the code is easy to understand. It would not be much to write logic repetitive code directly in the controller.Development efficiency is low. Sub-projects facilitate code reuse,Sometimes it can be used directly in other projects with minor modifications.

For performance, I think it will definitely affect more layering.But it won't be big.Now the update speed of hardware is much faster than software.Dealing with business logic is easy,Instantiating several classes has little effect on performance.Generally speaking, website operation is basically a process of storing and retrieving databases.Business logic is still relatively small,It ’s just that there are more pictures and animations used on websites now.The effect is more gorgeous.I think the efficiency bottleneck of the website mainly appears on the server's bandwidth, io performance and access to the database.What can be done in terms of code is to optimize database access.For general projects,For a few percent of the operating efficiency is far less important than improving development efficiency and easier code management,Just be able to fulfill the demand,What is running efficiency is what Daniel does.

For the four items idal, dal, ibll, bll:

idal writes a base interface. Several database operation methods are fixed in the interface.All other interfaces inherit from this interface;

The dal project makes a base class to implement the base interface of this idal, and other classes inherit from the base class.

Similarly, a base interface is also written in ibll, which fixes several basic operation methods.Similarly other interfaces also inherit from this base interface

A base class is also written in ibll to implement the base interface in ibll. Other classes inherit from this base class.

Here is the basic pattern of building code based on user actions:

I. Model

Write three model classes here.Open ninesk.models and add user, usergroup, and userconfig respectively.

1. The user model—the user class

User model or account model,Why say so look at the following code

using system;
using system.componentmodel.dataannotations;
namespace ninesky.models
{
 ///<summary>
 ///user model
 ///<remarks>
 ///Create:2014.02.02<br />
 ///Modification:2014.02.05
 ///</remarks>
 ///</summary>
 public class user
 {
  [key]
  public int userid {get;set;}
  ///<summary>
  ///username
  ///</summary>
  [required (errormessage="required")]
  [stringlength (20, minimumlength=4, errormessage="{1} to {0} characters")]
  [display (name="Username")]
  public string username {get;set;}
  ///<summary>
  ///user group id
  ///</summary>
  [required (errormessage="Required")]
  [display (name="User Group id")]
  public int groupid {get;set;}
  ///<summary>
  ///display name
  ///</summary>
  [required (errormessage="Required")]
  [stringlength (20, minimumlength=2, errormessage="{1} to {0} characters")]
  [display (name="Display Name")]
  public string displayname {get;set;}
  ///<summary>
  ///password
  ///</summary>
  [required (errormessage="Required")]
  [display (name="Password")]
  [datatype (datatype.password)]
  public string password {get;set;}
  ///<summary>
  ///email
  ///</summary>
  [required (errormessage="Required")]
  [display (name="Mailbox")]
  [datatype (datatype.emailaddress)]
  public string email {get;set;}
  ///<summary>
  ///user status<br />
  ///0 is normal, 1 is locked, 2 has not passed email verification,3 failed administrator
  ///</summary>
  public int status {get;set;}
  ///<summary>
  ///Registration time
  ///</summary>
  public datetime registrationtime {get;set;}
  ///<summary>
  ///Last login time
  ///</summary>
  public datetime logintime {get;set;}
  ///<summary>
  ///Last login IP
  ///</summary>
  public datetime loginip {get;set;}
  public virtual usergroup group {get;set;}
 }
}

This model class only contains attributes such as username, password, user group, display name, mailbox, etc.Purely basic account information,The purpose is to allow users to fill in as little information as possible when registering.If necessary, you can write a new class to associate with the account.The user needs to log in before filling in (such as capital information, personal information, contact information, etc.).I won't consider these here). The display name here can be used as a nickname, real name, etc. as needed.

User group model-usergroup class

Note the grouptype of this class, which is used to classify user groups.Easy to manage,It doesn't really mean anything.My idea is to put ordinary registered user groups in the ordinary type,If a large website allows users to upgrade,Restricted to this type of user group.Privileged group can put some user groups like VIP,Need administrator to give,Distinguish ordinary user groups,But there is no management right.Management type user groups need to be given by the background administrator,Can manage articles, comments and consultations.

using system.componentmodel.dataannotations;
namespace ninesky.models
{
 ///<summary>
 ///user group
 ///<remarks>
 ///Created:2014.02.02
 ///Modification:2014.02.08
 ///</remarks>
 ///</summary>
 public class usergroup
 {
  [key]
  public int groupid {get;set;}
  ///<summary>
  ///name
  ///</summary>
  [required (errormessage="required")]
  [stringlength (20, minimumlength=2, errormessage="{1} to {0} words")]
  [display (name="name")]
  public string name {get;set;}
  ///<summary>
  ///User group type<br />
  ///0 ordinary type (ordinary registered user), 1 privilege type (type like VIP), 3 management type (type of management authority)
  ///</summary>
  [required (errormessage="Required")]
  [display (name="User Group Type")]
  public int grouptype {get;set;}
  ///<summary>
  ///description
  ///</summary>
  [required (errormessage="Required")]
  [stringlength (50, errormessage="less than {0} words")]
  [display (name="Description")]
  public string description {get;set;}
 }
}

3, user configuration model class-userconfig classThis class is some user configuration information (only registration settings are considered for the time being), and it is set at the background administrator.

using system.componentmodel.dataannotations;
namespace ninesky.models
{
 ///<summary>
 ///user configuration
 ///<remarks>
 ///Created:2014.02.06
 ///</remarks>
 ///</summary>
 public class userconfig
 {
  [key]
  public int configid {get;set;}
  ///<summary>
  ///enable registration
  ///</summary>
  [display (name="Enable registration")]
  [required (errormessage="required")]
  public bool enabled {get;set;}
  ///<summary>
  ///Forbidden user name<br />
  ///user names are separated by "|"
  ///</summary>
  [display (name="Forbidden username")]
  public string prohibitusername {get;set;}
  ///<summary>
  ///enable administrator verification
  ///</summary>
  [display (name="Enable administrator authentication")]
  [required (errormessage="Required")]
  public bool enableadminverify {get;set;}
  ///<summary>
  ///enable email verification
  ///</summary>
  [display (name="Enable mail authentication")]
  [required (errormessage="Required")]
  public bool enableemailverify {get;set;}
  ///<summary>
  ///default user group id
  ///</summary>
  [display (name="default user group id")]
  [required (errormessage="Required")]
  public int defaultgroupid {get;set;}
 }
}

Data storage layerThe data storage layer is responsible for dealing with the database,Due to the use of the interface, two items dal and idal are generated. idal is the interface project,dal is the implementation of the interface.

There are some common operations with the convenience of the database,Like add, modify, delete, query, etc. Don't want to write these things in the user class when actually writing the code,Write the user group class again, and then repeat this code for future articles and comments.What to do, get a base class.Later, other classes inherited these public methods from the base class.

1.idal project

First open the idal project and add the class interfacebaserepository, the code is as follows.

using system;
using system.linq;
using system.linq.expressions;
namespace ninesky.idal
{
 ///<summary>
 ///Interface base class
 ///<remarks>Created:2014.02.03<br />
 ///Modification:2014.02.09</remarks>
 ///</summary>
 ///<typeparam name="t">type</typeparam>
 public interface interfacebaserepository<t>
 {
  ///<summary>
  ///Add to
  ///</summary>
  ///<param name="entity">data entity</param>
  ///<returns>added data entity</returns>
  t add (t entity);
  ///<summary>
  ///query the number of records
  ///</summary>
  ///<param name="predicate">conditional expression</param>
  ///<returns>record count</returns>
  int count (expression&func<t, bool>>predicate);
  ///<summary>
  ///update
  ///</summary>
  ///<param name="entity">data entity</param>
  ///<returns>whether success</returns>
  bool update (t entity);
  ///<summary>
  ///delete
  ///</summary>
  ///<param name="entity">data entity</param>
  ///<returns>whether success</returns>
  bool delete (t entity);
  ///<summary>
  ///does it exist
  ///</summary>
  ///<param name="anylambda">query expression</param>
  ///<returns>boolean</returns>
  bool exist (expression<func<t, bool>>anylambda);
  ///<summary>
  ///Query data
  ///</summary>
  ///<param name="wherelambda">query expression</param>
  ///<returns>entity</returns>
  t find (expression<func<t, bool>>wherelambda);
  ///<summary>
  ///find data list
  ///</summary>
  ///<typeparam name="s">Sort</typeparam>
  ///<param name="wherelamdba">query expression</param>
  ///<param name="isasc">whether ascending</param>
  ///<param name="orderlamdba">sort expression</param>
  ///<returns></returns>
  iqueryable<t>findlist<s>(expression<func<t, bool>>wherelamdba, bool isasc, expression<func<t, s>orderlamdba);
  ///<summary>
  ///find paged data list
  ///</summary>
  ///<typeparam name="s">Sort</typeparam>
  ///<param name="pageindex">current page</param>
  ///<param name="pagesize">records per page</param>
  ///<param name="totalrecord">Total Records</param>
  ///<param name="wherelamdba">query expression</param>
  ///<param name="isasc">whether ascending</param>
  ///<param name="orderlamdba">sort expression</param>
  ///<returns></returns>
  iqueryable<t>findpagelist<s>(int pageindex, int pagesize, out int totalrecord, expression<func<t, bool>>wherelamdba, bool isasc, expression<func<t, s>orderlamdba);
 }
}

Here are defined to add, delete, modify, determine the existence, the query that returns the model, the query that returns the collection,7 public methods for queries that return paginated collections.These methods basically meet the general needs,Special methods are added during inheritance.

Also used generics,When inheriting, you can directly inherit these methods by passing in the entity type.Look at the interfaceuserrepository interface specifically.

using ninesky.models;
namespace ninesky.idal
{
 ///<summary>
 ///user interface
 ///<remarks>Created:2014.02.03</remarks>
 ///</summary>
 public interface interfaceuserrepository:interfacebaserepository<user>
 {
 }
}

Simple, just inherit from interfacebaserepository and pass it to the entity class user. We look at the class view,Is it inheriting the interface of the base class.

2.dal project

The dal project is an implementation of the idal project interface,To create a dbcontext class in the project, many people have discussed the efficiency of database access to the dbcontext class.msdn says it is lightweight, It doesn't take much to create,It is also not a thread-safe object,And because of the nature (tracking) of the data container, many people think that it should not be static and singletonized. But for a user's single request, it is only reasonable to implement dbcontext. Look at the code first,very simple.

using ninesky.models;
using system.data.entity;
namespace ninesky.dal
{
 ///<summary>
 ///data context
 ///<remarks>created:2014.02.03</remarks>
 ///</summary>
 public class nineskydbcontext:dbcontext
 {
  public dbset<user>users {get;set;}
  public dbset<usergroup>usergroups {get;set;}
  public dbset<userconfig>userconfig {get;set;}
  public nineskydbcontext ()
   :base ("defaultconnection")
  {
  }
 }
}

Let's create a baserepository class that inherits from interfacebaserepository and implements methods of its interface.

using ninesky.idal;
using system;
using system.linq;
using system.linq.expressions;
namespace ninesky.dal
{
 ///<summary>
 ///Warehouse base class
 ///<remarks>Created:2014.02.03</remarks>
 ///</summary>
 public class baserepository<t> ;:interfacebaserepository<t>where t:class
 {
  protected nineskydbcontext ncontext=contextfactory.getcurrentcontext ();
  public t add (t entity)
  {
   ncontext.entry<t>(entity) .state=system.data.entity.entitystate.added;
   ncontext.savechanges ();
   return entity;
  }
  public int count (expression<func<t, bool>>predicate)
  {
   return ncontext.set<t>(). count (predicate);
  }
  public bool update (t entity)
  {
   ncontext.set<t>(). attach (entity);
   ncontext.entry<t>(entity) .state=system.data.entity.entitystate.modified;
   return ncontext.savechanges ()>0;
  }
  public bool delete (t entity)
  {
   ncontext.set<t>(). attach (entity);
   ncontext.entry<t>(entity) .state=system.data.entity.entitystate.deleted;
   return ncontext.savechanges ()>0;
  }
  public bool exist (expression<func<t, bool>>anylambda)
  {
   return ncontext.set<t>(). any (anylambda);
  }
  public t find (expression<func<t, bool>>wherelambda)
  {
   t _entity=ncontext.set<t>(). firstordefault<t>(wherelambda);
   return _entity;
  }
  public iqueryable<t>findlist<s>(expression<func<t, bool>>wherelamdba, bool isasc, expression<func<t, s>orderlamdba)
  {
   var _list=ncontext.set<t>(). where<t>(wherelamdba);
   if (isasc) _list=_list.orderby<t, s>(orderlamdba);
   else _list=_list.orderbydescending<t, s>(orderlamdba);
   return _list;
  }
  public iqueryable<t>findpagelist<s>(int pageindex, int pagesize, out int totalrecord, expression<func<t, bool>>wherelamdba, bool isasc, expression<func<t, s>orderlamdba)
  {
   var _list=ncontext.set<t>(). where<t>(wherelamdba);
   totalrecord=_list.count ();
   if (isasc) _list=_list.orderby<t, s>(orderlamdba) .skip<t>((pageindex-1) * pagesize) .take<t>(pagesize);
   else _list=_list.orderbydescending<t, s>(orderlamdba) .skip<t>((pageindex-1) * pagesize) .take<t>(pagesize);
   return _list;
  }
 }
}

The code is all operations on the database.More interesting is this sentence protected nineskydbcontext ncontext=contextfactory.getcurrentcontext ();

contextfactory is a simple factory class,getcurrentcontext () is a static function.Use a simple factory to get the current dbcontext in the request, which is the dbcontext singleton in the request. First add a factory class contextfactory

using system.data.entity;
using system.runtime.remoting.messaging;
namespace ninesky.dal
{
 ///<summary>
 ///context simple factory
 ///<remarks>
 ///Created:2014.02.05
 ///</remarks>
 ///</summary>
 public class contextfactory
 {
  ///<summary>
  ///get the current data context
  ///</summary>
  ///<returns></returns>
  public static nineskydbcontext getcurrentcontext ()
  {
   nineskydbcontext _ncontext=callcontext.getdata ("nineskycontext") as nineskydbcontext;
   if (_ncontext == null)
   {
    _ncontext=new nineskydbcontext ();
    callcontext.setdata ("nineskycontext", _ncontext);
   }
   return _ncontext;
  }
 }
}

Here is to get the nineskycontext in the callcontext, if it is empty, initialize a nineskycontext, and return directly if it exists.Looking at callcontext, msdn says that callcontext provides a data slot that is unique to each logical execution thread.In a web program, each request happens to be a logical thread, so you can use callcontext to implement a dbcontext singleton within a single request.

Add the specific storage code below.

Add another userrepository class in dal, inheriting from baserepository and interfaceuserrepository. The purpose is to inherit from the baserepository class and implement the interfaceuserrepositor interface.

using ninesky.idal;
using ninesky.models;
using system.linq;
namespace ninesky.dal
{
 ///<summary>
 ///user warehouse
 ///<remarks>Created:2014.02.03</remarks>
 ///</summary>
 class userrepository:baserepository<user> ;, interfaceuserrepository
 {
 }
}

userrepository directly inherits the methods in the base class,The methods in the base class can meet most of the needs,userrepository no longer need to add functions,The other repository classes are similar,No longer posting code.

Here we are building a repository factory to return all the repository classes in the project.

using ninesky.idal;
namespace ninesky.dal
{
 ///<summary>
 ///Simple factory?
 ///<remarks>created:2014.02.03</remarks>
 ///</summary>
 public static class repositoryfactory
 {
  ///<summary>
  ///user storage
  ///</summary>
  public static interfaceuserrepository userrepository {get {return new userrepository ();}}
 }
}

When calling in bll in the future, you don't need to write interfaceuserrepository _iuserrsy=new userrepository () every time. You can write interfaceuserrepository _iuserrsy=repositoryfactory.userrepository directly. The advantage is thatIn the future, when the class implementing the interfaceuserrepository interface needs to be modified in the dal project, we can directly create a new class.Then let the userrepository property in the repositoryfactory class return the new class.

3.ibll project

ibll is the interface of the business logic layer,The operation of the business logic layer on the database basically adds, deletes, and changes. Also write a base interface to write these three operations,Here is similar to the idea of ​​idal.

namespace ninesky.ibll
{
 ///<summary>
 ///Interface base class
 ///<remarks>Created:2014.02.03</remarks>
 ///</summary>
 public interface interfacebaseservice<t>where t:class
 {
  ///<summary>
  ///Add to
  ///</summary>
  ///<param name="entity">data entity</param>
  ///<returns>added data entity</returns>
  t add (t entity);
  ///<summary>
  ///update
  ///</summary>
  ///<param name="entity">data entity</param>
  ///<returns>whether success</returns>
  bool update (t entity);
  ///<summary>
  ///delete
  ///</summary>
  ///<param name="entity">data entity</param>
  ///<returns>whether success</returns>
  bool delete (t entity);
 }
}

Add an interface userservice interface, inherited from interfacebaseservice. Several methods were added to the interface as needed.The name of the find method is unified here,Any name that returns an entity class is find () or findbyxxx (), the method name that returns a set of data is findlist () or findxxxlist, and the name of the pagination is findpagelist () or findxxxpagelist ()

using ninesky.models;
using system.linq;
namespace ninesky.ibll
{
 ///<summary>
 ///user related interface
 ///<remarks>
 ///Created:2014.02.09
 ///</remarks>
 ///</summary>
 public interface interfaceuserservice:interfacebaseservice<user>
 {
  ///<summary>
  ///Does the user exist
  ///</summary>
  ///<param name="username">username</param>
  ///<returns>boolean</returns>
  bool exist (string username);
  ///<summary>
  ///find user
  ///</summary>
  ///<param name="userid">userid</param>
  ///<returns></returns>
  user find (int userid);
  ///<summary>
  ///find user
  ///</summary>
  ///<param name="username">username</param>
  ///<returns></returns>
  user find (string username);
  ///<summary>
  ///user list
  ///</summary>
  ///<param name="pageindex">page number</param>
  ///<param name="pagesize">records per page</param>
  ///<param name="totalrecord">Total Records</param>
  ///<param name="order">Sort:0-id ascending (default), 1id descending, 2 registration time ascending,3 descending registration time,4 login time in ascending order,5Login time descending</param>
  ///<returns></returns>
  iqueryable<user>findpagelist (int pageindex, int pagesize, out int totalrecord, int order);
 }
}

4.bll projectbll project to implement the interfaceuserservice interface method,Add baseservice first

using ninesky.ibll;
using ninesky.idal;
namespace ninesky.bll
{
 ///<summary>
 ///service base class
 ///<remarks>created:2014.02.03</remarks>
 ///</summary>
 public abstract class baseservice<t>:interfacebaseservice<t>where t:class
 {
  protected interfacebaserepository<t>currentrepository {get;set;}
  public baseservice (interfacebaserepository<t>currentrepository) {currentrepository=currentrepository;}
  public t add (t entity) {return currentrepository.add (entity);}
  public bool update (t entity) {return currentrepository.update (entity);}
  public bool delete (t entity) {return currentrepository.delete (entity);}
 }
}

One parameter to be passed in the constructor of this class is currentrepository, which is passed in during inheritance.Here again, look at the user class.

using ninesky.dal;
using ninesky.ibll;
using ninesky.models;
using system.linq;
namespace ninesky.bll
{
 ///<summary>
 ///user service class
 ///<remarks>
 ///Created:2014.02.12
 ///</remarks>
 ///</summary>
 public class userservice:baseservice<user>, interfaceuserservice
 {
  public userservice ():base (repositoryfactory.userrepository) {}
  public bool exist (string username) {return currentrepository.exist (u =>u.username == username);}
  public user find (int userid) {return currentrepository.find (u =>u.userid == userid);}
  public user find (string username) {return currentrepository.find (u =>u.username == username);}
  public iqueryable<user>findpagelist (int pageindex, int pagesize, out int totalrecord, int order)
  {
   switch (order)
   {
    case 0:return currentrepository.findpagelist (pageindex, pagesize, out totalrecord, u =>true, true, u =>u.userid);
    case 1:return currentrepository.findpagelist (pageindex, pagesize, out totalrecord, u =>true, false, u =>u.userid);
    case 2:return currentrepository.findpagelist (pageindex, pagesize, out totalrecord, u =>true, true, u =>u.registrationtime);
    case 3:return currentrepository.findpagelist (pageindex, pagesize, out totalrecord, u =>true, false, u =>u.registrationtime);
    case 4:return currentrepository.findpagelist (pageindex, pagesize, out totalrecord, u =>true, true, u =>u.logintime);
    case 5:return currentrepository.findpagelist (pageindex, pagesize, out totalrecord, u =>true, false, u =>u.logintime);
    default:return currentrepository.findpagelist (pageindex, pagesize, out totalrecord, u =>true, true, u =>u.userid);
   }
  }
 }
}

The above findpagelist code is too cumbersome,Not a good idea for a while.

5.Summary

I wrote here today and still think about the call implementation between projects,I wrote two base interfaces and two base classes, and other classes will inherit from them later.The writing is very similar.Next time you can start making interfaces,In the ninesky.web project, I basically deal with data through ibll, bll.

======================================================= =

findpagelist () This sorting method is really not very general,The code is modified as follows:

1.Interface interfacebaserepository

Modify the two interface methods as shown in the red box.

image

2.baserepository class

Add the orderby method, the code is as follows:

///<summary>
  ///sort
  ///</summary>
  ///<typeparam name="t">type</typeparam>
  ///<param name="source">the original iqueryable</param>
  ///<param name="propertyname">sort property name</param>
  ///<param name="isasc">whether positive order</param>
  ///<returns>sorted iqueryable<t></returns>
  private iqueryable<t>orderby (iqueryable<t>source, string propertyname, bool isasc)
  {
   if (source == null) throw new argumentnullexception ("source", "cannot be empty");
   if (string.isnullorempty (propertyname)) return source;
   var _parameter=expression.parameter (source.elementtype);
   var _property=expression.property (_parameter, propertyname);
   if (_property == null) throw new argumentnullexception ("propertyname", "propertyname");
   var _lambda=expression.lambda (_property, _parameter);
   var _methodname=isasc?"orderby":"orderbydescending";
   var _resultexpression=expression.call (typeof (queryable), _methodname, new type [] {source.elementtype, _property.type}, source.expression, expression.quote (_lambda));
   return source.provider.createquery<t>(_ resultexpression);
  }
Modify the findlist and findpagelist methods, modify the following figure
image
3.Modify the findpagelist method of userservice
The modified code is as follows:
public iqueryable<user>findpagelist (int pageindex, int pagesize, out int totalrecord, int order)
  {
   bool _isasc=true;
   string _ordername=string.empty;
   switch (order)
   {
    case 0:
     _isasc=true;
     _ordername="userid";
     break;
    case 1:
     _isasc=false;
     _ordername="userid";
     break;
    case 2:
     _isasc=true;
     _ordername="registrationtime";
     break;
    case 3:
     _isasc=false;
     _ordername="registrationtime";
     break;
    case 4:
     _isasc=true;
     _ordername="logintime";
     break;
    case 5:_isasc=false;
     _ordername="logintime";
     break;
    default:
     _isasc=false;
     _ordername="userid";
     break;
   }
   return currentrepository.findpagelist (pageindex, pagesize, out totalrecord, u =>true, _ordername, _isasc);
  }
  • Previous The effect of the pull-down zoom-in and pull-up blur of the view in iOS development
  • Next Purchase date selection effect based on jQuery slider