这一小节主要的内容是在一个ASP Net Core Web项目中应用EFCore,下面是主要的步骤:
解决方案搭建
创建解决方案,添加ASP NetCore Web 项目。选择ASP Net Core 2.2的MVC模板。
添加Net Core类库AspEfCore.Domain和AspEfCore.Data。
给Domain项目添加Model:City和Province,添加的时候可以用Resharper的Refactor功能调整命名空间。
在Data项目中安装EntityFramework.SqlServer的NuGet包。
添加项目的引用关系:Data项目需要引用Domain,Web项目需要引用Domain和Data。
)
在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是如何获取数据库连接字符串的配置信息的?
至此我们可能想到,这个Web项目也需要引用EntityFrameworkCore和SQLServer。但是,我们可以查看下Web项目的csproj文件,会发现依赖库中包含Microsoft.AspNetCore.App这个包。
而这个包中已经含有EFCore和EFCore.Sqlserver。除此之外,AspNetCore.App这个package中还依赖了很多其他的包。
数据迁移
把Web项目作为启动项目,把Data项目作为package Managee Console的默认项目后,就可以开始迁移了:
通过SQL Server对象 资源管理器可以查看我们的数据库表已经已经创建成功了。
再建立一个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; }
}
}
然后进行这次多对多的迁移及同步:
下面进行表一对一关系类型配置:
创建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
更新完成后可以查看一下数据表结构是否正确: