1. 程式人生 > >一起學ASP.NET Core 2.0學習筆記(二): ef core2.0 及mysql provider 、Fluent API相關配置及遷移

一起學ASP.NET Core 2.0學習筆記(二): ef core2.0 及mysql provider 、Fluent API相關配置及遷移

upd order rac option 包管理 rtl code create .net core

不得不說微軟的技術叠代還是很快的,上了微軟的船就得跟著她走下去,前文一起學ASP.NET Core 2.0學習筆記(一): CentOS下 .net core2 sdk nginx、supervisor、mysql環境搭建搭建好了.net core linux的相關環境,今天就來說說ef core相關的配置及遷移:

簡介:

Entity Framework(以下簡稱EF) 是微軟以 ADO.NET 為基礎所發展出來的對象關系對應 (O/R Mapping) 解決方案,EF Core是Entity framework得下一版本,相比EF原來版本(4-6.1),顯得更加輕量級,相比同樣出身名門的dapper還是顯得有點龐大,不過魚與熊掌向來不可兼得,想想一堆堆語法糖,方便的linq查詢以及lambda表達式,可以很大程度上從T-SQL語法上脫身出來很多,在配置過程中也碰到一些問題,記錄下來,以便後期翻閱:

一.poco的創建以及Fluent Api關系配置

1.一對多關系:

項目中最常見的就是一對多關系了,以語言及語言為例

語言(Language)類

