.net Core 踩坑记:全新零框架项目搭建
参考【ASP.NET Core跨平台开发从入门到实战 [张剑桥].pdf】从零搭建.net core项目(名称叫NetNote),又踩了不少坑,趁着有印象记下:
1、选用的是最简单最空的项目,说是WebApplication,实际和控制台一样了,只有最基本的Starup.cs、Program.cs,以及几个json,其它全没有。这种最好,一个个往上搭,看需要什么东西
2、看有些core源码没有再分一层数据层出来,直接在控制器搞定。我还是按习惯把默认的改为NetNote.UI,再建一个类库,叫NetNote.DAL
一下没注意,选类库的时候,还是选了上面的.net Standard,结果代码都写好了,发现是不能CodeFirst还是什么的,总之提示不是core项目,报错。仔细一看,原来新建项目时下面还有专门的.net core 类库。
再建个core类库,把代码剪切过来,把旧的删了,再改文件夹名称、改解决方案里文件夹名称,正常了
3、照着教程先来DAL层,要装Framework.Core.SQLServer、Framework.Core.Tools,很久之前(去年?前年?)还听说ef core支持不好,现在应该很成熟了
4、照着把DAL层的实体类、接口、接口实现仓储建好,要在UI层配置,教程里都建了接口,平时工作都没先定义接口再做增删改查,导致叫法不统一,比如有的叫Update,有的叫Save,但也不是很影响就是了,都有代码提示。
5、坑都在这里踩了,重点说一下
public void ConfigureServices(IServiceCollection services) { string connection = @"Server=.;Database=Note;UID=sa;PWD=XXX;"; services.AddDbContext<NoteContext>(options => options.UseSqlServer(connection)); services.AddScoped<INoteRepository, NoteRepository>(); services.AddMvc(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } //app.Run(async (context) => //{ // await context.Response.WriteAsync("Hello World!"); //}); app.UseMvcWithDefaultRoute(); }
仓储层建好后要在ConfigureServices配置
一开始MVC不识别,即浏览器访问不了(因为是完全空的项目),要配置services.AddMvc(),并在Configure里配置默认路由。这个弄了半天,以为都底层默认支持原始路由。
MVC配置后仍无法用,所有页面均为Hello World,原来是初始代码里有这么一句(上面注释了的),所有输出均为Hello World了
6、好不容易UI层弄好了,要CodeFirst生成数据库,死活不行,一开始说要装Core Tools,还有个什么东西忘了,因为我是空项目一个个往里装所需插件的。装完后还是不行,最后用Package Manager又可以了
会多一个这样的文件夹出来,查网上资料好像也是说如果要直接用dotnet ef迁移,要提前把这个迁移文件夹及内容配好。用PM的会帮我们配
平时都是DBFirst,没用过CodeFirst,刚好尝试下
7、整体项目文件夹如下:
教程是另弄了个ViewModel来和实体对应,JAVA也基本是这样,在数据库实体之外再弄一个VO来对应。我一直觉得很麻烦、没必要。
如果字段有所不同还好,如果是完全一样的,何必这样再来一遍?退一步说真要这样弄个实体,好歹也用反射什么的自动赋值吧
这个之前有和水平较高的大佬讨论过,也网上看过,说是为了不暴露数据库内部结构,那如果不想暴露,好歹ViewModel要取完全不同的才有意义吧,而且数据库字段名给别人知道了也没什么特别要紧。
还有种说法是为了不因数据库结构而改变前端什么的,也不对啊,数据库如果变了,前端模型也得跟着变,代码也得跟着变,都是要一条龙的
这个可能就和先接口再实现一样吧,是一种规范,大项目要这样约束好,平时小项目感觉是不要紧,既不要接口,也不要对应UI实体类,节省时间,也不会带来什么后遗症
8、顺便说下,core都是用这种异步写法了,虽说小项目也是效率看不出来,但养成习惯就好,毕竟是底层更高效的,写了也不吃亏
主要是方法上用asyn Task,内部代码用await,其它没什么区别
public async Task<IActionResult> Index() { List<Note> notes = await noteRepository.ListAsync(); return View(notes); }
9、控制器上都这样用注入了,原先公司用的是在Base里用单例工厂,感觉是一样用,就是不用实例化就能调后台呗,不是一回事?
private INoteRepository noteRepository; public NoteController(INoteRepository diNoteRepository) { noteRepository = diNoteRepository; }
公司目前是这样,单例的工厂提前备好服务,哪里要用直接用就可以,也是无需再实例化,这个单例和注入的区别,还要再研究下基础知识才行
public class BaseController : Controller {protected EntityRepositoryService ucEntity = UserCenterFactory.EntityRepositoryService; protected SqlRepositoryService ucSql = UserCenterFactory.SqlRepositoryService;
=================
10、TagHelpers的坑。。。照着教程用 <a asp-action="Add">添加Note</a> ,但解析出来,居然就是原原本本的 <a asp-action="Add">添加Note</a> 这种html,为什么不翻译成a标签的href之类的呢?
找了半天,原来还是因为新建的是空项目的原因,默认会自带个_ViewImports.cshtml,里面就一句话
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
手动在视图页面上面加了这个就好了
11、还有个坑,教程上漏写了一个普通视图的控制器入口,我还以为框架底层又有什么新写法,直接兼容页面和提交呢(或还是我哪里没配对?)
public IActionResult Add() { return View(); } [HttpPost] public async Task<IActionResult> Add(NoteModel nmModel) { if (!ModelState.IsValid) { return BadRequest(ModelState); } await noteRepository.AddAsync(new Note { Title = nmModel.Title, Content = nmModel.Content, Create = DateTime.Now }); return RedirectToAction("Index"); }
教程里面是没有上面普通的public IActionResult Add(){return View()} 的,只有下面post的保存方法,我就看了半天,那Add页面是怎么展示的呢?原先只有接收post啊。
也不知是真支持这样写,但需要另外配(或框架哪里有处理),还是真漏了,反正我加上普通的方法让它返回到页面就可以了
12、外键
平时EF从没用过外键,不方便,网上也建议不要用外键,数据库只做好存储的事,不要搭上业务。
教程里这样写,试了会报错,建表是会自动建外键,但新增时,只增了一张表,另张表没处理,是外键会自动处理?感觉是需要手动加一句吧,两张表都要保存,不然外键的那张表怎么知道要存什么数据。
去掉了外键,单表保存正常,这本教程感觉源码直接拿来上机会有些问题,刚好在排查问题时能加深理解