zoukankan      html  css  js  c++  java
  • 【asp.net core】7 实战之 数据访问层定义

    0. 前言

    在上一篇,我们搭建了一个项目框架,基本上是一个完整的项目。目前而言,大部分的应用基本都是这个结构。好的,不废话了,进入今天的议题:完成并实现数据层的基础实现。

    1. 数据实体

    通常情况下,一个项目的数据实体中字段并不是完全没有规律可寻。通常情况下,必须有一个主键。有些时候,会要求在数据表中增加上次修改时间和创建时间,以及创建人和修改人的主键。

    所以,我们可以创建一个泛型父类,来帮我们定义这些公共字段:

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

    看上述代码里,命名空间并不在Data里,而是在Data.Infrastructure里。这个命名空间 Infrastructure 用来存放一些项目的架构类或者接口,里面还会其他的类。

    那么,给这个类补充一些可能有用的方法:

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

    这里用来保存用户ID的字段,我都用了字符串做保存,是借用字符串类型保存数据时能容纳更多的数据类型。

    2. 常见数据操作接口

    在正常开发中,一个完整的数据操作接口会有很多分类,但是很多时候我们需要分开增删改和查询这两种操作。对于数据库而言,视图和有些数据表都是不被允许改变的,这时候就需要我们只对调用方开放查询接口,而不开放修改接口。

    所以,在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);       
        }
    }
    

    上述是更新接口,那么我们回过头来写查询接口,查询接口的方法有很多。我们先创建一个接口文件:

    using System;
    using System.Linq.Expressions;
    
    namespace Domain.Infrastructure
    {
        /// <summary>
        /// 查询接口
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public interface ISearchRepository<T>
        {
            
        }
    }
    

    一个查询接口应该包括以下方法:

    • 获取单个数据
    /// <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);
    
    • 统计数量:
    /// <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);
    
    • 存在性判断
    /// <summary>
    /// 是否存在满足条件的数据
    /// </summary>
    /// <param name="predicate"></param>
    /// <returns></returns>
    bool IsExists(Expression<Func<T, bool>> predicate);
    
    • 查询
    // <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);
    
    • 排序
    /// <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);
    
    • 分页

    实际上分页的接口定义模型需要两个类的辅助,如果没有这两个类,接口的定义会变得十分复杂,不利于代码的可读性:

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

    这是两个辅助类,可以简单看一下如果这些参数不进行封装直接传给方法,可以预见方法的参数列表会特别长,这对于可读性和可维护性来说简直就是灾难。我曾经接手过一个项目的维护,上一位开发者在一个方法写了近15个参数,而且还有大量的可选参数,嗯,十分头疼。所以,我不建议大家这样写,一个方法参数超过4个我建议还是封装一下。

    那么,看一看方法的声明:

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

    这是使用参数封装了请求的写法,小伙伴们可以试试不用封装,方法是如何声明的。

    3. 总结

    在这一篇带领大家梳理了一下数据访问的接口定义,对一个系统来说,这些方法都是有必要的(但不是每个方法使用频率都一样高)。也是简单的跟大家分享一下我在实际工作中写代码的总结。

    更多内容烦请关注我的博客《高先生小屋》

    file

  • 相关阅读:
    axis2 WebService的发布与调用
    Lucene、Compass学习以及与SSH的整合
    qsort函数应用大全
    Effective C++ ——模板和泛型编程
    Effective C++ ——继承与面向对象设计
    Effective C++ ——实现
    Effective C++ ——设计与声明
    Effective C++ ——资源管理
    Effective C++ ——构造/析构/赋值运算符
    Effective C++ ——让自己习惯C++
  • 原文地址:https://www.cnblogs.com/c7jie/p/13061127.html
Copyright © 2011-2022 走看看