技術分享
 1 public partial class Language : BaseEntity<int>
 2     {
 3         private ICollection<LocaleStringResource> _localeStringResources;
 4 
 5         /// <summary>
 6         /// Gets or sets the name
 7         ///
</summary> 8 9 public string Name { get; set; } 10 11 /// <summary> 12 /// Gets or sets the language culture 13 /// </summary> 14 15 public string LanguageCulture { get; set; } 16 17 /// <summary> 18 /// Gets or sets the unique SEO code
19 /// </summary> 20 21 public string UniqueSeoCode { get; set; } 22 23 /// <summary> 24 /// Gets or sets the flag image file name 25 /// </summary> 26 27 public string FlagImageFileName { get; set; } 28 29 /// <summary> 30 /// Gets or sets a value indicating whether the language supports "Right-to-left" 31 /// </summary> 32 33 public bool Rtl { get; set; } 34 35 36 /// <summary> 37 /// Gets or sets a value indicating whether the language is published 38 /// </summary> 39 40 public bool Published { get; set; } 41 42 /// <summary> 43 /// Gets or sets the display order 44 /// </summary> 45 46 public int DisplayOrder { get; set; } 47 48 public bool IsDefault { get; set; } 49 50 /// <summary> 51 /// Gets or sets locale string resources 52 /// </summary> 53 public virtual ICollection<LocaleStringResource> LocaleStringResources 54 { 55 get { return _localeStringResources ?? (_localeStringResources = new HashSet<LocaleStringResource>()); } 56 protected set { _localeStringResources = value; } 57 }
View Code

語言資源(LocaleStringResource):

技術分享
 1 public partial class LocaleStringResource : BaseEntity<int>
 2     {
 3         /// <summary>
 4         /// Gets or sets the language identifier
 5         /// </summary>
 6         public int LanguageId { get; set; }
 7 
 8         /// <summary>
 9         /// Gets or sets the resource name
10         /// </summary>
11         public string ResourceName { get; set; }
12 
13         /// <summary>
14         /// Gets or sets the resource value
15         /// </summary>
16         public string ResourceValue { get; set; }
17 
18         /// <summary>
19         /// Gets or sets a value indicating whether this resource was installed by a plugin
20         /// </summary>
21         public bool? IsFromPlugin { get; set; }
22 
23         /// <summary>
24         /// Gets or sets a value indicating whether this resource was modified by the user
25         /// </summary>
26         public bool? IsTouched { get; set; }
27 
28         /// <summary>
29         /// Gets or sets the language
30         /// </summary>
31         public virtual Language Language { get; set; }
32 
33     }
View Code

其中語言及資源主外鍵關系配置如下

技術分享
 1  public partial class LanguageMapper : IEntityTypeConfiguration<Language>
 2     {
 3         public void Configure(EntityTypeBuilder<Language> builder)
 4         {
 5             builder.ToTable("Language");
 6             builder.HasKey(r => r.Id);
 7             builder.HasIndex(r => r.LanguageCulture);
 8             builder.HasIndex(r => r.Name);
 9             builder.HasIndex(r => r.UniqueSeoCode);
10         }
11     }
View Code
 public partial class LocaleStringResourceMapper : IEntityTypeConfiguration<LocaleStringResource>
    {
        public void Configure(EntityTypeBuilder<LocaleStringResource> builder)
        {
            builder.ToTable("LocaleStringResource");
            builder.HasKey(r => r.Id);
            builder.HasIndex(r => r.LanguageId);
            builder.HasIndex(r => r.ResourceName);
            builder.HasOne(r => r.Language).WithMany(b => b.LocaleStringResources)
                .HasForeignKey(fk => fk.LanguageId);
        }
    }

2、父子關系 :

技術分享
 1  public class TechCategory:BaseEntity<int>
 2     {
 3         /// <summary>
 4         /// 名稱
 5         /// </summary>
 6         public string Name { get; set; }
 7         /// <summary>
 8         /// 分類描述
 9         /// </summary>
10         public string Descript { get; set; }
11         /// <summary>
12         /// meta標題
13         /// </summary>
14         public string MetaTitle { get; set; }
15         /// <summary>
16         /// meta描述
17         /// </summary>
18         public string MetaDescript { get; set; }
19         /// <summary>
20         /// 縮略圖
21         /// </summary>
22         public string Thumb { get; set; }
23         /// <summary>
24         /// 圖片
25         /// </summary>
26         public string Image { get; set; }
27         /// <summary>
28         /// 排序
29         /// </summary>
30         public int Sort { get; set; }
31         /// <summary>
32         /// 父級分類
33         /// </summary>
34         public int? ParentId { get; set; }
35         
36         /// <summary>
37         /// 父級分類
38         /// </summary>
39 
40         public virtual TechCategory Parent { get; set; }
41         public virtual ICollection<Technology> Technologys { get; set; }
42         /// <summary>
43         /// 子級
44         /// </summary>
45         public virtual ICollection<TechCategory> Childs { get; set; }
46         /// <summary>
47         /// 關鍵詞
48         /// </summary>
49         public string Slug { get; set; }
50         /// <summary>
51         /// 創建時間
52         /// </summary>
53         public long CreatedOn { get; set; }
54     }
View Code 技術分享
 1  public class TechCategoryMapper : IEntityTypeConfiguration<TechCategory>
 2     {
 3         public void Configure(EntityTypeBuilder<TechCategory> builder)
 4         {
 5             builder.ToTable("TechCategory");
 6             builder.HasKey(r => r.Id);
 7             builder.HasMany(r => r.Childs).WithOne(x => x.Parent)
 8              .HasForeignKey(fk => fk.ParentId);
 9             builder.HasIndex(r => r.Name);
10         }
11     }
View Code

二、數據庫上下文:

根據前面創建的相關實體類 以及相關mapper類型,我們可以重現DbContext的OnModelCreating的方法,反射出實現了IEntityTypeConfiguration接口的相關mapper類型創建相關實體-數據表的相關對應關系

技術分享
 1  public class DefaultContext : DbContext
 2     {
 3         public DefaultContext(DbContextOptions<DefaultContext> opt)
 4             :base(opt)
 5         {
 6             AutoCommitEnabled = true;
 7         }
 8   protected override void OnModelCreating(ModelBuilder builder)
 9         {
10             base.OnModelCreating(builder);
11             var mappingInterface = typeof(IEntityTypeConfiguration<>);
12             // Types that do entity mapping
13             var mappingTypes = GetType().GetTypeInfo().Assembly.GetTypes()
14                 .Where(x => x.GetInterfaces().Any(y => y.GetTypeInfo().IsGenericType && y.GetGenericTypeDefinition() == mappingInterface));
15 
16             // Get the generic Entity method of the ModelBuilder type
17             var entityMethod = typeof(ModelBuilder).GetMethods()
18                 .Single(x => x.Name == "Entity" &&
19                         x.IsGenericMethod &&
20                         x.ReturnType.Name == "EntityTypeBuilder`1");
21 
22             foreach (var mappingType in mappingTypes)
23             {
24 
25                 // Get the type of entity to be mapped
26                 var genericTypeArg = mappingType.GetInterfaces().Single().GenericTypeArguments.Single();
27 
28                 // Get the method builder.Entity<TEntity>
29                 var genericEntityMethod = entityMethod.MakeGenericMethod(genericTypeArg);
30 
31                 // Invoke builder.Entity<TEntity> to get a builder for the entity to be mapped
32                 var entityBuilder = genericEntityMethod.Invoke(builder, null);
33 
34                 // Create the mapping type and do the mapping
35                 var mapper = Activator.CreateInstance(mappingType);
36                 mapper.GetType().GetMethod("Configure").Invoke(mapper, new[] { entityBuilder });
37             }
38             foreach (var relationship in builder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
39             {
40                 relationship.DeleteBehavior = DeleteBehavior.Restrict;
41             }
42         }
43     }
View Code

三、多數據庫支持:

多數據庫的支持,並不是意味著同時對多種數據庫操作,當然,後面,我們會嘗試同時對多種數據庫操作,這可能需要多個上下文,暫且不論。分布式數據庫。我們的項目可能是在windows上開發的使用的是SqlServer,我們要發布到linux上,SqlServer 2017 據說是支持liunx的,但是還沒出... 當然不是說 SqlServer 就不能裝在liunx上,但是我們的Liunx服務器可能已經安裝了MySql或 Oracle,我們希望使用現有的,又或者是,我們需要切換數據庫。那麽,我們需要可以隨時切換數據庫的支持,以上篇的mysql為例:

1.引用相關包,mysql我用的Pomelo.EntityFrameworkCore.MySql,國人開發的mysql for ef core的相關服務對象,ef

core 2.0中需要指定2.0版本(Install-Package Pomelo.EntityFrameworkCore.MySql -Version 2.0.0-rtm-10059),

2.需改配置文件依據相關配置動態加載數據庫提供對象;相關代碼及配置如下

nuget引用列表

技術分享
1  <ItemGroup>
2     <PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.0" />
3     <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.0.0" />
4     <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.0" />
5     <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.0.0-rtm-10058" />
6   </ItemGroup>
View Code

相關配置文件修改

 "ConnectionStrings": {
    "MongoDbConnectionString": "mongodb://tchistory:[email protected]/history",
    "ConnectionString": "Data Source=127.0.0.1;Initial Catalog=farmdata;User ID=root;Password=123456",
    "DataProvider": "MySql"
  },

在startup.cs 的configureServices方法中添加數據庫上下文

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<DefaultContext>(option => option.UseFarmDatabase(Configuration));
            services.UseFarmService(Configuration);
            services.AddMvc();

        }

數據庫提供對象擴展方法如下:

public static DbContextOptionsBuilder UseFarmDatabase(this DbContextOptionsBuilder optionsBuilder, IConfiguration configuration)
{
string provider = configuration.GetConnectionString("DataProvider"), connection = configuration.GetConnectionString("ConnectionString");
if (provider.Equals(DataBaseServer.SqlServer, StringComparison.InvariantCultureIgnoreCase))
{
return optionsBuilder.UseSqlServer(connection);
}
else if (provider.Equals(DataBaseServer.MySql, StringComparison.InvariantCultureIgnoreCase))
{
return optionsBuilder.UseMySql(connection);
}
else
{
throw Error.Argument("UseDatabaseServer", "No databaseProvider");
}
}

在程序包管理控制臺添加相關遷移 命令 add-migration init: 會在當前目錄下生成相關遷移目錄

技術分享

執行update-database -Verbose提交更改到數據庫,這樣數據庫遷移及更改就算完成了,其中要註意的地方是ef core 2.0 中有些數據庫提供對象暫時是沒有完全實現,如用Pomelo.EntityFrameworkCore.MySql -Version 1.1.2 可能會報方法未實現,更新2.0 rtm版本即可。

祝願你在碼農道路上越走越順暢;

一起學ASP.NET Core 2.0學習筆記(二): ef core2.0 及mysql provider 、Fluent API相關配置及遷移