.net core 2.0 Code First Fluent API配置

A.net core 2.0新特性支持通过IEntityTypeConfiguration添加Code First配置到一个封装类。新建目标框架为.NET Core类库新建完了以后右键点击程序集,选择属性,选择目标框架为.NET Core 2.0 在EntityFrameworkCo...

A.net core 2.0新特性支持通过IEntityTypeConfiguration<>添加Code First配置到一个封装类。

新建目标框架为.NET Core类库

新建完了以后右键点击程序集,选择属性,选择目标框架为.NET Core 2.0

 

在EntityFrameworkCore程序集中添加类User(用户)、Address(用户住址)、Book(书)、Author(作者)。这里不讨论各个类设计的合理性和程序架构,主要目的是为了演示各个类之间的关系配置。

User和Address类用于演示配置1-0..1关系。User和Book用于演示1-N,即1对多关系。Book和Author用于演示N-N关系,即多对多关系。

注意:在EF6.x中,当配置1-0..1关系时,附属表的主键值可以与主表的主键相同,主表对应这里的User,附属表对应Address,而由于篇幅有限,在.net core 2.0中不讨论此实现,即每个表都有自己的主键,表关联都通过外键作为实现。

User表实现如下

using System;

namespace EntityFrameworkCore
{
    /// <summary>
    /// 用户模型
    /// </summary>
    public sealed class User
    {
        public User()
        {
            ID = Guid.NewGuid();
            TimeCreated = DateTime.Now;
        }

        public Guid ID { get; private set; }

        /// <summary>
        /// 用户名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 用户登录密码
        /// </summary>
        public string Password { get; set; }

        /// <summary>
        /// 用户创建时间
        /// </summary>
        public DateTime TimeCreated { get; private set; }

        public Address Address { get; set; }

    }
}

Address表实现如下

using System;

namespace EntityFrameworkCore
{
    /// <summary>
    /// 用户地址模型
    /// </summary>
    public sealed class Address
    {
        public Address()
        {
            ID = Guid.NewGuid();
            TimeCreated = DateTime.Now;
        }
        public Guid ID { get; private set; }

        /// <summary>
        /// 国家
        /// </summary>
        public string Country { get; set; }

        /// <summary>
        /// 省
        /// </summary>
        public string Province { get; set; }

        /// <summary>
        /// 城市
        /// </summary>
        public string City { get; set; }

        /// <summary>
        /// 区/县
        /// </summary>
        public string Area { get; set; }

        /// <summary>
        /// 街道
        /// </summary>
        public string Street { get; set; }

        /// <summary>
        /// 创建时间
        /// </summary>
        public DateTime TimeCreated { get; private set; }

        /// <summary>
        /// 用户ID
        /// </summary>
        public Guid UserID { get; set; }

        public User User { get; set; }
    }
}

Book实现如下

using System;
using System.Collections.Generic;

namespace EntityFrameworkCore
{
    /// <summary>
    /// 书模型
    /// </summary>
    public sealed class Book
    {
        public Book()
        {
            ID = Guid.NewGuid();
            TimeCreated = DateTime.Now;
        }

        public Guid ID { get; private set; }
        public DateTime TimeCreated { get; private set; }
        /// <summary>
        /// 书名
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 出版号
        /// </summary>
        public string PublicNo { get; set; }

        public List<Author> Authors { get; set; }
    }
}

Author实现如下

using System;
using System.Collections.Generic;

namespace EntityFrameworkCore
{
    /// <summary>
    /// 作者模型
    /// </summary>
    public sealed class Author
    {
        public Author()
        {
            ID = Guid.NewGuid();
            TimeCreated = DateTime.Now;
        }

        public Guid ID { get; private set; }

        public DateTime TimeCreated { get; private set; }

        /// <summary>
        /// 作者名字
        /// </summary>
        public string Name { get; set; }

        public List<Book> Books { get; set; }
    }
}

 

