wxin 发表于 2017-12-13 10:31:06

一起学ASP.NET Core 2.0学习笔记(二): ef core2.0 及mysql provider 、Fluent API相关配置及迁移

  不得不说微软的技术迭代还是很快的,上了微软的船就得跟着她走下去,前文一起学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)类

  

public partial>
   {  
         private ICollection<LocaleStringResource> _localeStringResources;
  

  
         /// <summary>
  
         /// Gets or sets the name
  
         /// </summary>
  

  
         public string Name { get; set; }
  

  
         /// <summary>
  
         /// Gets or sets the language culture
  
         /// </summary>
  

  
         public string LanguageCulture { get; set; }
  

  
         /// <summary>
  
         /// Gets or sets the unique SEO code
  
         /// </summary>
  

  
         public string UniqueSeoCode { get; set; }
  

  
         /// <summary>
  
         /// Gets or sets the flag image file name
  
         /// </summary>
  

  
         public string FlagImageFileName { get; set; }
  

  
         /// <summary>
  
         /// Gets or sets a value indicating whether the language supports "Right-to-left"
  
         /// </summary>
  

  
         public bool Rtl { get; set; }
  

  

  
         /// <summary>
  
         /// Gets or sets a value indicating whether the language is published
  
         /// </summary>
  

  
         public bool Published { get; set; }
  

  
         /// <summary>
  
         /// Gets or sets the display order
  
         /// </summary>
  

  
         public int DisplayOrder { get; set; }
  

  
         public bool IsDefault { get; set; }
  

  
         /// <summary>
  
         /// Gets or sets locale string resources
  
         /// </summary>
  
         public virtual ICollection<LocaleStringResource> LocaleStringResources
  
         {
  
             get { return _localeStringResources ?? (_localeStringResources = new HashSet<LocaleStringResource>()); }
  
             protected set { _localeStringResources = value; }
  
         }
  


View Code  语言资源(LocaleStringResource):

  

public partial>
   {  
         /// <summary>
  
         /// Gets or sets the language>  
         /// </summary>
  
         public int LanguageId { get; set; }
  

  
         /// <summary>
  
         /// Gets or sets the resource name
  
         /// </summary>
  
         public string ResourceName { get; set; }
  

  
         /// <summary>
  
         /// Gets or sets the resource value
  
         /// </summary>
  
         public string ResourceValue { get; set; }
  

  
         /// <summary>
  
         /// Gets or sets a value indicating whether this resource was installed by a plugin
  
         /// </summary>
  
         public bool? IsFromPlugin { get; set; }
  

  
         /// <summary>
  
         /// Gets or sets a value indicating whether this resource was modified by the user
  
         /// </summary>
  
         public bool? IsTouched { get; set; }
  

  
         /// <summary>
  
         /// Gets or sets the language
  
         /// </summary>
  
         public virtual Language Language { get; set; }
  

  
   }
  


View Code  其中语言及资源主外键关系配置如下


  

public partial>
   {  
         public void Configure(EntityTypeBuilder<Language> builder)
  
         {
  
             builder.ToTable("Language");
  
             builder.HasKey(r => r.Id);
  
             builder.HasIndex(r => r.LanguageCulture);
  
             builder.HasIndex(r => r.Name);
  
             builder.HasIndex(r => r.UniqueSeoCode);
  
         }
  
   }
  


View Code  

public partial>{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、父子关系 :

  

public>
   {  
         /// <summary>
  
         /// 名称
  
         /// </summary>
  
         public string Name { get; set; }
  
         /// <summary>
  
         /// 分类描述
  
         /// </summary>
  
         public string Descript { get; set; }
  
         /// <summary>
  
         /// meta标题
  
         /// </summary>
  
         public string MetaTitle { get; set; }
  
         /// <summary>
  
         /// meta描述
  
         /// </summary>
  
         public string MetaDescript { get; set; }
  
         /// <summary>
  
         /// 缩略图
  
         /// </summary>
  
         public string Thumb { get; set; }
  
         /// <summary>
  
         /// 图片
  
         /// </summary>
  
         public string Image { get; set; }
  
         /// <summary>
  
         /// 排序
  
         /// </summary>
  
         public int Sort { get; set; }
  
         /// <summary>
  
         /// 父级分类
  
         /// </summary>
  
         public int? ParentId { get; set; }
  
         
  
         /// <summary>
  
         /// 父级分类
  
         /// </summary>
  

  
         public virtual TechCategory Parent { get; set; }
  
         public virtual ICollection<Technology> Technologys { get; set; }
  
         /// <summary>
  
         /// 子级
  
         /// </summary>
  
         public virtual ICollection<TechCategory> Childs { get; set; }
  
         /// <summary>
  
         /// 关键词
  
         /// </summary>
  
         public string Slug { get; set; }
  
         /// <summary>
  
         /// 创建时间
  
         /// </summary>
  
         public long CreatedOn { get; set; }
  
   }
  


View Code
  

public>
   {  
         public void Configure(EntityTypeBuilder<TechCategory> builder)
  
         {
  
             builder.ToTable("TechCategory");
  
             builder.HasKey(r => r.Id);
  
             builder.HasMany(r => r.Childs).WithOne(x => x.Parent)
  
            .HasForeignKey(fk => fk.ParentId);
  
             builder.HasIndex(r => r.Name);
  
         }
  
   }
  


View Code  二、数据库上下文:
  根据前面创建的相关实体类 以及相关mapper类型,我们可以重现DbContext的OnModelCreating的方法,反射出实现了IEntityTypeConfiguration接口的相关mapper类型创建相关实体-数据表的相关对应关系

  

public>
   {  

         public DefaultContext(DbContextOptions<DefaultContext> opt)  

             :base(opt)  

         {  

             AutoCommitEnabled = true;  

         }  

   protected override void OnModelCreating(ModelBuilder builder)  

         {  

             base.OnModelCreating(builder);  

             var mappingInterface = typeof(IEntityTypeConfiguration<>);  

             // Types that do entity mapping  
             var mappingTypes = GetType().GetTypeInfo().Assembly.GetTypes()
  
               .Where(x => x.GetInterfaces().Any(y => y.GetTypeInfo().IsGenericType && y.GetGenericTypeDefinition() == mappingInterface));
  

  
             // Get the generic Entity method of the ModelBuilder type
  
             var entityMethod = typeof(ModelBuilder).GetMethods()
  
               .Single(x => x.Name == "Entity" &&
  
                         x.IsGenericMethod &&
  
                         x.ReturnType.Name == "EntityTypeBuilder`1");
  

  
             foreach (var mappingType in mappingTypes)
  
             {
  

  
               // Get the type of entity to be mapped
  
               var genericTypeArg = mappingType.GetInterfaces().Single().GenericTypeArguments.Single();
  

  
               // Get the method builder.Entity<TEntity>
  
               var genericEntityMethod = entityMethod.MakeGenericMethod(genericTypeArg);
  

  
               // Invoke builder.Entity<TEntity> to get a builder for the entity to be mapped
  
               var entityBuilder = genericEntityMethod.Invoke(builder, null);
  

  
               // Create the mapping type and do the mapping
  
               var mapper = Activator.CreateInstance(mappingType);
  
               mapper.GetType().GetMethod("Configure").Invoke(mapper, new[] { entityBuilder });
  
             }
  
             foreach (var>  
             {
  
                >  
             }
  
         }
  
   }
  


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引用列表


  

<ItemGroup>  
   <PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.0" />
  
   <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.0.0" />
  
   <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.0" />
  
   <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.0.0-rtm-10058" />
  
   </ItemGroup>
  


View Code  相关配置文件修改
  

"ConnectionStrings": {"MongoDbConnectionString": "mongodb://tchistory:tc123456@127.0.0.1/history","ConnectionString": "Data Source=127.0.0.1;Initial Catalog=farmdata;User>
"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版本即可。
  祝愿你在码农道路上越走越顺畅;
页: [1]
查看完整版本: 一起学ASP.NET Core 2.0学习笔记(二): ef core2.0 及mysql provider 、Fluent API相关配置及迁移