MVC + EF6 tutorials five: MVC in storage mode applications

Article Outline

  • Overview Highlights
  • Theoretical basis
  • detailed steps
  • to sum up

Overview Highlights

Produced design patterns, that is, the development process for continuous abstraction.

We look at the typical process of accessing data before.

Controller in a Context is defined, for example:

private AccountContext db = new AccountContext();

Access in Action, for example, a list of users:

var users=db.SysUsers;

This is similar to the coupling too high. Business logic directly access the data storage layer can cause some problems, such as

Duplicate code; not easy to concentrate the use of data related policies, such as caching; ongoing maintenance, modification and so inconvenient to add new features.

We use the repository to the business layer and data layer separate entities, business logic should data types data source agnostic layer, such as a database or data source may be a Web service

Between the data source and business layers layer increases a repository coordinate, the following effect:

1. query data from the data source

2. mapping data to business entities

3. Save modify business entity data to the data source (persistent data)

Such repository will interact with the business logic and the underlying data sources were separated.

Separating the service layer data and has the following three advantages:

1. Centralized management of different data sources of the underlying logic.

2. to provide a separation unit testing points.

3. Provide flexible framework, the overall design can adapt to the evolving program.

We will approach the original two rounds of abstraction, to achieve the effect we want.

Theoretical basis

Storage and working cell pattern is used to create a layer of abstraction between the business logic and data access layer.

Application of these models can be used to help isolate your program in a data store changes.

The chart below compares the differences mode when not in use and use of libraries and library mode controller context of interaction.

Description: There are many implementation library approach mode, it is one of the FIG.

detailed steps

The whole process is divided into two abstract.

The first round Abstract: Decoupling Controller and the data layer

Establishing a corresponding storage class for each entity type.

SysUser to create a new class warehousing and storage interfaces.

By using the controller in this manner is similar to the following:

ISysUserRepository sysUserRepository = new SysUserRepository(); 

Creating SysUser storage class Specifically look at the following:

1. Create a new folder Repositories, behind the new storage class are placed in this folder

2. Create an interface ISysUserRepository

Declared in the interface of a typical set of CRUD methods.

Wherein there are two discovery methods: Returns all single and return according to ID.

 public  interface ISysUserRepository 
    { 
        // query for all users 
        IQueryable <SYSUSER> the SelectAll ();
         // querying user according to the user name 
        SYSUSER SelectByName ( String the userName);
         // user ID querying user 
        SYSUSER SelectByID ( int ID);
         // add user 
        void the Add (SYSUSER user);
         // delete user based on the user ID 
        BOOL the delete ( int ID); 
    }

3. Create a corresponding storage class SysUserRepository

Create a class SysUserRepository, implement the interface ISysUserRepository

 public class SysUserRepository : ISysUserRepository
    {
        protected AccountContext db = new AccountContext();
        /// <summary>
        /// 添加用户
        /// </summary>
        /// <param name="user"></param>
        public void Add(SysUser user)
        {
            db.SysUsers.Add(user);
            db.SaveChanges();
        }
        /// <summary>
        /// 删除用户
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public bool Delete(int id)
        {
            SysUser user = db.SysUsers.Find(id);
            if (user != null)
            {
                db.SysUsers.Remove(user);
                db.SaveChanges();
                return true;
            }
            else
            {
                return false;
            }
        }
        /// <summary>
        /// 查询所有用户
        /// </summary>
        /// <returns></returns>
        public IQueryable<SysUser> SelectAll()
        { 
            Return db.SysUsers; 
        } 
        ///  <Summary> 
        /// The user name querying user
         ///  </ Summary> 
        ///  <param name = "the userName"> username </ param> 
        ///  <Returns > </ Returns> 
        public SYSUSER SelectByName ( String the userName) 
        { 
            SYSUSER user = db.SysUsers.FirstOrDefault (U => == u.UserName the userName);
             return user; 
        } 
        ///  <Summary> 
        /// The user ID query user
         ///  </ Summary> 
        ///  <param name = "id "> User ID </ param>
        /// <returns></returns>
        public SysUser SelectByID(int id)
        {
            SysUser user = db.SysUsers.FirstOrDefault(u => u.ID == id);
            return user;
        }
    }

4.Controller used SysUser storage class

 public ActionResult Index()
 {
   var users = repository.SelectAll();
   return View(users);
 }

Can be seen that, we add a layer of abstraction, the data connection portion is moved to the Repository, the decoupling achieved Controller and the data layer.

Look can be found, there are two problems:

1. If the controller used in a plurality of repositories, each will have a separate context

2. Each entity type must achieve a corresponding repository class, it will generate code redundancy.

Here we are once again the abstract, to solve these two problems.

The second round Abstract: By eliminating the redundant generic repository class

Will create a repository class for each entity type

a. generating a lot of redundant code

b. will lead to inconsistent updates

For example:

You have to update two different entity type in a transaction in

If a different context instance, a possible success, another may fail.

We use the generic repository to remove redundant code

Using the unit of work to ensure that all repositories using the same context

Description:

Later we will create a new unit of work class used to coordinate multiple repositories work, by creating a single context so that we share.

First, the first to solve the problem of redundant code.

We ISysUserRepository and SysUserRepository make another abstract, removal of redundant repository class.

Modeled ISysUserRepository and SysUserRepository, New IGenericRepository and GenericRepository

Similar steps and in front of:

1. Create a generic interface IGenericRepository

 interface IGenericRepository <TEntity>   WHERE TEntity: class 
    { 
        ///  <Summary> 
        /// Query all entities
         ///  </ Summary> 
        ///  <Returns> </ Returns> 
        the IEnumerable <TEntity> the Get ( );
         ///  <Summary> 
        /// query entity of the entity ID
         ///  </ Summary> 
        ///  <param name = "ID"> </ param> 
        ///  <Returns> </ Returns> 
        TEntity getByID ( Object ID);
         ///  <Summary> 
        /// Add a data
         ///  </summary>
        /// <param name = "Entity"> </ param> 
        void the Insert (TEntity Entity);
         ///  <Summary> 
        /// update a data
         ///  </ Summary> 
        ///  <param name = "Entity"> < / param> 
        void the Update (TEntity entity);
         ///  <Summary> 
        /// remove a further data entity ID
         ///  </ Summary> 
        ///  <param name = "ID"> </ param> 
        ///  <Returns> </ Returns> 
        BOOL the Delete ( Object ID); 
    }

2. Create a generic repository class corresponding GenericRepository

 public  class GenericRepository <TEntity>: IGenericRepository <TEntity> WHERE TEntity: class 
    { 
        / * * 
         * of type Detached: object exists, but the object by the tracking service. After creating the entity, but before adding it to the object context, the entity in this state; 
         * Unchanged: Since objects are loaded into the context, or since the last call System.Data.Objects.ObjectContext.SaveChanges () method after, this object has not been modified; 
         * added: objects that have been added to the object context, but has not been called System.Data.Objects.ObjectContext.SaveChanges () method; 
         * Deleted: use System.Data.Objects.ObjectContext.DeleteObject (System.Object) method to remove objects from the context objects; 
         * Modified: object has changed, but has not been called System.Data.Objects.ObjectContext.SaveChanges () method. 
         * / 
        Internal AccountContext context;
         Internal DbSet <TEntity> dbSet;
        public GenericRepository(AccountContext context)
        {
            this.context = context;
            this.dbSet = context.Set<TEntity>();
        }
        /// <summary>
        /// 根据ID删除实体
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public bool Delete(object id)
        {
            TEntity entity = dbSet.Find(id);
            if (entity != null)
            {
                Delete(entity);
                return true;
            }
            else
            {
                return false;
            }
        }
        public virtual void Delete(TEntity entity)
        {
            if (context.Entry(entity).State==EntityState.Detached)
            {
                dbSet.Attach(entity);
            }
            dbSet.Remove(entity);
        }
        /// <summary>
        /// 查询
        /// </summary>
        /// <returns></returns>
        public IEnumerable<TEntity> Get()
        {
           return dbSet.ToList();
        }
        /// <summary>
        /// 根据ID查询实体
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public TEntity GetByID(object id)
        {
            return dbSet.Find(id);
        }
        /// <summary>
        /// 添加一条数据
        /// </summary>
        /// <param name="entity"></param>
        public void Insert(TEntity entity)
        {
            dbSet.Add(entity);
        }
        /// <summary>
        /// 更新一条数据
        /// </summary>
        /// <param name="entity"></param>
        public void Update(TEntity entity)
        {
            dbSet.Attach(entity);
            context.Entry(entity).State = EntityState.Modified;
        }
    }

3. Modify AccountController

private AccountContext db = new AccountContext();
public ActionResult Index()
{
  IGenericRepository<SysUser> genericRepository = new GenericRepository<SysUser>(db);
  var users = genericRepository.Get();
  return View(users);
}

We can see that by the generic class has been eliminated redundant.

If there are other entities only need to change the incoming TEntity on it, you do not need to re-create the repository class.

