【導讀】如果我們存在基礎設施服務和其他服務,我們會定義屬于基礎設施服務的上下文以及其他服務的上下文, 而且會獨立部署,此時其他服務需要使用基礎服務,我們都會暴露基礎服務接口給到其他服務調用,這也是常規操作
若在項目較小的情況下且僅內部調用等等,為免去重新定義基礎設施服務上下文以及模型等等,我們大可以將基礎設施服務上下文打成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>))){returnExpression.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){}......
}
哦,沒啥可總結的勒,這玩意只能根據經驗猜或者看源碼可得知,再會!