点击工具->NuGet包管理器->程序包管理控制台,在程序包管理控制台中输入命令:Install-Package Microsoft.EntityFrameworkCore.SqlServer

 

在EntityFrameworkCore中添加文件夹ModelConfigs


1-0..1关系配置User—Address:

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace EntityFrameworkCore.ModelConfigs
{
    public sealed class UserConfig:IEntityTypeConfiguration<User>
    {
        public void Configure(EntityTypeBuilder<User> builder)
        {
            builder.HasKey(q => q.ID);
            builder.Property(q => q.Name).HasMaxLength(100).IsRequired();
            builder.Property(q => q.Password).HasMaxLength(100).IsRequired();
            builder.Property(q => q.TimeCreated).IsRequired();

            //使用HasOne和WithOne两个扩展方法对User表和Address表进行1-1关系配置
            builder.HasOne(q => q.Address).WithOne(q => q.User).HasForeignKey<Address>(q => q.UserID);
        }
    }
}
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace EntityFrameworkCore.ModelConfigs
{
    public sealed class AddressConfig: IEntityTypeConfiguration<Address>
    {
        public void Configure(EntityTypeBuilder<Address> builder)
        {
            builder.HasKey(q => q.ID);
            builder.Property(q => q.Area).HasMaxLength(100).IsRequired();
            builder.Property(q => q.City).HasMaxLength(100).IsRequired();
            builder.Property(q => q.Country).HasMaxLength(100).IsRequired();
            builder.Property(q => q.Province).HasMaxLength(100).IsRequired();
            builder.Property(q => q.Street).HasMaxLength(200).IsRequired();
            builder.Property(q => q.TimeCreated).IsRequired();
            builder.Property(q => q.UserID).IsRequired();
        }
    }
}

 

UserConfig和AddressConfig分别对User表数据和Address表数据进行了配置,添加FluentApiDemoDbContext类并继承DbContext,微软官方解释DbContext实例是一个带有数据库的会话并且能用于添加和查询实体。FluentApiDemoDbContext类代码如下:

using System;
using System.Linq;
using System.Reflection;
using Microsoft.EntityFrameworkCore;

namespace EntityFrameworkCore
{
    public class FluentApiDemoDbContext : DbContext
    {
        public FluentApiDemoDbContext(DbContextOptions<FluentApiDemoDbContext> options) : base(options)
        {
        }

        public DbSet<User> User { get; set; }

        public DbSet<Address> Address { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {   
            base.OnModelCreating(modelBuilder);

            var typesToRegister = Assembly.GetExecutingAssembly().GetTypes()
                .Where(q => q.GetInterface(typeof(IEntityTypeConfiguration<>).FullName) != null);
          
            foreach (var type in typesToRegister)
            {

                dynamic configurationInstance = Activator.CreateInstance(type);
                modelBuilder.ApplyConfiguration(configurationInstance);
            }

        }
    }
}

  

如果这里直接把EntityFrameworkCore设为启动项,当在程序包管理控制台中输入:Update-Database时将报错:Unable to create an object of type 'FluentApiDemoDbContext'. Add an implementation of 'IDesignTimeDbContextFactory<FluentApiDemoDbContext>' to the project, or see https://go.microsoft.com/fwlink/?linkid=851728 for additional patterns supported at design time.提示需要添加一个IDesignTimeDbContextFactory<FluentApiDemoDbContext>的实现,这里如果是用Console写Demo,那么可以自行实现该接口,在该文中我将添加WebApi作为启用项。Startup.cs内容如下:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using EntityFrameworkCore;

namespace WebApi
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            var connection = @"Server=(localdb)\mssqllocaldb;Database=FluentApiDemoDb;Trusted_Connection=True;";
            services.AddDbContextPool<FluentApiDemoDbContext>(options => options.UseSqlServer(connection));
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseMvc();
        }
    }
}

 

配置以后将该WebApi项目设置为启动项,在程序包管理控制台中输入:Add-Migration Initial以启动迁移。完成以后,项目会添加Migraions文件夹,

 

