賞金將在 1 小時后到期。此問題的答案有資格獲得 50聲望賞金。 marco Birchler想引起更多人對這個問題的關注:
我正在為我的問題尋找特定的技術解決方案。具有深厚物體框架知識的專業人士應該能夠給我一個解決它的想法,或者應該能夠解釋為什么這根本不可能。
我們確實有一個定義如下的物體類:
[Table("Users", Schema = "Mstr")]
[Audited]
public class User
{
public virtual string FamilyName { get; set; }
public virtual string SurName { get; set; }
[NotMapped]
public virtual string DisplayName
{
get => SurName " " FamilyName;
private set { }
}
}
這作業得很好。現在我們想將邏輯部分提取SurName " " FamilyName到一個通常通過依賴注入注入的輔助類中。不幸的是,DI 不適用于物體類。
因此我的問題是:有沒有辦法攔截新用戶物件的創建?是否有來自 EF 的方法,我可以在 EF 創建 User 物件后重寫以執行一些額外的邏輯?
uj5u.com熱心網友回復:
實際上(至少在 EF Core 6 中)您可以在構造物體時使用 DI。解決方案有點老套,它基于 EF Core 功能,可以將背景關系本身等“本機”服務注入物體建構式:
目前,只能注入 EF Core 已知的服務。正在考慮在未來的版本中支持注入應用程式服務。
以及AccessorExtensions.GetService<TService>似乎支持從 DI 決議服務的擴展方法。
所以基本上只是引入接受你DbContext作為物體引數的ctor并呼叫GetService它并使用服務:
public class MyEntity
{
public MyEntity()
{
}
public MyEntity(SomeContext context)
{
var valueProvider = context.GetService<IValueProvider>();
NotMapped = valueProvider.GetValue();
}
public int Id { get; set; }
[NotMapped]
public string NotMapped { get; set; }
}
// Example value provider:
public interface IValueProvider
{
string GetValue();
}
class ValueProvider : IValueProvider
{
public string GetValue() => "From DI";
}
示例背景關系:
public class SomeContext : DbContext
{
public SomeContext(DbContextOptions<SomeContext> options) : base(options)
{
}
public DbSet<MyEntity> Entities { get; set; }
}
和例子:
var serviceCollection = new ServiceCollection();
serviceCollection.AddTransient<IValueProvider, ValueProvider>();
serviceCollection.AddDbContext<SomeContext>(builder =>
builder.UseSqlite($"Filename={nameof(SomeContext)}.db"));
var serviceProvider = serviceCollection.BuildServiceProvider();
// init db and add one item
using (var scope = serviceProvider.CreateScope())
{
var someContext = scope.ServiceProvider.GetRequiredService<SomeContext>();
someContext.Database.EnsureDeleted();
someContext.Database.EnsureCreated();
someContext.Add(new MyEntity());
someContext.SaveChanges();
}
// check that value provider is used
using (var scope = serviceProvider.CreateScope())
{
var someContext = scope.ServiceProvider.GetRequiredService<SomeContext>();
var myEntities = someContext.Entities.ToList();
Console.WriteLine(myEntities.First().NotMapped); // prints "From DI"
}
請注意,var valueProvider = context.GetService<IValueProvider>();如果服務未注冊,則會拋出,因此下一個實作可能會更好:
public MyEntity(SomeContext context)
{
var serviceProvider = context.GetService<IServiceProvider>();
var valueProvider = serviceProvider.GetService<IValueProvider>();
NotMapped = valueProvider?.GetValue() ?? "No Provider";
}
您也可以考慮洗掉未映射的屬性并使用它和將執行映射的服務創建單獨的模型。
同樣在 EF Core 的第 7 版中,應該添加一個針對這種情況的新鉤子。請參閱此github問題。
uj5u.com熱心網友回復:
在您的 DbContext 或任何您的背景關系檔案中,您可以攔截 SaveChanges() 方法并用您自己的東西覆寫它。在我的示例中,我重寫了 SaveChanges() 以自動添加我的審核欄位,因此我不必在一百萬個位置的代碼中重復它。
這是我的例子。因此,當創建一個新物件時,您可以覆寫它。在我的示例中,我覆寫了添加的新記錄和修改的記錄。
這些在 EntitState.Added 和 EntityStateModified 中注明。
這是代碼。
public override int SaveChanges()
{
var state = this.ChangeTracker.Entries().Select(x => x.State).ToList();
state.ForEach(x => {
if (x == EntityState.Added)
{
//Create new record changes
var created = this.ChangeTracker.Entries().Where(e => e.State == EntityState.Added).Select(e => e.Entity).ToArray();
foreach (var entity in created)
{
if (entity is AuditFields)
{
var auditFields = entity as AuditFields;
auditFields.CreateDateTimeUtc = DateTime.UtcNow;
auditFields.ModifiedDateTimeUtc = DateTime.UtcNow;
auditFields.Active = true;
}
}
}
else if (x == EntityState.Modified)
{
//Modified record changes
var modified = this.ChangeTracker.Entries().Where(e => e.State == EntityState.Modified).Select(e => e.Entity).ToArray();
foreach (var entity in modified)
{
if (entity is AuditFields)
{
var auditFields = entity as AuditFields;
auditFields.ModifiedDateTimeUtc = DateTime.UtcNow;
}
}
}
else
{
//do nothing
}
});
return base.SaveChanges();
}
既然你說:
有沒有辦法攔截新用戶物件的創建?
您可能希望在上面的 EntityState.Added 代碼區域中執行您的邏輯,這將允許您攔截新用戶的創建,并在將其保存到資料庫之前執行您想做的任何事情。
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/429562.html
