欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

[外包]!采用asp.net core 快速构建小型创业公司后台管理系统(一)

程序员文章站 2023-03-28 07:56:21
[外包]!采用asp.net core 快速构建小型创业公司后台管理系统(一) ......

 

今天给大家分享一下采用asp.net core 快速构建小型创业公司后台管理系统,该项目是我给一个朋友做的,将要用到公司项目,今天分享出来权限管理模块喜欢的朋友可以试用用一下。

项目不是一个什么新项目,也没有用到什么牛逼的东西,但里面融入了我用asp.net core构建管理系统的思考,可以说是思想的结合体。

该项目的权限模块我已经放到了liinu上,项目预览地址:http://xingchenbeta.52expo.top/welcome 账户:admin 密码:123456

接下来再说一下该项目基本点:

  • 项目采用asp.net core2.1+ef core构建后台服务
  • 项目前端使用bootstrap+angular1.x构建管理系统前端
  • 数据库采用mysql 5.7.24
  • 部署环境在centos 7.4上
  • 项目中用到redis
  • 用到quartz

接下来在上一张项目图片:

[外包]!采用asp.net core 快速构建小型创业公司后台管理系统(一)

项目基本完成功能点有:

  • 角色管理(curd)
  • 角色授权
  • 后台用户管理(crud)
  • 代码动态生成权限

废话就说这么多吧,接下来我就从头开始介绍这个项目。

 一.项目基本组成介绍

[外包]!采用asp.net core 快速构建小型创业公司后台管理系统(一)

项目成员就这么多,大家看着名字可能很眼熟,其实我只接用了ddd的名字而已。

除去单元测试,项目分为五部分:api,website,domain,infrastuctrue,applicaiton

api 主要用来给我们的移动端兄弟们提供api支持

website里面呢就是我们的后台管理系统

[外包]!采用asp.net core 快速构建小型创业公司后台管理系统(一)

 

domain里面我放的是数据库实体模型,识图模型,枚举类,服务接口约束,以及必要的核心东西

[外包]!采用asp.net core 快速构建小型创业公司后台管理系统(一)

 

infrastructure里面我放着部分domain接口的实现,数据库上下文,三方,工具类,一些拓展方法,等基础构造

[外包]!采用asp.net core 快速构建小型创业公司后台管理系统(一)

 

application里面放着domain接口的实现,这里我主要放自己需要写的服务,也可以称之为业务逻辑

[外包]!采用asp.net core 快速构建小型创业公司后台管理系统(一)

 

二.抽丝剥茧,看看具体怎么实现

1,设计好数据库(我们还是采用db frist的思想,因为我觉得code frist开发的有点慢,并不是说code frist不好!),在domain项目引入支持mysql的nuget包

<packagereference include="pomelo.entityframeworkcore.mysql" version="2.1.2" />

  生成实体类

## dbfrist 数据库迁移命令

scaffold-dbcontext "server=host;database=restapi;user=root;password=xx.;" "pomelo.entityframeworkcore.mysql" -force -outputdir entitys

  如图

  [外包]!采用asp.net core 快速构建小型创业公司后台管理系统(一)

  把context从实体中拉出来,放到infrastructure里,在infrastructure里创建context文件夹

  [外包]!采用asp.net core 快速构建小型创业公司后台管理系统(一)

2. 在domain 创建repositorys文件夹

  [外包]!采用asp.net core 快速构建小型创业公司后台管理系统(一)

  idbrepository封装装ef数据仓储接口

  

