Asp.net Core基於MVC框架實現PostgreSQL操作


簡單介紹

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

開始

  1. 新建項目,選擇Asp.net Core Web Application項目模板。

  2. 選擇Web應用程序模板,然后修改身份驗證哪里,選擇不進行身份驗證。

    這里要說一下,Asp.net core項目中,包含一個Identity子項目,在GitHub上有介紹如下:

    這里大家一看就知道了,這就是原來提供的ASP.net自帶的權限框架。這個框架現在其實非常強大了,還支持Google和TWriter等OAuth用法,不過缺點是只支持SQL Server數據庫。如果選擇了個人用戶賬戶,那么會默認使用這個框架創建項目。我們希望用PostgreSQL,所以不能選他,很遺憾。而且目前創建控制器等一些內置模板用法,都是基於SqlServer,用到這個Identity,大家如果看過微軟的GettingStarted,就會看到介紹,所以建議大家首先學習微軟GettingStarted。

    當然,不要勾選雲中托管。

  3. 空白項目結構

    上圖是新建完成的空白項目結構,你會發現Models,Data等文件夾都不存在,這里需要手動新建,並且丟進去一個class。

    必須要丟進去個class文件,讓類定義的時候聲明出Models和Data的命名空間,否則待會生成models文件的時候會報錯。

  4. 增加項目引用

    Asp.net core項目有兩種方法增加引用,一種是直接修改project.json,另一種是nuget。

    上述方法打開控制台

    輸入如下命令

    會發現最后的Tools安裝不了,可能nuget沒同步過來,可以在project.json中手動添加。

    最后的文件如下:

    主要是圈選中的部分。可以看到這里我們用來操作PostgreSQL的Provider是Npgsql,這是aspnet下的一個子項目。可以在github找到。

    其他EntityFrameworkCore的引用,是從示例項目中搬來的。

  5. 生成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類進行管理。

  1. 修改DBContext文件

    模板已經提示給你,需要修改這部分代碼,實現動態配置連接的目的。

    進行如下修改:

    注釋掉OnConfiguring方法,增加構造函數,我們把注冊放到Startup中去完成。

  2. 添加數據庫連接配置項

    打開appsetting.json文件,增加如下配置:

    其實這就相當於原來asp.net中的Web.config中的ConnectionStrings。

  3. 注冊DBContext

    打開StartUP.cs 文件,找到ConfigureServices方法,添加選中代碼:

        到這里就看明白了吧,注冊DBContext,使用Npgsql,並給出數據庫連接字符串。

  1. 增加控制器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方法中執行。這樣處理能夠極大的提高服務器的並發性能,具體請自行學習。

  2. 增加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");}

}

 

  1. 增加驗證腳本引用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>

 

  1. 修改Layout.cshtml文件

    增加如上兩個連接。Blog的添加參考TbUser。這里主要是想讓大家看出來DBContext的用法,所以特意弄了兩個實體類。

  2. 然后vs里面可以運行了,效果如下:

    這里沒有改默認的頁面。

    再看我們加的頁面。

    並且asp.net mvc默認使用了bootstrap,所以我們可以用chrome的手機模式看看。

  3. 改成中文顯示

    改一下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; }

    }

    }

    再看看頁面

  4. 加一下驗證

    改成中文驗證

            [Display(Name = "用戶姓名")]

        [StringLength(10, MinimumLength = 3,ErrorMessage = "字段{0}長度不能小於3,總長度不能大於10")]

        [Required]

    public string Name { get; set; }

    

  1. 最后就是部署到Ubuntu服務器了。

    將WebApp項目發布出來,使用SSH Secure File Transfer上傳到服務器,然后參照前文發布。

    前文:http://blog.csdn.net/lanwilliam/article/details/51880124

總結:

    Asp.net Core 目前來說功能性還不完善,暫時不建議大型應用往上遷移,但是如果是小型項目,比較簡單,可以在仔細論證后嘗試使用。注意仔細論證,因為目前.net core並不是所有類庫都能夠實現跨平台。簡單來說,System.Drawing就無法使用,所以后台畫水印就需要其他三方庫,這個請自行仔細論證。不過小項目發布到linux虛擬機上面,確實可以省一筆錢。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM