Entity Framework Core 2.X Crash Tutorial (2)


这一小节主要的内容是在一个ASP Net Core Web项目中应用EFCore,下面是主要的步骤:

解决方案搭建

创建解决方案,添加ASP NetCore Web 项目。选择ASP Net Core 2.2的MVC模板。
AddSolutionWeb
AddMVCWeb
添加Net Core类库AspEfCore.Domain和AspEfCore.Data。
AddDomianAndDataProject
给Domain项目添加Model:City和Province,添加的时候可以用Resharper的Refactor功能调整命名空间。
AddCityAndProvinceModel
在Data项目中安装EntityFramework.SqlServer的NuGet包。
AddNuGetEFSQLServerInDataProject
添加项目的引用关系:Data项目需要引用Domain,Web项目需要引用Domain和Data。
DataProjectReference)WebProjectReference
在Data项目中添加MyContext
写Demo 的话可以这么用,但是生产环境一般不这么用。

namespace AspEfCore.Data
{
 public class MyContext:DbContext
   {
   public DbSet<Province> Provinces { get; set; }
   public DbSet<City> Cities { get; set; }
     protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
     {
       optionsBuilder.UseSqlServer("Server=(localdb)\\MSSQLLocalDB; Database=EFCoreDemo; Trusted_Connection=True;");
     }
   }
}

options里面包含连接字符串,里面也包含了一些其他的配置信息。options决定哪个数据库的供应商,决定哪个provider。

依赖注入

Startup.CS 中利用依赖注入的机制注入MyContext,然后在构造函数注入。
ConfigureServices这个方法中,在AddMvc后面添加如下关于MyContext的配置代码:

 public void ConfigureServices(IServiceCollection services)
 {
   services.Configure<CookiePolicyOptions>(options =>
   {
     // This lambda determines whether user consent for non-essential cookies is needed for a given request.
     options.CheckConsentNeeded = context => true;
     options.MinimumSameSitePolicy = SameSiteMode.None;
   });
     services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
     services.AddDbContext<MyContext>(options =>
    {
      options.UseSqlServer("Server=(localdb)\\MSSQLLocalDB; Database=ASPEFCoreDemo; Trusted_Connection=True;");
    });
 }

建议将连接字符创写在appsetting中,这里可以做如下优化:

{
  "Logging": {
    "LogLevel": 
    {
     "Default": "Debug",
     "System": "Information",
     "Microsoft": "Information"
    }
  },
  "ConnectionStrings": 
  {
   "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB; Database=ASPEFCoreDemo; Trusted_Connection=True;"
  }
}

然后相应的在Startup.cs的ConfigureServices中可以做如下的调用:

 public void ConfigureServices(IServiceCollection services)
 {
   services.Configure<CookiePolicyOptions>(options =>
   {
     // This lambda determines whether user consent for non-essential cookies is needed for a given request.
     options.CheckConsentNeeded = context => true;
     options.MinimumSameSitePolicy = SameSiteMode.None;
   });
     services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
     services.AddDbContext<MyContext>(options =>
     {
       options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
     });
 }

这里我们可以探究一下,GetConnectionString是如何获取数据库连接字符串的配置信息的?
GetConnectionString

至此我们可能想到,这个Web项目也需要引用EntityFrameworkCore和SQLServer。但是,我们可以查看下Web项目的csproj文件,会发现依赖库中包含Microsoft.AspNetCore.App这个包。
Webcsproj
WebcsprojApp
而这个包中已经含有EFCore和EFCore.Sqlserver。除此之外,AspNetCore.App这个package中还依赖了很多其他的包。
AspNetCoreApp

数据迁移

把Web项目作为启动项目,把Data项目作为package Managee Console的默认项目后,就可以开始迁移了:
add-Migration-Initial
通过SQL Server对象 资源管理器可以查看我们的数据库表已经已经创建成功了。
SQLServerBrowser
再建立一个Model,叫作Company,目的是建立表的多对多的关系。

namespace AspEfCore.Domain
{
 public class Company
 {
   public int Id { get; set; }
   public string Name { get; set; }
   public DateTime EstablishDate { get; set; }
   public string LegalPerson { get; set; }
  }
}

想要在City和Company之间建立多对多的关系,仅使用这两个Model是不够的,必须使用中间的Model才能实现。
新建CityCompany这个中间Model:

namespace AspEfCore.Domain
{
 public class CityCompany
 {
   public int CityId { get; set; }
   public City City { get; set; }
   public int CompanyId { get; set; }
   public Company Company { get; set; }
 }
}

现在中间Model的导航属性有了,分别去City和Company这个两个Model中做CityCompany的导航属性:

namespace AspEfCore.Domain
{
 public class City
 {
   public City()
   {
     CityCompanies=new List<CityCompany>();
   }
   public int Id { get; set; }
   public string Name { get; set; }
   public string AreaCode { get; set; }
   public int ProvinceId { get; set; }
   public Province Province { get; set; }
   public List<CityCompany> CityCompanies { get; set; }
 }
}

