[Asp.net core] 7 actual data access layer definition

0. Preface

In the previous article, we built a project framework, which is basically a complete project. At present, most of the applications are basically this structure. Okay, no nonsense, enter today's topic: complete and implement the basic implementation of the data layer.

image

1. Data entities

Normally, the fields in the data entity of a project are not completely irregular. Normally, there must be a primary key. Sometimes, it will be required to add the last modification time and creation time, as well as the primary key of the creator and modifier in the data table.

So, we can create a generic parent class to help us define these public fields:

using System;

namespace Data.Infrastructure
{
   public class BaseEntity<T>
   {
       public T Id { get; set; }

       public string ModifyUserId { get; set; }

       public DateTime? ModifyTime { get; set; }


       public string CreatorId { get; set; }
       public DateTime? CreateTime { get; set; }
   }
}

Looking at the above code, the namespace is not in Data, but in Data.Infrastructure. This namespace Infrastructure is used to store the architecture classes or interfaces of some projects, and there will be other classes in it.

So, add some useful methods to this class:

public void Create(object userId)
{
   CreatorId = userId.ToString();
   CreateTime = DateTime.Now;
}

public void Create(object userId, DateTime createTime)
{
   CreatorId = userId.ToString();
   CreateTime = createTime;
}

public void Modify(object userId)
{
   ModifyUserId = userId.ToString();
   ModifyTime = DateTime.Now;
}

public void Modify(object userId, DateTime modifyTime)
{
   ModifyUserId = userId.ToString();
   ModifyTime = modifyTime;
}

For the fields used to save the user ID, I have used strings for saving, which can accommodate more data types when the string type is used to save the data.

2. Common data operation interface

In normal development, a complete data operation interface will have many categories, but in many cases we need to separate the two operations of addition, deletion, modification and query. For the database, the view and some data tables are not allowed to be changed. At this time, we need to only open the query interface to the caller, and not open the modification interface.

Therefore, there should be the following two interfaces under Domain:

using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace Domain.Infrastructure
{
   /// <summary>
   /// 修改接口
   /// </summary>
   /// <typeparam name="T"></typeparam>
   public interface IModifyRepository<T>
   {
       /// <summary>
       /// 插入数据
       /// </summary>
       /// <param name="entity"></param>
       /// <returns></returns>
       T Insert(T entity);
       /// <summary>
       /// 插入数据
       /// </summary>
       /// <param name="entities"></param>
       void Insert(params T[] entities);
       /// <summary>
       /// 插入数据
       /// </summary>
       /// <param name="entities"></param>
       void Insert(IEnumerable<T> entities);
       /// <summary>
       /// 保存已提交的修改
       /// </summary>
       /// <param name="entity"></param>
       void Update(T entity);
       /// <summary>
       /// 保存已提交的修改
       /// </summary>
       /// <param name="entities"></param>
       void Update(params T[] entities);
       /// <summary>
       /// 更新数据
       /// </summary>
       /// <param name="predicate"></param>
       /// <param name="updator"></param>
       void Update(Expression<Func<T, bool>> predicate, Expression<Func<T, T>> updator);
       /// <summary>
       /// 删除
       /// </summary>
       /// <param name="entity"></param>
       void Delete(T entity);
       /// <summary>
       /// 删除数据
       /// </summary>
       /// <param name="entities"></param>
       void Delete(params T[] entities);
       /// <summary>
       /// 根据条件删除数据
       /// </summary>
       /// <param name="predicate"></param>
       void Delete(Expression<Func<T,bool>> predicate);
       /// <summary>
       /// 删除主键对应的数据
       /// </summary>
       /// <param name="key"></param>
       void DeleteByKey(object key);
       /// <summary>
       /// 删除主键对应的数据
       /// </summary>
       /// <param name="keys"></param>
       void DeleteByKeys(params object[] keys);
   }
}

The above is to update the interface, so let's go back and write the query interface. There are many ways to query the interface. We first create an interface file:

using System;
using System.Linq.Expressions;

namespace Domain.Infrastructure
{
   /// <summary>
   /// 查询接口
   /// </summary>
   /// <typeparam name="T"></typeparam>
   public interface ISearchRepository<T>
   {

   }
}

A query interface should include the following methods:

  • Get a single data

/// <summary>
/// 根据主键获取数据
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
T Get(object key);
/// <summary>
/// 查询
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
T Get(Expression<Func<T,bool>> predicate);
  • total quantity:

