我正在实现一个数据库审计跟踪,通过我的 Web API 项目中的 Controller 执行的 CRUD 操作将序列化旧的和新的 poco 并存储它们的值以供以后检索(历史、回滚等...) .
当我全部工作时,我不喜欢它让我的 Controller 在 POST 期间看起来如何,因为我最终不得不调用 SaveChanges()
两次,一次是为了获取插入的 ID实体,然后再次提交需要知道该 ID 的审计记录。
我着手将项目(仍处于起步阶段)转换为使用序列而不是标识列。这有进一步从 SQL Server 中抽象出来的额外好处,虽然这不是真正的问题,但它也允许我减少提交次数并让我将该逻辑从 Controller 中提取出来并将其填充到我的服务层中从存储库中抽象出我的 Controller ,并让我在这个“垫片”层中进行类似审计的工作。
创建Sequence
对象和公开它的存储过程后,我创建了以下类:
public class SequentialIdProvider : ISequentialIdProvider
{
private readonly IService<SequenceValue> _sequenceValueService;
public SequentialIdProvider(IService<SequenceValue> sequenceValueService)
{
_sequenceValueService = sequenceValueService;
}
public int GetNextId()
{
var value = _sequenceValueService.SelectQuery("GetSequenceIds @numberOfIds", new SqlParameter("numberOfIds", SqlDbType.Int) { Value = 1 }).ToList();
if (value.First() == null)
{
throw new Exception("Unable to retrieve the next id's from the sequence.");
}
return value.First().FirstValue;
}
public IList<int> GetNextIds(int numberOfIds)
{
var values = _sequenceValueService.SelectQuery("GetSequenceIds @numberOfIds", new SqlParameter("numberOfIds", SqlDbType.Int) { Value = numberOfIds }).ToList();
if (values.First() == null)
{
throw new Exception("Unable to retrieve the next id's from the sequence.");
}
var list = new List<int>();
for (var i = values.First().FirstValue; i <= values.First().LastValue; i++)
{
list.Add(i);
}
return list;
}
}
它简单地提供了两种获取 ID 的方法,一个是单个 ID,一个是一个范围。
这一切在第一组单元测试中运行良好,但当我开始在真实场景中进行测试时,我很快意识到调用 GetNextId()
会返回相同的结果该上下文生命周期的值(value),直到 SaveChanges()
被调用,从而否定任何真正的好处。
我不确定是否有办法解决这个问题,即创建第二个上下文(不是一个选项)或使用老式的 ADO.NET 并进行直接 SQL 调用并使用 AutoMapper 获得相同的最终结果。这些都对我没有吸引力,所以我希望其他人有想法。
最佳答案
不知道这是否对您有帮助,但这是我首先使用代码进行审计日志跟踪的方式。 以下编码为继承自 DbContext 的类。
在我的构造函数中我有以下内容
IObjectContextAdapter objectContextAdapter = (this as IObjectContextAdapter);
objectContextAdapter.ObjectContext.SavingChanges += SavingChanges;
这是我之前连接的保存更改方法
void SavingChanges(object sender, EventArgs e) {
Debug.Assert(sender != null, "Sender can't be null");
Debug.Assert(sender is ObjectContext, "Sender not instance of ObjectContext");
ObjectContext context = (sender as ObjectContext);
IEnumerable<ObjectStateEntry> modifiedEntities = context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
IEnumerable<ObjectStateEntry> addedEntities = context.ObjectStateManager.GetObjectStateEntries(EntityState.Added);
addedEntities.ToList().ForEach(a => {
//Assign ids to objects that don't have
if (a.Entity is IIdentity && (a.Entity as IIdentity).Id == Guid.Empty)
(a.Entity as IIdentity).Id = Guid.NewGuid();
this.Set<AuditLogEntry>().Add(AuditLogEntryFactory(a, _AddedEntry));
});
modifiedEntities.ToList().ForEach(m => {
this.Set<AuditLogEntry>().Add(AuditLogEntryFactory(m, _ModifiedEntry));
});
}
这些是以前用来建立审计日志细节的方法
private AuditLogEntry AuditLogEntryFactory(ObjectStateEntry entry, string entryType) {
AuditLogEntry auditLogEntry = new AuditLogEntry() {
EntryDate = DateTime.Now,
EntryType = entryType,
Id = Guid.NewGuid(),
NewValues = AuditLogEntryNewValues(entry),
Table = entry.EntitySet.Name,
UserId = _UserId
};
if (entryType == _ModifiedEntry) auditLogEntry.OriginalValues = AuditLogEntryOriginalValues(entry);
return auditLogEntry;
}
/// <summary>
/// Creates a string of all modified properties for an entity.
/// </summary>
private string AuditLogEntryOriginalValues(ObjectStateEntry entry) {
StringBuilder stringBuilder = new StringBuilder();
entry.GetModifiedProperties().ToList().ForEach(m => {
stringBuilder.Append(String.Format("{0} = {1},", m, entry.OriginalValues[m]));
});
return stringBuilder.ToString();
}
/// <summary>
/// Creates a string of all modified properties' new values for an entity.
/// </summary>
private string AuditLogEntryNewValues(ObjectStateEntry entry) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < entry.CurrentValues.FieldCount; i++) {
stringBuilder.Append(String.Format("{0} = {1},",
entry.CurrentValues.GetName(i), entry.CurrentValues.GetValue(i)));
}
return stringBuilder.ToString();
}
希望这可以为您指明方向,帮助您解决问题。
关于c# - Entity Framework 代码优先和 SQL Server 2012 序列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24127721/