zoukankan      html  css  js  c++  java
  • 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虚拟机上面,确实可以省一笔钱。

  • 相关阅读:
    1015. 德才论
    1014. 福尔摩斯的约会
    1013. 数素数
    1012. 数字分类
    1011. A+B和C
    1010. 一元多项式求导
    1009. 说反话
    1008. 数组元素循环右移问题
    1007. 素数对猜想
    1006. 换个格式输出整数
  • 原文地址:https://www.cnblogs.com/lanwilliam/p/5663931.html
Copyright © 2011-2022 走看看