Second, the next solve the second problem: context consistency.

We create a new folder in a file DAL class UnitOfWork responsible for the consistency of context:

When a plurality of repositories, sharing the same context

We use multiple repositories of a series of operations is called a unit of work

When a unit of work done, we call the SaveChanges method to do the actual context of the changes. Because it is the same context, all operations will be coordinated better.

Save This class method requires only a repository and a set of attributes.

Each repository property returns a repository instance, all of these instances will share the same context.

    public class UnitOfWork : IDisposable
    {
        private AccountContext context = new AccountContext();
        private GenericRepository<SysUser> sysUserRepository;
        private GenericRepository<SysRole> sysRoleRepository;
        private GenericRepository<SysUserRole> sysUserRoleRepository;
        private GenericRepository<Organize> organizeRepository;
        public GenericRepository<SysUser> SysUserRepository
        {
            get
            {
                if (this.sysUserRepository == null)
                {
                    this.sysUserRepository = new GenericRepository<SysUser>(context);
                }
                return sysUserRepository;
            }
        }
        public GenericRepository<SysRole> SysRoleRepository
        {
            get
            {
                if (this.sysRoleRepository == null)
                {
                    this.sysRoleRepository = new GenericRepository<SysRole>(context);
                }
                return sysRoleRepository;
            }
        }
        public GenericRepository<SysUserRole> SysUserRoleRepository
        {
            get
            {
                if (this.sysUserRoleRepository == null)
                {
                    this.sysUserRoleRepository = new GenericRepository<SysUserRole>(context);
                }
                return sysUserRoleRepository;
            }
        }
        public GenericRepository<Organize> OrganizeRepository
        {
            get
            {
                if (this.organizeRepository == null)
                {
                    this.organizeRepository = new GenericRepository<Organize>(context);
                }
                return organizeRepository;
            }
        }

        public void Save()
        {
            context.SaveChanges();
        }
        private bool disposed = false;
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    context.Dispose();
                }
            }
            this.disposed = true;
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }

Description:

GC.SuppressFinalize(this);

Because the object is released Dispose, so you need to call GC.SuppressFinalize to make objects out of the queue terminate, terminate prevent objects being executed twice.

Modify AccountController

 private UnitOfWork work = new UnitOfWork();
 public ActionResult Index()
 {
   var users = work.SysUserRepository.Get();
   return View(users);
 }

Third, query optimization

Now that you have the code redundancy and inconsistency problems were solved in context.

But there is a problem.

We see our query method:

IEnumerable<TEntity> Get();

TEntity GetByID(object id);

The above method is to return all of the results, a single record is returned in accordance with id.

In practice, there are sure to encounter a problem:

We need to query based on various conditions.

Such as querying the user to achieve a similar GetUsersByName or GetUsersByDescription and so on.

A common practice is to solve this problem:

Create a derived class, add specific Get method for a particular entity type, for example, said earlier GetUsersByName.

This has a drawback, it will have redundant code. Especially in a complex program, it may have a lot of class and inherit a particular method, but it takes a lot of maintenance work. We do not use this approach.

We use the expression argument of the method.

Get transform what method.

First analysis of demand, there are three commonly used:

1. Filter Condition

2. Sort

3. Eager loading data relevant to the needs of

For these three points, we added three optional parameters to Get

 IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null,
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            string includeProperties = "");

Let's look at realization of GenericRepository

public IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null,
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            string includeProperties = "")
        {
            IQueryable < TEntity > query= dbSet;
            if (filter!=null)
            {
                query = query.Where(filter);
            }
            foreach (var includeProperty in includeProperties.Split(new char[] { ','},StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }
            if (orderBy!=null)
            {
                return orderBy(query).ToList();
            }
            else
            {
                return query.ToList();
            }
        }

Modify AccountController

        private UnitOfWork work = new UnitOfWork();
        public ActionResult Index()
        {
            // 不加条件
            var users = work.SysUserRepository.Get();
            // 加入过滤条件
            //var users = work.SysUserRepository.Get(filter:u=>u.UserName=="钱二");
            // 加排序
            //var users = work.SysUserRepository.Get(orderBy: q => q.OrderBy(u => u.UserName));
            return View(users);
        }

to sum up

First, a layer of abstraction between the Controller and Context be responsible for data access.

Then a second abstract generalization of the common methods of extracting a GenericRepository out into each specific type of UnitOfWork uniform treatment.

Finally, the transformation of query method, the flexibility to return query results by passing an expression depending on the conditions.

Guess you like

Origin www.cnblogs.com/qianj/p/12511695.html