EF Core 2.1 新特性


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,不知道楊老師啥時候更新下。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM