簡單介紹
Asp.net Core最大的價值在於跨平台、跨平台、跨平台。重要的事情說三遍。但是目前畢竟是在開發初期,雖然推出了1.0.0 正式版,但是其實好多功能還沒有完善。比方說編譯時的一些文件編碼問題,輔助工具Tools的一些Bug,還有一些好用的模板和平台實現尚未完成等一些問題。但這畢竟是一個好的開始,並且在Github上,大家都還在積極的完善,你也可以參與進來。地址:https://github.com/aspnet
Asp.net Core在學習的時候,首先你應該跟着微軟官方的入門教材來學習,在這里: https://docs.asp.net/en/latest/tutorials/first-mvc-app/start-mvc.html。這個入門教材很淺顯易懂的讓你了解了Asp.net Core mvc框架以及entity framework core的用法。不過缺點是,使用的是Sql compact數據庫,也就是SQL Server數據庫。要知道,我們使用Asp.net 的主要目的是實現跨平台部署,所以數據庫的選擇上,首選肯定不是SqlServer,否則意義何在?當然,目前Entity Framework core暫時還不支持所有數據庫。截止2016年7月,支持情況如下:
Database Providers
The following providers are available
所以提供給我們選擇的數據庫還是有限的(主要是不支持MySql,Devart這東西筆者不了解,不評論)。總得來說,對MS SQL Server的支持肯定是最好的,所以場景允許下,首選Sql server。其次,就DB2和PostgreSQL可選了。筆者不喜歡DB2(很多原因,主要是開發操作管理麻煩,並非說DB2本身存在問題),PostgreSQL其實也不太好用,不過誰叫他免費呢,肯定是好多國內公司首選。
PostgreSQL本身歷史悠久,記得好像上世紀80年代就存在了,免費開源,而且是有專門社區維護。設計理念是以健壯性為首選,所以收到光打企業級平台歡迎。關於PostgreSQL和MySQL之間的優缺點,這個其實不太好說,MySQL在損失健壯性的同時,提高了性能,並且支持很多非標准新特性,而PostgreSQL在健壯性上,號稱不弱於Oracle,性能優秀,完全支持SQL標准,所以其實並不差。
准備環境
Ubuntu Server(16.04)虛擬機1台,IP:192.168.1.6 預裝了PostgreSQL數據庫,並配置好防火牆,ssh連接等基礎環境。確保能夠外部訪問。
VS2015 Update3
Putty 和SSH Secure File Transfer Client
服務器環境部署
參考之前的博客:http://blog.csdn.net/lanwilliam/article/details/51880124
開始
-
新建項目,選擇Asp.net Core Web Application項目模板。
-
選擇Web應用程序模板,然后修改身份驗證哪里,選擇不進行身份驗證。
這里要說一下,Asp.net core項目中,包含一個Identity子項目,在GitHub上有介紹如下:
這里大家一看就知道了,這就是原來提供的ASP.net自帶的權限框架。這個框架現在其實非常強大了,還支持Google和TWriter等OAuth用法,不過缺點是只支持SQL Server數據庫。如果選擇了個人用戶賬戶,那么會默認使用這個框架創建項目。我們希望用PostgreSQL,所以不能選他,很遺憾。而且目前創建控制器等一些內置模板用法,都是基於SqlServer,用到這個Identity,大家如果看過微軟的GettingStarted,就會看到介紹,所以建議大家首先學習微軟GettingStarted。
當然,不要勾選雲中托管。
-
空白項目結構
上圖是新建完成的空白項目結構,你會發現Models,Data等文件夾都不存在,這里需要手動新建,並且丟進去一個class。
必須要丟進去個class文件,讓類定義的時候聲明出Models和Data的命名空間,否則待會生成models文件的時候會報錯。
-
增加項目引用
Asp.net core項目有兩種方法增加引用,一種是直接修改project.json,另一種是nuget。
上述方法打開控制台
輸入如下命令
會發現最后的Tools安裝不了,可能nuget沒同步過來,可以在project.json中手動添加。
最后的文件如下:
主要是圈選中的部分。可以看到這里我們用來操作PostgreSQL的Provider是Npgsql,這是aspnet下的一個子項目。可以在github找到。
其他EntityFrameworkCore的引用,是從示例項目中搬來的。
-
生成Models文件
這里要說明一下,微軟MVC教程中時使用的模板創建的Controller,他會自動創建ApplicationDBContext和對應的Views視圖文件,以及一些其他的內容。因為他更新了ApplicationDBContext文件和ApplicationDbContextModelSnapshot文件,所以控制台執行dotnet ef命令才會正常完成,這里無法用到這些方法。因為沒選"個人用戶賬戶",這里請自行嘗試。
dotnet ef migrations add Initial
dotnet ef database update
由於自行寫DBContext類實現太麻煩,我們這里采取開發中常用的辦法,首先設計數據庫,然后根據數據庫生成類。
在數據庫新建表如下:(測試用,看看就好)
然后回到VS2015,在Nuget程序包控制台輸入:
Scaffold-DbContext "Server=192.168.1.6;Database=testdb;User ID=test;Password=test;" Npgsql.EntityFrameworkCore.PostgreSQL -OutputDir Models
這個命令不用解釋了吧?數據庫連接,使用的數據庫provider和輸出目錄。
順利完成的話,Models下會出現幾個類文件。
如果報錯的話,請根據錯誤提示進行處理。處理原則,首先保證程序能夠編譯通過,然后確保Models下面沒有存在本次要生成的同名文件,然后確認目前系統沒有其他DBContext存在。現在dotnet core的好多工具都在完善中,健壯性都有待提高。
打開看看生成的文件:
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
namespace PostgresSqlDemo.Models
{
public partial class testdbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. See http://go.microsoft.com/fwlink/?LinkId=723263 for guidance on storing connection strings.
optionsBuilder.UseNpgsql(@"Server=192.168.1.6;Database=testdb;User ID=test;Password=test;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>(entity =>
{
entity.ToTable("blog");
entity.Property(e => e.Id)
.HasColumnName("id")
.ValueGeneratedNever();
entity.Property(e => e.Title)
.IsRequired()
.HasColumnName("title")
.HasColumnType("varchar")
.HasMaxLength(300);
entity.Property(e => e.Url)
.IsRequired()
.HasColumnName("url")
.HasColumnType("varchar")
.HasMaxLength(300);
});
modelBuilder.Entity<TbUser>(entity =>
{
entity.HasKey(e => e.Userid)
.HasName("PK_tb_user");
entity.ToTable("tb_user");
entity.Property(e => e.Userid)
.HasColumnName("userid")
.ValueGeneratedNever();
entity.Property(e => e.Age).HasColumnName("age");
entity.Property(e => e.Name)
.IsRequired()
.HasColumnName("name")
.HasColumnType("varchar")
.HasMaxLength(30);
});
}
public virtual DbSet<Blog> Blog { get; set; }
public virtual DbSet<TbUser> TbUser { get; set; }
}
}
Entityframework中,默認是一個數據庫使用唯一一個DBContext類進行管理。
-
修改DBContext文件
模板已經提示給你,需要修改這部分代碼,實現動態配置連接的目的。
進行如下修改:
注釋掉OnConfiguring方法,增加構造函數,我們把注冊放到Startup中去完成。
-
添加數據庫連接配置項
打開appsetting.json文件,增加如下配置:
其實這就相當於原來asp.net中的Web.config中的ConnectionStrings。
-
注冊DBContext
打開StartUP.cs 文件,找到ConfigureServices方法,添加選中代碼:
到這里就看明白了吧,注冊DBContext,使用Npgsql,並給出數據庫連接字符串。
-
增加控制器Controller
在Controller文件夾下新建Controller文件,並添加如下代碼:
using Microsoft.AspNetCore.Mvc;
using PostgresSqlDemo.Data;
using PostgresSqlDemo.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
namespace PostgresSqlDemo.Controllers
{
public class TbUserController : Controller
{
private readonly testdbContext _context;
public TbUserController(testdbContext context)
{
_context = context;
}
// GET: tbusers
public async Task<IActionResult> Index()
{
return View(await _context.TbUser.ToListAsync());
}
public async Task<IActionResult> Details(Guid? id)
{
if (id == null)
{
return NotFound();
}
var tbuser = await _context.TbUser.SingleOrDefaultAsync(m => m.Userid == id);
if (tbuser == null)
{
return NotFound();
}
return View(tbuser);
}
// GET: tbusers/Create
public IActionResult Create()
{
return View();
}
// POST: tbusers/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Userid,Name,Age")] TbUser tbuser)
{
if (ModelState.IsValid)
{
tbuser.Userid = Guid.NewGuid();
_context.Add(tbuser);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(tbuser);
}
// GET: tbusers/Edit/5
public async Task<IActionResult> Edit(Guid? id)
{
if (id == null)
{
return NotFound();
}
var tbuser = await _context.TbUser.SingleOrDefaultAsync(m => m.Userid == id);
if (tbuser == null)
{
return NotFound();
}
return View(tbuser);
}
// POST: tbusers/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(Guid id, [Bind("Userid,Name,Age")] TbUser tbuser)
{
if (id != tbuser.Userid)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(tbuser);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!tbuserExists(tbuser.Userid))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction("Index");
}
return View(tbuser);
}
// GET: tbusers/Delete/5
public async Task<IActionResult> Delete(Guid? id)
{
if (id == null)
{
return NotFound();
}
var tbuser = await _context.TbUser.SingleOrDefaultAsync(m => m.Userid == id);
if (tbuser == null)
{
return NotFound();
}
return View(tbuser);
}
// POST: tbusers/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(Guid id)
{
var tbuser = await _context.TbUser.SingleOrDefaultAsync(m => m.Userid == id);
_context.TbUser.Remove(tbuser);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
private bool tbuserExists(Guid id)
{
return _context.TbUser.Any(e => e.Userid == id);
}
}
}
如果你看過Getting Started Asp.net core MVC的話,相信應該能夠看懂,基本是把使用新增Controller模板生成的方法修改了一下拿過來用了。
Asp.net core mvc中,默認路由名成為(name)Controller,所以我們這里叫TbUserController,訪問的時候是http://youip/TbUser這樣。並且默認是觸發Index方法。
Controller中,每個方法,都相當於一個客戶端Form的Action。沒有特殊聲明的方法,默認為HttpGet,可以直接請求。需要Post數據的,請手動增加[HttpPost]聲明。
Async是C# 5.0后提供的關鍵字,是自動實現一個異步的方法,需要配合await關鍵字使用。Await會被自動轉換為一個async的異步請求,后面的代碼,會被自動放到completed方法中執行。這樣處理能夠極大的提高服務器的並發性能,具體請自行學習。
-
增加Views頁面
增加對應的頁面如下:
Index.cshtml
@model IEnumerable<PostgresSqlDemo.Models.TbUser>
@{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Userid)
</th>
<th>
@Html.DisplayNameFor(model => model.Name)
</th>
<th>
@Html.DisplayNameFor(model => model.Age)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Userid)
</td>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Age)
</td>
<td>
<a asp-action="Edit" asp-route-id="@item.Userid">Edit</a> |
<a asp-action="Details" asp-route-id="@item.Userid">Details</a> |
<a asp-action="Delete" asp-route-id="@item.Userid">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Create.cshtml
@model PostgresSqlDemo.Models.TbUser
@{
ViewData["Title"] = "Create";
}
<h2>Create</h2>
<form asp-action="Create">
<div class="form-horizontal">
<h4>TbUser</h4>
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="Age" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Age" class="form-control" />
<span asp-validation-for="Age" class="text-danger" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
</form>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Delete.cshtml
@model PostgresSqlDemo.Models.TbUser
@{
ViewData["Title"] = "Delete";
}
<h2>Delete</h2>
<h3>Are you sure you want to delete this?</h3>
<div>
<h4>Blog</h4>
<hr />
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.Userid)
</dt>
<dd>
@Html.DisplayFor(model => model.Userid)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Name)
</dt>
<dd>
@Html.DisplayFor(model => model.Name)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Age)
</dt>
<dd>
@Html.DisplayFor(model => model.Age)
</dd>
</dl>
<form asp-action="Delete">
<div class="form-actions no-color">
<input type="submit" value="Delete" class="btn btn-default" /> |
<a asp-action="Index">Back to List</a>
</div>
</form>
</div>
Details.cshtml
@model PostgresSqlDemo.Models.TbUser
@{
ViewData["Title"] = "Details";
}
<h2>Details</h2>
<div>
<h4>Blog</h4>
<hr />
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.Userid)
</dt>
<dd>
@Html.DisplayFor(model => model.Userid)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Name)
</dt>
<dd>
@Html.DisplayFor(model => model.Name)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Age)
</dt>
<dd>
@Html.DisplayFor(model => model.Age)
</dd>
</dl>
</div>
<div>
<a asp-action="Edit" asp-route-id="@Model.Userid">Edit</a> |
<a asp-action="Index">Back to List</a>
</div>
Edit.cshtml
@model PostgresSqlDemo.Models.TbUser
@{
ViewData["Title"] = "Edit";
}
<h2>Edit</h2>
<form asp-action="Edit">
<div class="form-horizontal">
<h4>Blog</h4>
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Userid" />
<div class="form-group">
<label asp-for="Name" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="Age" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Age" class="form-control" />
<span asp-validation-for="Age" class="text-danger" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
</form>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
-
增加驗證腳本引用View文件
<environment names="Development">
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
</environment>
<environment names="Staging,Production">
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.14.0/jquery.validate.min.js"
asp-fallback-src="~/lib/jquery-validation/dist/jquery.validate.min.js"
asp-fallback-test="window.jQuery && window.jQuery.validator">
</script>
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validation.unobtrusive/3.2.6/jquery.validate.unobtrusive.min.js"
asp-fallback-src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"
asp-fallback-test="window.jQuery && window.jQuery.validator && window.jQuery.validator.unobtrusive">
</script>
</environment>
-
修改Layout.cshtml文件
增加如上兩個連接。Blog的添加參考TbUser。這里主要是想讓大家看出來DBContext的用法,所以特意弄了兩個實體類。
-
然后vs里面可以運行了,效果如下:
這里沒有改默認的頁面。
再看我們加的頁面。
並且asp.net mvc默認使用了bootstrap,所以我們可以用chrome的手機模式看看。
-
改成中文顯示
改一下TbUser.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace PostgresSqlDemo.Models
{
public partial class TbUser
{
[Display(Name = "用戶編號")]
public Guid Userid { get; set; }
[Display(Name = "用戶姓名")]
public string Name { get; set; }
[Display(Name = "用戶年齡")]
public int? Age { get; set; }
}
}
再看看頁面
- 加一下驗證
改成中文驗證
[Display(Name = "用戶姓名")]
[StringLength(10, MinimumLength = 3,ErrorMessage = "字段{0}長度不能小於3,總長度不能大於10")]
[Required]
public string Name { get; set; }
-
最后就是部署到Ubuntu服務器了。
將WebApp項目發布出來,使用SSH Secure File Transfer上傳到服務器,然后參照前文發布。
總結:
Asp.net Core 目前來說功能性還不完善,暫時不建議大型應用往上遷移,但是如果是小型項目,比較簡單,可以在仔細論證后嘗試使用。注意仔細論證,因為目前.net core並不是所有類庫都能夠實現跨平台。簡單來說,System.Drawing就無法使用,所以后台畫水印就需要其他三方庫,這個請自行仔細論證。不過小項目發布到linux虛擬機上面,確實可以省一筆錢。