EF Core 2.1隨.NET Core 2.1一起發布,本篇文章總結一下EF Core的新增功能,先從簡單的開始說。
一、延遲加載
延遲加載不用介紹了吧,直接看一下怎樣配置吧。EF Core 2.1默認是不允許延遲加載的,想要使用這個特性必須調用UseLazyLoadingProxies方法,這個擴展方法在 Microsoft.EntityFrameworkCore.Proxies 包中。
在Startup中配置:
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<TestContext>(options => { options.UseLazyLoadingProxies().UseSqlServer("yourConnectionString"); }); }
也可在DbContext中重寫OnConfiguring方法中配置:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseLazyLoadingProxies().UseSqlServer("yourConnectionString"); }
配置好后在實體類中對應的屬性上加上 virtual 關鍵字就OK,用法和EF6沒區別。除了加 virtual 標記外,EF Core 2.1還可以通過 ILazyLoader 類型的對象來實現延遲加載,實現如下:
public class Person { public Person() { } public Person(ILazyLoader lazyLoader) { LazyLoader = lazyLoader; } public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } private ILazyLoader LazyLoader { get; set; } private ICollection<Child> _children; public virtual ICollection<Child> Children { get => LazyLoader?.Load(this, ref _children); set => _children = value; } }
ILazyLoader在 Microsoft.EntityFrameworkCore.Abstractions 程序集中。按照官方文檔所述,除了注入 ILazyLoader 類型使用LazyLoader外還可以注入Action<object,string>類型來實現LazyLoader,但我嘗試的時候報錯,如果有嘗試成功的希望能在留言區里留言,多謝。
二、支持GroupBy
這個特性就不多說了,使用方法和EF6沒區別。
三、支持TransactionScope
這個特性也和EF6一樣,終於可以隨時隨地使用事務了。
四、Data Seeding
這個功能是用來初始化數據的,在進行數據庫遷移時,EF會往數據庫中插入一些數據,實現如下:
public class PersonConfig : IEntityTypeConfiguration<Person> { public void Configure(EntityTypeBuilder<Person> builder) { builder.HasData(new Person[] { new Person { Id = 1, Name ="張三", Age = 30 } }); } }
使用EntityTypeBuilder下的HasData就可以實現該功能。但令人感到奇怪的是,就算Id是自增長的EF也會要求Id有值!而且也不支持同時插入子表的數據。
public class PersonConfig : IEntityTypeConfiguration<Person> { public void Configure(EntityTypeBuilder<Person> builder) {
builder.HasData(new Person[] { new Person { Id = 1, Name ="張三", Age = 30, Children = new List<Child> { new Child { Id = 1, Name = "小張三", Age = 5, PersonId = 1 }, new Child { Id = 2, Name = "小小張三", Age = 1, PersonId = 1 } } } }); }
}
上面的代碼,在進行數據遷移時,Children的數據不會插入到數據庫中,不知道以后是否會支持關聯屬性的數據導入。
五、值轉換
這個功能簡單來說就是將屬性的類型轉換成數據庫中的類型(比如枚舉轉換成字符串),實例中可以這樣寫:
public class Person { public Person() { } public Person(ILazyLoader lazyLoader) { LazyLoader = lazyLoader; } public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } public Gender Gender { get; set; } private ILazyLoader LazyLoader { get; set; } private ICollection<Child> _children; public virtual ICollection<Child> Children { get => LazyLoader?.Load(this, ref _children); set => _children = value; } } public enum Gender { 男, 女 }
然后再配置一下:
public class PersonConfig : IEntityTypeConfiguration<Person> { public void Configure(EntityTypeBuilder<Person> builder) { builder.Property(p => p.Gender).HasConversion(v => v.ToString(), v => (Gender)Enum.Parse(typeof(Gender), v)).IsRequired().HasMaxLength(2); } }
這里有個問題比較奇怪,我在配置類中加上這段配置並在HasData中給Gender賦值,使用Migration的時候發現如果不加 IsRequired 他居然不給我把Gender的數據給導入到數據庫里面!!!
除了基本的枚舉轉字符串以外,EF Core還提供如下的轉換類:
BoolToZeroOneConverter | 將布爾值轉換為0或1 |
BoolToStringConverter | 將布爾值轉換為字符串(Y或N) |
BoolToTwoValuesConverter | 將布爾值轉換為指定的兩個值(沒搞明白干嘛用的) |
BytesToStringConverter | 將字節數組轉換為Base64編碼的字符串 |
CastingConverter | 從一種類型轉換到另一種類型(可以被C#互相轉換的類型) |
CharToStringConverter | char轉為string |
DateTimeOffsetToBinaryConverter | DateTimeOffset轉為二進制的64位的值 |
DateTimeOffsetToBytesConverter | DateTimeOffset轉為字節數組 |
DateTimeOffsetToStringConverter | DateTimeOffset轉為字符串 |
DateTimeToBinaryConverter | DateTime轉為帶有DateTimeKind的64位的值 |
DateTimeToStringConverter | DateTime轉為字符串 |
DateTimeToTicksConverter | DateTime轉為ticks |
EnumToNumberConverter | 枚舉轉數字 |
EnumToStringConverter | 枚舉轉字符串 |
GuidToBytesConverter | Guid轉字節數組 |
GuidToStringConverter | Guid轉字符串 |
NumberToBytesConverter | 數字轉字節數組 |
NumberToStringConverter | 數字轉字符串 |
StringToBytesConverter | 字符串轉字節數組 |
TimeSpanToStringConverter | TimeSpan轉字符串 |
TimeSpanToTicksConverter | TimeSpan轉ticks |
上面的這些對象的使用方式如下:
var converter = new EnumToStringConverter<Person>(); builder.Property(p => p.Gender).HasConversion(converter);
除了這種方式外,EF Core也支持直接指定類型,如:
builder.Property(p => p.Gender).HasConversion(string);
需要注意的是,不能將null進行轉換,一個屬性只能對應一個列做轉換。
六、Query Types
這個功能用來查詢數據庫視圖的。先創建個實體類:
public class Family { public int ParentId { get; set; } public string ParentName { get; set; } public int ParentAge { get; set; } public int ChildId { get; set; } public string ChildName { get; set; } public int ChildAge { get; set; } }
根據實體類在數據庫中創建視圖就行(貌似Migration不支持創建視圖),SQL我就不寫了。然后創建個繼承自 IQueryTypeConfiguration<> 的配置類,代碼如下:
public class FamilyConfig : IQueryTypeConfiguration<Family> { public void Configure(QueryTypeBuilder<Family> builder) { builder.ToView("Family_View"); } }
DbContext中使用 DbQuery<Family> 類型增加一個屬性,運行下看看結果:
根據官方文檔所述,該功能只能查詢,不能增刪改,不具有狀態跟蹤,不能包含具體查詢類型的導航屬性(既然不能有導航屬性為啥這里有個lazyLoader...)。
本文總結的都是我認為有用的功能,EF Core 2.1新增的其他功能可以到移步至官方文檔:https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-2.1
最后補充一個擴展方法,功能和EF6的AddFromAssembly方法相同。
private static bool IsIEntityTypeConfigurationType(Type typeIntf) { return typeIntf.IsInterface && typeIntf.IsGenericType && typeIntf.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>); } public static void ApplyConfigurationsFromAssembly(this ModelBuilder modelBuilder, Assembly assembly) { //篩選出繼承自IEntityTypeConfiguration的類型 IEnumerable<Type> types = assembly.GetTypes().Where(t => !t.IsAbstract && t.GetInterfaces().Any(it => IsIEntityTypeConfigurationType(it))); Type typeModelBuilder = modelBuilder.GetType(); MethodInfo methodNonGenericApplyConfiguration = typeModelBuilder.GetMethods() .Where(m => m.IsGenericMethod && m.Name == nameof(ModelBuilder.ApplyConfiguration) && m.GetParameters().Any(s => s.ParameterType.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>))).First(); foreach (var type in types) { object entityTypeConfig = Activator.CreateInstance(type); //獲取實體的類型 Type typeEntity = type.GetInterfaces().First(t => IsIEntityTypeConfigurationType(t)).GenericTypeArguments[0]; //通過MakeGenericMethod轉換為泛型方法 MethodInfo methodApplyConfiguration = methodNonGenericApplyConfiguration.MakeGenericMethod(typeEntity); methodApplyConfiguration.Invoke(modelBuilder, new[] { entityTypeConfig }); } }
以上代碼根據RuPeng.EFCore.Ext組件修改,該組件暫時不支持EF Core 2.1,GitHub已提交pr,不知道楊老師啥時候更新下。