具有自动 CRUD 日志记录的数据库优先方法
Database first approach with automatic CRUD logging
我已按如下方式设置 EF+ 审计:
public partial class FocusConnection : DbContext
{
static FocusConnection()
{
AuditManager.DefaultConfiguration.AutoSavePreAction = (context, audit) =>
// ADD "Where(x => x.AuditEntryID == 0)" to allow multiple SaveChanges with same Audit
(context as FocusConnection).AuditEntries.AddRange(audit.Entries);
}
public override int SaveChanges()
{
var audit = new Audit();
audit.PreSaveChanges(this);
var rowAffecteds = base.SaveChanges();
audit.PostSaveChanges();
if (audit.Configuration.AutoSavePreAction != null)
{
audit.Configuration.AutoSavePreAction(this, audit);
base.SaveChanges();
}
return rowAffecteds;
}
public override Task<int> SaveChangesAsync()
{
return SaveChangesAsync(CancellationToken.None);
}
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken)
{
var audit = new Audit();
audit.PreSaveChanges(this);
var rowAffecteds = await base.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
audit.PostSaveChanges();
if (audit.Configuration.AutoSavePreAction != null)
{
audit.Configuration.AutoSavePreAction(this, audit);
await base.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
}
return rowAffecteds;
}
}
我使用了部分 class 来避开 EF6 生成的文本模板原始上下文。我使用以下 SQL 脚本按原样生成两个 table:
CREATE TABLE [dbo].[AuditEntries] (
[AuditEntryID] [int] NOT NULL IDENTITY,
[EntitySetName] [nvarchar](255),
[EntityTypeName] [nvarchar](255),
[State] [int] NOT NULL,
[StateName] [nvarchar](255),
[CreatedBy] [nvarchar](255),
[CreatedDate] [datetime] NOT NULL,
CONSTRAINT [PK_dbo.AuditEntries] PRIMARY KEY ([AuditEntryID])
)
GO
CREATE TABLE [dbo].[AuditEntryProperties] (
[AuditEntryPropertyID] [int] NOT NULL IDENTITY,
[AuditEntryID] [int] NOT NULL,
[RelationName] [nvarchar](255),
[PropertyName] [nvarchar](255),
[OldValue] [nvarchar](max),
[NewValue] [nvarchar](max),
CONSTRAINT [PK_dbo.AuditEntryProperties] PRIMARY KEY ([AuditEntryPropertyID])
)
GO
CREATE INDEX [IX_AuditEntryID] ON [dbo].[AuditEntryProperties]([AuditEntryID])
GO
ALTER TABLE [dbo].[AuditEntryProperties]
ADD CONSTRAINT [FK_dbo.AuditEntryProperties_dbo.AuditEntries_AuditEntryID]
FOREIGN KEY ([AuditEntryID])
REFERENCES [dbo].[AuditEntries] ([AuditEntryID])
ON DELETE CASCADE
GO
我在静态构造函数中遇到错误:
static FocusConnection()
{
AuditManager.DefaultConfiguration.AutoSavePreAction = (context, audit) =>
// ADD "Where(x => x.AuditEntryID == 0)" to allow multiple SaveChanges with same Audit
(context as FocusConnection).AuditEntries.AddRange(audit.Entries);
}
AuditEntries.AddRange(audit.Entries)
错误:
CS1503 参数 1:无法从 System.Collections.Generic.List<Z.EntityFramework.Plus.AuditEntry>
转换为 System.Collections.Generic.IEnumerable<DA.Systems.Focus.Data.EntityFramework.FocusOrm.AuditEntry>
我是不是漏掉了什么?
您首先使用数据库。
来自 EF+ 的 AuditEntry
class 与您的上下文生成的不同。
您需要Import
值。
AuditManager.DefaultConfiguration.AutoSavePreAction = (context, audit) =>
{
// ADD "Where(x => x.AuditEntryID == 0)" to allow multiple SaveChanges with same Audit
var customAuditEntries = audit.Entries.Select(x => Import(x));
(context as Entities).AuditEntries.AddRange(customAuditEntries);
};
using (var ctx = new Entities())
{
Audit audit = new Audit();
audit.CreatedBy = "ZZZ Projects"; // Optional
ctx.Entity_Basic.Add(new Entity_Basic() {ColumnInt = 2});
ctx.SaveChanges(audit);
}
public AuditEntry Import(Z.EntityFramework.Plus.AuditEntry entry)
{
var customAuditEntry = new AuditEntry
{
EntitySetName = entry.EntitySetName,
EntityTypeName = entry.EntityTypeName,
State = (int)entry.State,
StateName = entry.StateName,
CreatedBy = entry.CreatedBy,
CreatedDate = entry.CreatedDate
};
customAuditEntry.AuditEntryProperties = entry.Properties.Select(x => Import(x)).ToList();
return customAuditEntry;
}
public AuditEntryProperty Import(Z.EntityFramework.Plus.AuditEntryProperty property)
{
var customAuditEntry = new AuditEntryProperty
{
RelationName = property.RelationName,
PropertyName = property.PropertyName,
OldValue = property.OldValueFormatted,
NewValue = property.NewValueFormatted
};
return customAuditEntry;
}
我已按如下方式设置 EF+ 审计:
public partial class FocusConnection : DbContext
{
static FocusConnection()
{
AuditManager.DefaultConfiguration.AutoSavePreAction = (context, audit) =>
// ADD "Where(x => x.AuditEntryID == 0)" to allow multiple SaveChanges with same Audit
(context as FocusConnection).AuditEntries.AddRange(audit.Entries);
}
public override int SaveChanges()
{
var audit = new Audit();
audit.PreSaveChanges(this);
var rowAffecteds = base.SaveChanges();
audit.PostSaveChanges();
if (audit.Configuration.AutoSavePreAction != null)
{
audit.Configuration.AutoSavePreAction(this, audit);
base.SaveChanges();
}
return rowAffecteds;
}
public override Task<int> SaveChangesAsync()
{
return SaveChangesAsync(CancellationToken.None);
}
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken)
{
var audit = new Audit();
audit.PreSaveChanges(this);
var rowAffecteds = await base.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
audit.PostSaveChanges();
if (audit.Configuration.AutoSavePreAction != null)
{
audit.Configuration.AutoSavePreAction(this, audit);
await base.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
}
return rowAffecteds;
}
}
我使用了部分 class 来避开 EF6 生成的文本模板原始上下文。我使用以下 SQL 脚本按原样生成两个 table:
CREATE TABLE [dbo].[AuditEntries] (
[AuditEntryID] [int] NOT NULL IDENTITY,
[EntitySetName] [nvarchar](255),
[EntityTypeName] [nvarchar](255),
[State] [int] NOT NULL,
[StateName] [nvarchar](255),
[CreatedBy] [nvarchar](255),
[CreatedDate] [datetime] NOT NULL,
CONSTRAINT [PK_dbo.AuditEntries] PRIMARY KEY ([AuditEntryID])
)
GO
CREATE TABLE [dbo].[AuditEntryProperties] (
[AuditEntryPropertyID] [int] NOT NULL IDENTITY,
[AuditEntryID] [int] NOT NULL,
[RelationName] [nvarchar](255),
[PropertyName] [nvarchar](255),
[OldValue] [nvarchar](max),
[NewValue] [nvarchar](max),
CONSTRAINT [PK_dbo.AuditEntryProperties] PRIMARY KEY ([AuditEntryPropertyID])
)
GO
CREATE INDEX [IX_AuditEntryID] ON [dbo].[AuditEntryProperties]([AuditEntryID])
GO
ALTER TABLE [dbo].[AuditEntryProperties]
ADD CONSTRAINT [FK_dbo.AuditEntryProperties_dbo.AuditEntries_AuditEntryID]
FOREIGN KEY ([AuditEntryID])
REFERENCES [dbo].[AuditEntries] ([AuditEntryID])
ON DELETE CASCADE
GO
我在静态构造函数中遇到错误:
static FocusConnection()
{
AuditManager.DefaultConfiguration.AutoSavePreAction = (context, audit) =>
// ADD "Where(x => x.AuditEntryID == 0)" to allow multiple SaveChanges with same Audit
(context as FocusConnection).AuditEntries.AddRange(audit.Entries);
}
AuditEntries.AddRange(audit.Entries)
错误:
CS1503 参数 1:无法从 System.Collections.Generic.List<Z.EntityFramework.Plus.AuditEntry>
转换为 System.Collections.Generic.IEnumerable<DA.Systems.Focus.Data.EntityFramework.FocusOrm.AuditEntry>
我是不是漏掉了什么?
您首先使用数据库。
来自 EF+ 的 AuditEntry
class 与您的上下文生成的不同。
您需要Import
值。
AuditManager.DefaultConfiguration.AutoSavePreAction = (context, audit) =>
{
// ADD "Where(x => x.AuditEntryID == 0)" to allow multiple SaveChanges with same Audit
var customAuditEntries = audit.Entries.Select(x => Import(x));
(context as Entities).AuditEntries.AddRange(customAuditEntries);
};
using (var ctx = new Entities())
{
Audit audit = new Audit();
audit.CreatedBy = "ZZZ Projects"; // Optional
ctx.Entity_Basic.Add(new Entity_Basic() {ColumnInt = 2});
ctx.SaveChanges(audit);
}
public AuditEntry Import(Z.EntityFramework.Plus.AuditEntry entry)
{
var customAuditEntry = new AuditEntry
{
EntitySetName = entry.EntitySetName,
EntityTypeName = entry.EntityTypeName,
State = (int)entry.State,
StateName = entry.StateName,
CreatedBy = entry.CreatedBy,
CreatedDate = entry.CreatedDate
};
customAuditEntry.AuditEntryProperties = entry.Properties.Select(x => Import(x)).ToList();
return customAuditEntry;
}
public AuditEntryProperty Import(Z.EntityFramework.Plus.AuditEntryProperty property)
{
var customAuditEntry = new AuditEntryProperty
{
RelationName = property.RelationName,
PropertyName = property.PropertyName,
OldValue = property.OldValueFormatted,
NewValue = property.NewValueFormatted
};
return customAuditEntry;
}