/// <summary>
    /// 封装装ef数据仓储接口
    /// </summary>
    public interface idbrepository<tcontext> where tcontext:dbcontext
    {
        /// <summary>
        /// get <see cref="!:tsource" /> from raw sql query
        /// the tsource must in database or <see cref="t:microsoft.entityframeworkcore.dbcontext" />
        /// </summary>
        /// <typeparam name="tsource"></typeparam>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        iqueryable<tsource> fromsql<tsource>(string sql, params object[] parameters) where tsource : class;

        /// <summary>get single or default tsource</summary>
        /// <typeparam name="tsource">entity</typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        tsource single<tsource>(expression<func<tsource, bool>> predicate = null) where tsource : class;

        /// <summary>get first or default tsource</summary>
        /// <typeparam name="tsource"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        tsource first<tsource>(expression<func<tsource, bool>> predicate = null) where tsource : class;

        /// <summary>select entity by conditions</summary>
        /// <typeparam name="tsource"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        iqueryable<tsource> where<tsource>(expression<func<tsource, bool>> predicate = null) where tsource : class;

        /// <summary>counting the entity's count under this condition</summary>
        /// <typeparam name="tsource"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        int count<tsource>(expression<func<tsource, bool>> predicate = null) where tsource : class;

        /// <summary>return the query</summary>
        /// <typeparam name="tsource"></typeparam>
        /// <returns></returns>
        iqueryable<tsource> query<tsource>() where tsource : class;

        /// <summary>check the condition</summary>
        /// <typeparam name="tsource"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        bool exists<tsource>(expression<func<tsource, bool>> predicate = null) where tsource : class;

        /// <summary>paging the query</summary>
        /// <typeparam name="t"></typeparam>
        /// <param name="query"></param>
        /// <param name="pageindex">page index</param>
        /// <param name="pagesize">page size </param>
        /// <param name="count">total row record count</param>
        /// <returns></returns>
        iqueryable<t> pages<t>(iqueryable<t> query, int pageindex, int pagesize, out int count) where t : class;

        /// <summary>paging the query</summary>
        /// <typeparam name="t"></typeparam>
        /// <param name="pageindex">page index</param>
        /// <param name="pagesize">page size </param>
        /// <param name="count">total row record count</param>
        /// <returns></returns>
        iqueryable<t> pages<t>(int pageindex, int pagesize, out int count) where t : class;

        /// <summary>
        /// 分页
        /// </summary>
        /// <typeparam name="t"></typeparam>
        /// <param name="query"></param>
        /// <param name="pageindex"></param>
        /// <param name="pagesize"></param>
        /// <param name="count">总条数</param>
        /// <param name="pagecount">页数</param>
        /// <returns></returns>
        iqueryable<t> pages<t>(iqueryable<t> query, int pageindex, int pagesize, out int count, out int pagecount) where t : class;
        /// <summary>save all the changes add, update, delete</summary>
        void save();

        /// <summary>add entity to context or database</summary>
        /// <param name="entity"></param>
        /// <param name="save">save the add and all changes before this to database</param>
        void add(object entity, bool save = false);

        /// <summary>update entity to context or database</summary>
        /// <param name="entity"></param>
        /// <param name="save">save the update and all changes before this to database</param>
        void update(object entity, bool save = false);

        /// <summary>update entitys to context or database</summary>
        /// <param name="list"></param>
        /// <param name="save">save the updates and all changes before this to database</param>
        void update(ienumerable<object> list, bool save = false);

        /// <summary>delete entity from context or database</summary>
        /// <param name="entity"></param>
        /// <param name="save">save the delete and all changes before this to database</param>
        void delete(object entity, bool save = false);

        /// <summary>delete entitys from context or database</summary>
        /// <param name="list"></param>
        /// <param name="save">save the deletes and all changes before this to database</param>
        void delete(ienumerable<object> list, bool save = false);
    }

  在infrastructure里创建repositorys,dbrepository类实现了idbrepository,封装泛型仓储 依赖接口 dbcontext约束

  [外包]!采用asp.net core 快速构建小型创业公司后台管理系统(一)

  

