EntityFrameworkCore上下文如何實現繼承?
?若在項目較小的情況下且僅內部調用等等,為免去重新定義基礎設施服務上下文以及模型等等,我們大可以將基礎設施服務上下文打成nuget包形式或項目引用方式等等,然后其他服務上下文繼承基礎設施上下文,如此這般,我們就可以操作基礎設施模型,那么我們應該怎么做呢?
實現上下文繼承
我們從頭開講,比如我們定義其他服務上下文以及模型等等
public class TestDbContext : DbContext
{
public TestDbContext(DbContextOptions<TestDbContext> options) : base(options)
{
}
public DbSet<Test> Tests { get; set; }
}
[Table("tests")]
public class Test
{
[Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
}
接下來我們使用控制臺程序注入上下文并查詢表數據,最基本操作,無需我多言
static void Main(string[] args)
{
var services = new ServiceCollection();
services.AddDbContext<TestDbContext>(options =>
{
options.UseSqlServer("Data Source=.;Initial Catalog=EFCore;User ID=sa;Password=sa123;");
});
var serviceProvider = services.BuildServiceProvider();
var context = serviceProvider.GetRequiredService<TestDbContext>();
var result = JsonConvert.SerializeObject(context.Tests.ToList());
}
此時上述服務上下文需要調用基礎服務上下文,我們該怎么辦呢?先定義好基礎服務上下文
public class BaseDbContext : DbContext
{
public BaseDbContext(DbContextOptions<BaseDbContext> options) : base(options)
{
}
public DbSet<User> Users { get; set; }
}
[Table("users")]
public class User
{
[Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
[Column("birthdate")]
public DateTime BirthDate { get; set; }
[Column("address")]
public string Address { get; set; }
}
接下來我們將其他服務上下文TestDbContext繼承自上述基礎服務上下文
public class TestDbContext : BaseDbContext
{
public TestDbContext(DbContextOptions<TestDbContext> options) : base(options)
{
}
......
}
此時編譯會報CS1503錯誤,無法將TestDbContext轉換為BaseContext,因為構造函數參數不匹配,我們知道DbContextOptions是DbContextOptions<T>父類,所以我們只需在BaseDbContext新增一個構造函數即可
public class BaseDbContext : DbContext
{
public BaseDbContext(DbContextOptions<BaseDbContext> options) : base(options)
{
}
public BaseDbContext(DbContextOptions options) : base(options)
{
}
......
}
這樣一來,我們則可以操作基礎服務上下文中的模型,如下
var context = serviceProvider.GetRequiredService<TestDbContext>();
var result = JsonConvert.SerializeObject(context.Users.ToList());
我們到這里是不是就大功告成了呢?當然沒有,若此時通過基礎服務上下文直接操作,我們發現會拋出如下異常
啥意思呢?根據大致意思來看,就是說上下文構造函數有問題,所以無法激活創建上下文,那么根本原因在哪里呢?這個問題其實在此前博文有講解 ,甩出源碼如下:
private static Func<TContext> CreateActivator(DbContextOptions options)
{
var constructors
= typeof(TContext).GetTypeInfo().DeclaredConstructors
.Where(c => !c.IsStatic && c.IsPublic)
.ToArray();
if (constructors.Length == 1)
{
var parameters = constructors[0].GetParameters();
if (parameters.Length == 1
&& (parameters[0].ParameterType == typeof(DbContextOptions)
|| parameters[0].ParameterType == typeof(DbContextOptions<TContext>)))
{
return
Expression.Lambda<Func<TContext>>(
Expression.New(constructors[0], Expression.Constant(options)))
.Compile();
}
}
return null;
}
首先獲取上下文中聲明的構造函數過濾掉了靜態和公共,且上下文必須有且只能有一個顯式構造函數且參數只能為DbContextOptions<T>,我們恍然大悟,將新增的構造函數訪問修飾符修改為受保護的(protected)即可
public class BaseDbContext : DbContext
{
public BaseDbContext(DbContextOptions<BaseDbContext> options) : base(options)
{
}
protected BaseDbContext(DbContextOptions options) : base(options)
{
}
......
}
哦,沒啥可總結的勒,這玩意只能根據經驗猜或者看源碼可得知,再會!?