好久没写随笔了,主要是因为近一个月赶项目。近期翻了翻前程无忧,发现招聘要求使用.net core的公司越来越多,不得不感叹一下.net 发展,有今天的局面离不开微软粑粑的大力开源。正好自己最近也在接触学习.net core,这里打算开两三期记录一下这个小demo的开发过程,后续会附录上源代码。
首先介绍下项目背景,本人使用的开发工具是vs2017 社区免费版,服务器是腾讯云低配版服务器(Ubuntu16.04 64位,个人随便搭建个环境玩玩,低配版跑的windows很卡,只能用linux),.net core sdk是2.1.400,web服务器采用jexus5.8.2(一开始是采用nginx,由于.netcore 运行需要前台运行,并且在关闭ssh连接之后会自动断开,因此采用jexus),数据库采用mysql。项目实现了两个小模块(增删查改),分别是分类管理和文章管理,由于是demo项目,诸位看官请勿太过计较= =||。
首先是项目结构,项目采用EFCore Code first模式,分为实体、仓库、Service接口、Application(service实现)和WebApp(UI)。实体层包括了实体和仓库接口,仓库层包括了Dbcontext和仓库实现(简单写了个BaseRepository实现),Service接口层包括了业务接口以及dto,Application层是service业务接口的实现(相关的业务逻辑写在这层)。如下图:
先看看startup
using System; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Ye.BoardMessage.Repository; using Ye.BoardMessage.Entity.IRepositories; using Ye.BoardMessage.Service; using Ye.BoardMessage.Application; using Microsoft.AspNetCore.Authentication.Cookies; namespace Ye.BoardMessage.WebApp { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { //DbContext services.AddDbContext<BoardMessageDbContext>((provider, builder) => { builder.UseMySql(Configuration.GetConnectionString("DefaultConnection"), b => { b.CommandTimeout(10); }); }); //服务注入 services.AddLogging(); services.Add(new ServiceDescriptor(typeof(DbContext), typeof(BoardMessageDbContext), ServiceLifetime.Scoped)); services.Add(new ServiceDescriptor(typeof(ICategoryRepository), typeof(CategoryRepository), ServiceLifetime.Scoped)); services.Add(new ServiceDescriptor(typeof(IMsgArticleRepository), typeof(MsgArticleRepository), ServiceLifetime.Scoped)); services.Add(new ServiceDescriptor(typeof(IAppUserRepository), typeof(AppUserRepository),ServiceLifetime.Scoped)); services.Add(new ServiceDescriptor(typeof(ICategoryService), typeof(CategoryService), ServiceLifetime.Scoped)); services.Add(new ServiceDescriptor(typeof(IMsgArticleService), typeof(MsgArticleService), ServiceLifetime.Scoped)); services.Add(new ServiceDescriptor(typeof(IAppUserService), typeof(AppUserService), ServiceLifetime.Scoped)); //https://www.cnblogs.com/oorz/p/8617530.html cookie授权登录参考 services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme,options => { options.Cookie.HttpOnly = true; options.Cookie.Expiration = TimeSpan.FromMinutes(30); // If the LoginPath isn't set, ASP.NET Core defaults // the path to /Account/Login. options.LoginPath = "/Account/Login"; options.Cookie.Name = CookieAuthenticationDefaults.AuthenticationScheme; }); services.AddMvc(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseBrowserLink(); app.UseDatabaseErrorPage(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseStaticFiles(); app.UseCookiePolicy(); app.UseAuthentication(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); } } }
然后是DbContext以及相关的Entity和EntityMap
1、DbContext实现
using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using System; using System.Collections.Generic; using System.Text; using Ye.BoardMessage.Repository.EntityMaps; namespace Ye.BoardMessage.Repository { public class BoardMessageDbContext : DbContext { public BoardMessageDbContext() : base() { } public BoardMessageDbContext(DbContextOptions<BoardMessageDbContext> options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfiguration(new CategoryEntityMap()); modelBuilder.ApplyConfiguration(new MsgArticleEntityMap()); modelBuilder.ApplyConfiguration(new AppUserEntityMap()); base.OnModelCreating(modelBuilder); } } }
2、Entity,这里因为Entity比较多,因此只选取一个来做介绍,这个是留言的实体
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Text; namespace Ye.BoardMessage.Entity { public class MsgArticleEntity { public int Id { get; set; } public string Title { get; set; } public string Author { get; set; } public DateTime CreatedTime { get; set; } public string MsgContent { get; set; } public int CategoryId { get; set; } public CategoryEntity Category { get; set; } } }
3、EntityMap,同样因为Map比较多,只选取一个来做介绍,留言实体映射
using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Text; using Ye.BoardMessage.Entity; using Microsoft.EntityFrameworkCore.Metadata.Builders; namespace Ye.BoardMessage.Repository.EntityMaps { public class MsgArticleEntityMap : IEntityTypeConfiguration<MsgArticleEntity> { public void Configure(EntityTypeBuilder<MsgArticleEntity> builder) { builder.ToTable("messagearticle"); builder.HasKey(t => t.Id); builder.Property(t => t.Id).HasColumnName("Id"); builder.Property(t => t.Author).HasColumnName("Author").IsRequired().HasMaxLength(30); builder.Property(t => t.CategoryId).HasColumnName("CategoryId").IsRequired(); builder.Property(t => t.CreatedTime).HasColumnName("CreatedTime").IsRequired(); builder.Property(t => t.MsgContent).HasColumnName("MsgContent").IsRequired().HasMaxLength(500); builder.HasOne<CategoryEntity>(t => t.Category) .WithMany(p => p.Articles).HasForeignKey(p => p.CategoryId).IsRequired(); } } }
总的来说,EFCore相比EntityFramework,API方面略有改变,大体用法还是一致的,另外asp.net core自带了服务注入,这个非常方便。EFCore一对多的配置有所改变,这里踩了个小坑的,参阅了网上不少资料才填完(感谢发达的网络- -b)。这个系列的第一篇介绍完毕,下一篇将介绍使用cookie登录验证以及BaseRepository。代码后续会贴出来,各位看官莫急。