namespace AspEfCore.Domain
{
 public class Company
 {
  public Company()
   {
     CityCompanies=new List<CityCompany>();
   }
   public int Id { get; set; }
   public string Name { get; set; }
   public DateTime EstablishDate { get; set; }
   public string LegalPerson { get; set; }
   public List<CityCompany> CityCompanies { get; set; }
 }
}

最好明确写一下表的关系,在DBContext中,可以override一个方法OnModelCreating,配置这些关系。
在CityCompany中可以通过CityId和CompanyId作为联合主键。

namespace AspEfCore.Data
{
 public class MyContext:DbContext
 {
   public MyContext(DbContextOptions<MyContext> options): base(options)
   {
   }
   protected override void OnModelCreating(ModelBuilder modelBuilder)
   {
    modelBuilder.Entity<CityCompany>()
    .HasKey(x => new { x.CityId, x.CompanyId });
    modelBuilder.Entity<City>()
    .HasOne(x => x.Province).WithMany(x => x.Cities).HasForeignKey(x => x.ProvinceId);
    modelBuilder.Entity<CityCompany>()
    .HasOne(x => x.City).WithMany(x => x.CityCompanies).HasForeignKey(x => x.CityId);
    modelBuilder.Entity<CityCompany>()
    .HasOne(x => x.Company).WithMany(x => x.CityCompanies).HasForeignKey(x => x.CompanyId);
   }
   public DbSet<Province> Provinces { get; set; }
   public DbSet<City> Cities { get; set; }
   public DbSet<CityCompany> CityCompanies { get; set; }
   public DbSet<Company> Companies{ get; set; }
 }
}

然后进行这次多对多的迁移及同步:
add-Migration-AddManyTomany
下面进行表一对一关系类型配置:
创建Mayor(市长),和City(城市)一对一,即一个城市只有市长,一个市长也只能对应一个城市。

namespace AspEfCore.Domain
{
 public class Mayor
 {
   public int CityId { get; set; }
   public int Id { get; set; }
   public string FirstName { get; set; }
   public string LastName { get; set; }
   public DateTime BirthDate { get; set; }
   public Gender Gender { get; set; }
   public City City { get; set; }
 }
}

同时在City类中添加Mayor导航属性:

 public class City
 {
   public City()
   {
     CityCompanies=new List<CityCompany>();
   }
   public int Id { get; set; }
   public string Name { get; set; }
   public string AreaCode { get; set; }
   public int ProvinceId { get; set; }
   public Province Province { get; set; }
   public List<CityCompany> CityCompanies { get; set; }
   public Mayor Mayor { get; set; }
 }

解读一下这种一对一关系:
城市是必须有的,市长是可选的。建立市长的时候必须有城市,建立城市的时候不一定要有市长。
这种情况EFCore是支持的。但是如果城市是必须的,市长也是必须的,这样的情况EFCore就无法支持了,需要业务逻辑进行实现。
然后,完善一下MyContext这个类,配置一对一的关系:

 public class MyContext:DbContext
 {
   public MyContext(DbContextOptions<MyContext> options): base(options)
   {
   }
   protected override void OnModelCreating(ModelBuilder modelBuilder)
   {
     modelBuilder.Entity<CityCompany>()
     .HasKey(x => new { x.CityId, x.CompanyId });
     modelBuilder.Entity<City>()
     .HasOne(x => x.Province).WithMany(x => x.Cities).HasForeignKey(x => x.ProvinceId);
     modelBuilder.Entity<CityCompany>()
     .HasOne(x => x.City).WithMany(x => x.CityCompanies).HasForeignKey(x => x.CityId);
     modelBuilder.Entity<CityCompany>()
     .HasOne(x => x.Company).WithMany(x => x.CityCompanies).HasForeignKey(x => x.CompanyId);
     modelBuilder.Entity<Mayor>()
     .HasOne(x => x.City).WithOne(x => x.Mayor).HasForeignKey<Mayor>(x => x.CityId);
   }
   public DbSet<Province> Provinces { get; set; }
   public DbSet<City> Cities { get; set; }
   public DbSet<CityCompany> CityCompanies { get; set; }
   public DbSet<Company> Companies{ get; set; }
   public DbSet<Mayor> Mayor { get; set; }
 }

最后执行迁移命令:
1)add-migration Update
2)update-database
ExcuteMigration
更新完成后可以查看一下数据表结构是否正确:
UpdateResultView


文章作者: Chaoqiang
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Chaoqiang !
评论
 上一篇
Entity Framework Core 2.X  Crash Tutorial (3) Entity Framework Core 2.X Crash Tutorial (3)
这一小节主要的内容是通过观察EFCore的执行日志了解EFCore的执行过程。 将EFCore的SQL执行过程输出到Log输出到Console:ASPNetCore的项目已经集成好了Log工具,可以通过查看Program.cs中的Creat
下一篇 
Entity Framework Core 2.X  Crash Tutorial (1) Entity Framework Core 2.X Crash Tutorial (1)
前言Entity Framework是.Net开发平台主流的数据访问框架,EFCore是一个真正的ORM(对象关系映射)框架。为什么这么说?因为很多开发人员经常用一些“轻量级”的ORM与EF进行比较,进而得出EF性能低下难以使用的结论。他们
  目录