/// <summary>
/// 返回数据库中的数据条目
/// </summary>
/// <returns></returns>
int Count();
/// <summary>
/// 返回数据库中的数据条目,类型为Long
/// </summary>
/// <returns></returns>
long LongCount();
/// <summary>
/// 返回符合条件的数据数目
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
int Count(Expression<Func<T,bool>> predicate);
/// <summary>
/// 返回长整形的符合条件的数目
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
long LongCount(Expression<Func<T,bool>> predicate);
  • Existence judgment

/// <summary>
/// 是否存在满足条件的数据
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
bool IsExists(Expression<Func<T, bool>> predicate);
  • Inquire

// <summary>
/// 返回数据库中所有记录
/// </summary>
/// <returns></returns>
List<T> Search();
/// <summary>
/// 返回所有符合条件的数据
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
List<T> Search(Expression<Func<T,bool>> predicate);
/// <summary>
/// 返回一个延迟查询的对象
/// </summary>
/// <returns></returns>
IEnumerable<T> Query();
/// <summary>
/// 返回一个延迟查询的对象,并预设了一个查询条件
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
IEnumerable<T> Query(Expression<Func<T,bool>> predicate);
  • Sort

/// <summary>
/// 排序查询,默认升序
/// </summary>
/// <param name="predicate"></param>
/// <param name="order"></param>
/// <typeparam name="P"></typeparam>
/// <returns></returns>
List<T> Search<P>(Expression<Func<T, bool>> predicate, Expression<Func<T, P>> order);
/// <summary>
/// 排序查找,指定是否降序排列
/// </summary>
/// <param name="predicate"></param>
/// <param name="order"></param>
/// <param name="isDesc"></param>
/// <typeparam name="P"></typeparam>
/// <returns></returns>
List<T> Search<P>(Expression<Func<T, bool>> predicate, Expression<Func<T, P>> order, bool isDesc);
  • Pagination

In fact, the paging interface definition model needs the assistance of two classes. Without these two classes, the definition of the interface will become very complicated, which is not conducive to the readability of the code:

using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace Data.Infrastructure
{
   /// <summary>
   /// 分页条件模型
   /// </summary>
   /// <typeparam name="T"></typeparam>
   public class PageCondition<T>
   {
       /// <summary>
       /// 查询条件
       /// </summary>
       /// <value></value>
       public Expression<Func<T, bool>> Predicate { get; set; }
       /// <summary>
       /// 排序字段
       /// </summary>
       /// <value></value>
       public string OrderProperty { get; set; }

       /// <summary>
       /// 升序排序或者降序排序,升序为 asc或者空,降序为desc
       /// </summary>
       /// <value></value>
       public string Sort{get;set;}
       /// <summary>
       /// 每页最大数据容量
       /// </summary>
       /// <value></value>
       public int PerpageSize { get; set; }
       /// <summary>
       /// 当前页
       /// </summary>
       /// <value></value>
       public int CurrentPage { get; set; }
   }
   /// <summary>
   /// 分页结果
   /// </summary>
   /// <typeparam name="T"></typeparam>
   public class PageModel<T>
   {
       /// <summary>
       /// 数据
       /// </summary>
       /// <value></value>
       public List<T> Items { get; set; }
       /// <summary>
       /// 当前页码
       /// </summary>
       /// <value></value>
       public int CurrentPage { get; set; }
       /// <summary>
       /// 每页最大数据容量
       /// </summary>
       /// <value></value>
       public int PerpageSize { get; set; }
       /// <summary>
       /// 查询数据总数
       /// </summary>
       /// <value></value>
       public long TotalCount { get; set; }
       /// <summary>
       /// 总页码
       /// </summary>
       /// <value></value>
       public int TotalPages { get; set; }
   }
}

These are two auxiliary classes. You can simply take a look at if these parameters are passed directly to the method without encapsulation, it can be foreseen that the parameter list of the method will be particularly long, which is a disaster for readability and maintainability. I once took over the maintenance of a project. The last developer wrote nearly 15 parameters in a method, and there were a large number of optional parameters. Well, it was a headache. Therefore, I do not recommend that you write this way. If there are more than 4 parameters in a method, I suggest encapsulating it.

So, take a look at the method declaration:

/// <summary>
/// 根据分页参数设置,进行分页查询
/// </summary>
/// <param name="condition"></param>
/// <returns></returns>
PageModel<T> Search(PageCondition<T> condition);

This is how the request is written with parameters encapsulated, and friends can try how to declare the method without encapsulation.

3. Summary

In this article, I led you to sort out the interface definition of data access. For a system, these methods are all necessary (but not every method is used with the same frequency). It is also simple to share with you a summary of the code I wrote in my actual work.




Guess you like

Origin blog.51cto.com/15060511/2639815