在程序包管理控制台中输入:Update-Database以完成数据迁移。完成后打开:视图->SQL Server对象资源管理器,可以查看比较具体的数据结构。

 

这里着重要看Address表中的UserID,UserID做为Address表的外键是非空的,按照文章开头说的1-0..1关系,这里只实现了1-1,并没有实现1-0关系,更改AddressConfig中的关系配置为:

…
builder.HasOne(q => q.User).WithOne(q => q.Address).HasForeignKey<Address>(q => q.UserID).IsRequired(false);

并更改Address中的代码为:

…
public Guid? UserID { get; set; }

添加迁移并更新到数据库


 

1-N关系配置,1个用户拥有多本书。在User表中添加

public List<Book> Books { get; set; }

Book表中添加

public Guid? UserID { get; set; }
public User User { get; set; }
 

在ModelConfigs中添加BookConfig

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace EntityFrameworkCore.ModelConfigs
{
    public sealed class BookConfig: IEntityTypeConfiguration<Book>
    {
        public void Configure(EntityTypeBuilder<Book> builder)
        {
            builder.HasKey(q => q.ID);
            builder.Property(q => q.Name).HasMaxLength(100).IsRequired();
            builder.Property(q => q.PublicNo).HasMaxLength(100).IsRequired();

            builder.HasOne(q => q.User).WithMany(q => q.Books).HasForeignKey(q => q.UserID).IsRequired(false);
        }
    }
}

在FluentApiDemoDbContext中注册实体

public DbSet<Book> Book { get; set; }

 

在程序包管理控制台中添加迁移并更新到数据库:

 


N-N关系配置,当使用EF6.x时,配置多对多关系,EF会自动生成中间表,在.net core中,需要手动添加关系表,实际上在EF6.x中当配置多对多关系时,也可以通过中间表配置1对多的方式实现多对多。

这里通过Book表和Author表实现多对多关系配置。一个作者可以有多本书,一本书有多个作者。

首先添加关系表AuthorsInBooks

using System;

namespace EntityFrameworkCore
{
    public sealed class AuthorsInBooks
    {
        public Guid BookID { get; set; }
        public Book Book { get; set; }

        public Guid AuthorID { get; set; }

        public Author Author { get; set; }
    }
}

同时更改Books表

public List<Author> Authors { get; set; }

改为

public List<AuthorsInBooks> AuthorsInBooks { get; set; }

同理更改Author表

public List<Book> Books { get; set; }

改为

 public List<AuthorsInBooks> AuthorsInBooks { get; set; }

  

添加AuthorsConfig

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace EntityFrameworkCore.ModelConfigs
{
    public sealed class AuthorConfig:IEntityTypeConfiguration<Author>
    {
        public void Configure(EntityTypeBuilder<Author> builder)
        {
            builder.HasKey(q => q.ID);
            builder.Property(q => q.Name).HasMaxLength(100).IsRequired();
            builder.Property(q => q.TimeCreated).IsRequired();
        }
    }
}

 

并在FluentApiDemoDbContext中注册Author

public DbSet<Author> Author { get; set; }

 

接下来添加AuthorsInBooksConfig

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace EntityFrameworkCore.ModelConfigs
{
    public sealed class AuthorsInBooksConfig:IEntityTypeConfiguration<AuthorsInBooks>
    {
        public void Configure(EntityTypeBuilder<AuthorsInBooks> builder)
        {
            builder.HasKey(q => new
            {
                q.AuthorID,
                q.BookID
            });

            builder.HasOne(q => q.Author).WithMany(q => q.AuthorsInBooks).HasForeignKey(q => q.AuthorID);
            builder.HasOne(q => q.Book).WithMany(q => q.AuthorsInBooks).HasForeignKey(q => q.BookID);
        }
    }
}

在管理控制台中添加迁移并更新到数据库

 

 

本文标题为:.net core 2.0 Code First Fluent API配置