好久沒寫隨筆了,主要是因為近一個月趕項目。近期翻了翻前程無憂,發現招聘要求使用.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。代碼后續會貼出來,各位看官莫急。