1. 程式人生 > >.net core 2.0 Code First Fluent API配置

.net core 2.0 Code First Fluent API配置

asp 完成 eating cte word his text -1 src

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配置