[外包]!采用asp.net core 快速构建小型创业公司后台管理系统(一)
/// <summary>
    /// 封装泛型仓储 依赖接口 dbcontext约束
    /// </summary>
    /// <typeparam name="tcontext"></typeparam>
    public class dbrepository<tcontext> : idbrepository where tcontext : dbcontext
    {
        private tcontext _context;

        protected virtual tcontext datacontext
        {
            get
            {
                if ((object)this._context == null)
                    this._context = servicecollectionextension.new<tcontext>();
                return this._context;
            }
        }

        /// <summary>
        /// get <see cref="!:tsource" /> from raw sql query
        /// the tsource must in database or <see cref="t:microsoft.entityframeworkcore.dbcontext" />
        /// </summary>
        /// <typeparam name="tsource"></typeparam>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public iqueryable<tsource> fromsql<tsource>(string sql, params object[] parameters) where tsource : class
        {
            return this.datacontext.set<tsource>().fromsql<tsource>((rawsqlstring)sql, parameters);
        }

        /// <summary>get single or default tsource</summary>
        /// <typeparam name="tsource">entity</typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public tsource single<tsource>(expression<func<tsource, bool>> predicate = null) where tsource : class
        {
            if (predicate == null)
                return this.datacontext.set<tsource>().singleordefault<tsource>();
            return this.datacontext.set<tsource>().singleordefault<tsource>(predicate);
        }

        /// <summary>get first or default tsource</summary>
        /// <typeparam name="tsource"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public tsource first<tsource>(expression<func<tsource, bool>> predicate = null) where tsource : class
        {
            if (predicate == null)
                return this.datacontext.set<tsource>().firstordefault<tsource>();
            return this.datacontext.set<tsource>().firstordefault<tsource>(predicate);
        }

        /// <summary>select entity by conditions</summary>
        /// <typeparam name="tsource"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public iqueryable<tsource> where<tsource>(expression<func<tsource, bool>> predicate = null) where tsource : class
        {
            if (predicate == null)
                return this.datacontext.set<tsource>().asqueryable<tsource>();
            return this.datacontext.set<tsource>().where<tsource>(predicate);
        }

        /// <summary>counting the entity's count under this condition</summary>
        /// <typeparam name="tsource"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public int count<tsource>(expression<func<tsource, bool>> predicate = null) where tsource : class
        {
            if (predicate == null)
                return this.datacontext.set<tsource>().count<tsource>();
            return this.datacontext.set<tsource>().count<tsource>(predicate);
        }

        /// <summary>return the query</summary>
        /// <typeparam name="tsource"></typeparam>
        /// <returns></returns>
        public iqueryable<tsource> query<tsource>() where tsource : class
        {
            return (iqueryable<tsource>)this.datacontext.set<tsource>();
        }

        /// <summary>check the condition</summary>
        /// <typeparam name="tsource"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public bool exists<tsource>(expression<func<tsource, bool>> predicate = null) where tsource : class
        {
            if (predicate == null)
                return this.datacontext.set<tsource>().any<tsource>();
            return this.datacontext.set<tsource>().any<tsource>(predicate);
        }

        /// <summary>paging the query</summary>
        /// <typeparam name="t"></typeparam>
        /// <param name="query"></param>
        /// <param name="pageindex">page index</param>
        /// <param name="pagesize">page size </param>
        /// <param name="count">total row record count</param>
        /// <returns></returns>
        public iqueryable<t> pages<t>(iqueryable<t> query, int pageindex, int pagesize, out int count) where t : class
        {
            if (pageindex < 1)
                pageindex = 1;
            if (pagesize < 1)
                pagesize = 10;
            count = query.count<t>();
            query = query.skip<t>((pageindex - 1) * pagesize).take<t>(pagesize);
            return query;
        }

        /// <summary>paging the query</summary>
        /// <typeparam name="t"></typeparam>
        /// <param name="pageindex">page index</param>
        /// <param name="pagesize">page size </param>
        /// <param name="count">total row record count</param>
        /// <returns></returns>
        public iqueryable<t> pages<t>(int pageindex, int pagesize, out int count) where t : class
        {
            if (pageindex < 1)
                pageindex = 1;
            if (pagesize < 1)
                pagesize = 10;
            var source = this.datacontext.set<t>().asqueryable<t>();
            count = source.count<t>();
            return source.skip<t>((pageindex - 1) * pagesize).take<t>(pagesize);
        }
        /// <summary>
        /// 分页
        /// </summary>
        /// <typeparam name="t"></typeparam>
        /// <param name="query"></param>
        /// <param name="pageindex"></param>
        /// <param name="pagesize"></param>
        /// <param name="count"></param>
        /// <param name="pagecount"></param>
        /// <returns></returns>
        public iqueryable<t> pages<t>(iqueryable<t> query, int pageindex, int pagesize, out int count, out int pagecount) where t : class
        {
            if (pageindex < 1)
            {
                pageindex = 1;
            }
            if (pagesize < 1)
            {
                pagesize = 10;
            }
            if (pagesize > 100)
            {
                pagesize = 100;
            }
            count = query.count();
            pagecount = count / pagesize;
            if ((decimal)pagecount < (decimal)count / (decimal)pagesize)
            {
                pagecount++;
            }
            query = query.skip((pageindex - 1) * pagesize).take(pagesize);
            return query;
        }


        /// <summary>save all the changes add, update, delete</summary>
        public void save()
        {
            this.datacontext.savechanges();
        }

        /// <summary>add entity to context or database</summary>
        /// <param name="entity"></param>
        /// <param name="save">save the add and all changes before this to database</param>
        public void add(object entity, bool save = false)
        {
            this.datacontext.add(entity);
            if (!save)
                return;
            this.save();
        }

        /// <summary>update entity to context or database</summary>
        /// <param name="entity"></param>
        /// <param name="save">save the update and all changes before this to database</param>
        public void update(object entity, bool save = false)
        {
            this.datacontext.update(entity);
            if (!save)
                return;
            this.save();
        }

        /// <summary>update entitys to context or database</summary>
        /// <param name="list"></param>
        /// <param name="save">save the updates and all changes before this to database</param>
        public void update(ienumerable<object> list, bool save = false)
        {
            this.datacontext.updaterange(list);
            if (!save)
                return;
            this.save();
        }

        /// <summary>delete entity from context or database</summary>
        /// <param name="entity"></param>
        /// <param name="save">save the delete and all changes before this to database</param>
        public void delete(object entity, bool save = false)
        {
            this.datacontext.remove(entity);
            if (!save)
                return;
            this.save();
        }

        /// <summary>delete entitys from context or database</summary>
        /// <param name="list"></param>
        /// <param name="save">save the deletes and all changes before this to database</param>
        public void delete(ienumerable<object> list, bool save = false)
        {
            this.datacontext.removerange(list);
            if (!save)
                return;
            this.save();
        }
    }
