[外包]!採用asp.net core 快速構建小型創業公司後臺管理系統(一.項目簡介)

 

今天給你們分享一下采用asp.net core 快速構建小型創業公司後臺管理系統,該項目是我給一個朋友作的,將要用到公司項目,今天分享出來權限管理模塊喜歡的朋友能夠試用用一下。前端

項目不是一個什麼新項目,也沒有用到什麼牛逼的東西,但裏面融入了我用asp.net core構建管理系統的思考,能夠說是思想的結合體。mysql

該項目的權限模塊我已經放到了Liinu上,項目預覽地址:http://xingchenbeta.52expo.top/Welcome 帳戶:admin 密碼:123456sql

接下來再說一下該項目基本點:數據庫

  • 項目採用Asp.Net Core2.1+EF Core構建後臺服務
  • 項目前端使用BootStrap+Angular1.x構建管理系統前端
  • 數據庫採用MySql 5.7.24
  • 部署環境在CentOS 7.4上
  • 項目中用到Redis
  • 用到Quartz

接下來在上一張項目圖片:api

項目基本完成功能點有:mvc

  • 角色管理(curd)
  • 角色受權
  • 後臺用戶管理(crud)
  • 代碼動態生成權限

廢話就說這麼多吧,接下來我就從頭開始介紹這個項目。asp.net

 一.項目基本組成介紹ide

項目成員就這麼多,你們看着名字可能很眼熟,其實我只接用了DDD的名字而已。工具

除去單元測試,項目分爲五部分:API,WebSite,Domain,Infrastuctrue,Applicaitonoop

Api 主要用來給咱們的移動端兄弟們提供api支持

Website裏面呢就是咱們的後臺管理系統

 

Domain裏面我放的是數據庫實體模型,識圖模型,枚舉類,服務接口約束,以及必要的核心東西

 

Infrastructure裏面我放着部分Domain接口的實現,數據庫上下文,三方,工具類,一些拓展方法,等基礎構造

 

Application裏面放着Domain接口的實現,這裏我主要放本身須要寫的服務,也能夠稱之爲業務邏輯

 

二.抽絲剝繭,看看具體怎麼實現

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

  如圖

  

  把context從實體中拉出來,放到infrastructure裏,在Infrastructure裏建立context文件夾

  

2. 在Domain 建立Repositorys文件夾

  

  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約束

  

  

/// <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裏的東西

  

  這個拓展我寫到了Infrastructure裏,主要用來作服務註冊用的,他很是重要很是重要很是重要,代碼我這裏分享一下。

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.寫本身具體的服務

  

  AccountService實現了接口IAccountService

  5.在StartUp裏注入服務

  

  6.在控制器裏使用

  

  這樣就大致上從上到下,梳理了一下這個項目

 

三.下期分享

  這個項目下期準備繼續講一下里面的

  • BaseController裏分裝的奧妙
  • 配置文件讀取強類型Model
  • Redis如何更好靈活的配置

相關文章
相關標籤/搜索