view code

  需要注意的地方我说一下

  

public class dbrepository<tcontext> : idbrepository<tcontext> where tcontext : dbcontext
    {
        private tcontext _context;

        protected virtual tcontext datacontext
        {
            get
            {
                if ((object)this._context == null)
                    this._context = servicecollectionextension.new<tcontext>();
                return this._context;
            }
        }
}

  

  servicecollectionextension.new<tcontext>()是我写的一个服务拓展,可以创建出一个tcontext类型的实体,当然我们可以把这个dbrepository放到application
  这样repository这块东西就只是domain和application通过接口之间的约束了

  3.简要说一下servicecollectionextension里的东西

  [外包]!采用asp.net core 快速构建小型创业公司后台管理系统(一)

  这个拓展我写到了infrastructure里,主要用来做服务注册用的,他非常重要非常重要非常重要,代码我这里分享一下。

[外包]!采用asp.net core 快速构建小型创业公司后台管理系统(一)
public static class servicecollectionextension
    {
        private static ihttpcontextaccessor _httpcontextaccessor;

        private static iserviceprovider _serviceprovider;
        /// <summary>
        /// cerf weige
        /// </summary>
        private static idataprotector protector => serviceprovider.getdataprotector("aspnetcore", array.empty<string>());


        /// <summary>
        /// 注册常用服务
        /// </summary>
        /// <param name="service"></param>
        public static iservicecollection addregistercontainer(this iservicecollection services)
        {
            //注入httpcontextaccessor
            services.addsingleton<ihttpcontextaccessor, httpcontextaccessor>();
            //注入配置文件获取服务
            services.addsingleton<iconfiggeter, configgeter>();
            return services;
        }

        //
        /// <summary>
        /// 创建自定义addmvc
        /// </summary>
        /// <param name="services"></param>
        /// <param name="mvcoptions"></param>
        /// <returns></returns>
        public static imvcbuilder addmvccustomer(this iservicecollection services, action<mvcapplicationoptions> mvcoptions = null)
        {
            servicecollectiondescriptorextensions.replace(services, servicedescriptor.singleton<ifilterprovider, mvcfilterprovider>());

            var result= services.addmvc().setcompatibilityversion(compatibilityversion.version_2_1).addjsonoptions(_mvcjsonoptions =>
            {
                _mvcjsonoptions.serializersettings.nullvaluehandling = nullvaluehandling.ignore;
                _mvcjsonoptions.serializersettings.dateformatstring = "yyyy-mm-d hh:mm";
                _mvcjsonoptions.serializersettings.datetimezonehandling = datetimezonehandling.local;
                _mvcjsonoptions.serializersettings.contractresolver = new camelcasepropertynamescontractresolver();
                _mvcjsonoptions.serializersettings.referenceloophandling = referenceloophandling.ignore;
            });

            services.addauthentication(options => options.addscheme<mvccookieauthenticationhandler>(tbconstant.website_authentication_scheme, tbconstant.website_authentication_scheme));
            
            services.buildserviceprovider().registerserviceprovider();
            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="t"></typeparam>
        /// <param name="services"></param>
        /// <returns></returns>
        public static iservicecollection addauthorizedfilter<t>(this iservicecollection services) where t : class, iauthorizationfilter
        {
            services.addtransient<iauthorizationfilter, t>();
            return services;
        }
        /// <summary>
        /// 创建服务提供者
        /// </summary>
        /// <param name="serviceprovider"></param>
        /// <returns></returns>
        public static iserviceprovider registerserviceprovider(this iserviceprovider serviceprovider)
        {
            _serviceprovider = serviceprovider ?? throw new mvcexception("iserviceprovider serviceprovider canot be null");
            _httpcontextaccessor = serviceprovider.getrequiredservice<ihttpcontextaccessor>();
            return serviceprovider;
        }

        /// <summary>
        /// 
        /// </summary>
        public static iserviceprovider serviceprovider
        {
            get
            {
                if (_serviceprovider == null)
                {
                    return _httpcontextaccessor.httpcontext.requestservices;
                }
                return _serviceprovider;//_httpcontextaccessor.httpcontext.requestservices;
            }
        }

        public static httpcontext httpcontext => _httpcontextaccessor?.httpcontext;


        public static object new(type type)
        {
            return activatorutilities.createinstance(serviceprovider, type, array.empty<object>());
        }

        public static t new<t>()
        {
            return activatorutilities.createinstance<t>(serviceprovider, array.empty<object>());
        }

        public static t get<t>()
        {
            t val;
            try
            {
                val = activatorutilities.getserviceorcreateinstance<t>(serviceprovider);
            }
            catch (exception ex)
            {
                try
                {
                    val = serviceprovider.getservice<t>();
                }
                catch (exception ex2)
                {
                    try
                    {
                        val = default(t);
                    }
                    catch (exception ex3)
                    {
                        throw new mvcexception($"ex0={ex.message}; ex1={ex2.message}; ex2={ex3.message};");
                    }
                }
            }
            if (val != null)
            {
                return val;
            }
            return default(t);
        }

        public static object get(type type)
        {
            try
            {
                return activatorutilities.getserviceorcreateinstance(serviceprovider, type);
            }
            catch
            {
                object service = serviceprovider.getservice(type);
                if (service == null)
                {
                    return null;
                }
                return service;
            }
        }

        /// <summary>
        /// base 64/256解密
        /// </summary>
        /// <param name="plaintext">密文</param>
        /// <returns></returns>
        public static string decrypt(string plaintext)
        {
            idataprotector protector = protector;
            if (protector is base256dataprotector)
            {
                return (protector as base256dataprotector).unprotect(plaintext, true);
            }
            return protector.unprotect(plaintext);
        }

        /// <summary>
        /// base64/256加密
        /// </summary>
        /// <param name="plaintext">明文</param>
        /// <returns></returns>
        public static string encrypt(string plaintext)
        {
            idataprotector protector = protector;
            if (protector is base256dataprotector)
            {
                return (protector as base256dataprotector).protect(plaintext, true);
            }
            return protector.protect(plaintext);
        }

        #endregion

        public static iservicecollection addtestidentityserver(this iservicecollection services)
        {
            services.addidentityserver()
                .adddevelopersigningcredential()
                .addinmemoryapiresources(auth.config.getapiresources())
                .addresourceownervalidator<resourceownerpasswordvalidator>()
                .addinmemoryclients(auth.config.getclients())
                .addtestusers(auth.config.getusers());
            return services;
        }

        public static iservicecollection addtestauthentication(this iservicecollection services)
        {
            services.addauthentication("bearer")
                .addidentityserverauthentication(options =>
                {
                    options.authority = configlocator.instance["applicationurl"];
                    options.requirehttpsmetadata = false;
                    options.apiname = "banbrickcustomer";
                });
            return services;
        }
    }
view code

  4.写自己具体的服务

  [外包]!采用asp.net core 快速构建小型创业公司后台管理系统(一)

  accountservice实现了接口iaccountservice

  5.在startup里注入服务

  [外包]!采用asp.net core 快速构建小型创业公司后台管理系统(一)

  6.在控制器里使用

  [外包]!采用asp.net core 快速构建小型创业公司后台管理系统(一)

  这样就大体上从上到下,梳理了一下这个项目

 

三.下期分享

  这个项目下期准备继续讲一下里面的

  • basecontroller里分装的奥妙
  • 配置文件读取强类型model
  • redis如何更好灵活的配置

[外包]!采用asp.net core 快速构建小型创业公司后台管理系统(一)