将 属性 值存储到另一个 table
Storing a property value into another table
我正在寻找一种方法将 class 的某些属性的值复制到数据库中的另一个 table。有没有什么特别的方法可以将 class 的属性标记为它们的值需要存储在两个地方(一个作为其自己的域对象的一部分,另一个在另一个域对象中,比如摘要)?
我有一个经过审计的实体如下:
public class Audited
{
public virtual int Id{ get; set; }
public virtual string FieldName{ get; set; }
public virtual string FieldValue{ get; set; }
}
以及其他实体,例如:
public class Plan : FullAuditedEntity
{
[ToBeAudited]
public virtual int PlanName{ get; set; }
public DateTime Date { get; set; }
}
我正在寻找一种能够使用属性(例如 [ToBeAudited])标记 classes 的属性的方法,以便将 属性 值复制到 Audited [=30] =] 在插入或更新时
我已将 [已审核] (Abp.Auditing
) 添加到 PlanName
并在日志中收到以下错误:
ERROR 2018-04-20 10:02:09,286 [5 ] Mvc.ExceptionHandling.AbpExceptionFilter - Object reference not set to an instance of an object.
System.NullReferenceException: Object reference not set to an instance of an object.
at Abp.EntityHistory.EntityHistoryHelper.ShouldSavePropertyHistory(PropertyEntry propertyEntry, Boolean defaultValue)
at Abp.EntityHistory.EntityHistoryHelper.GetPropertyChanges(EntityEntry entityEntry)
at Abp.EntityHistory.EntityHistoryHelper.CreateEntityChangeInfo(EntityEntry entityEntry)
at Abp.EntityHistory.EntityHistoryHelper.CreateEntityChangeSet(ICollection1 entityEntries)
at Abp.Zero.EntityFrameworkCore.AbpZeroCommonDbContext
3.d__98.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.d__20.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.d__12.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.d__14.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Abp.Domain.Uow.UnitOfWorkBase.d__57.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Abp.AspNetCore.Mvc.Uow.AbpUowActionFilter.d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__10.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__14.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__23.MoveNext()
您可以利用 ABP 的 Entity History 功能(无需此自定义即可使用)。
这个答案假设 Audited
是一个 Entity
以便于使用 IRepository
,但它不一定是:
public class Audited : Entity
{
public virtual int EntityId { get; set; }
public virtual string FieldName { get; set; }
public virtual string FieldValue { get; set; }
}
首先,执行IEntityHistoryStore
:
public class MyEntityHistoryStore : IEntityHistoryStore
{
private readonly IRepository<Audited> _auditedRepository;
public MyEntityHistoryStore(IRepository<Audited> auditedRepository)
{
_auditedRepository = auditedRepository;
}
public async Task SaveAsync(EntityChangeSet entityChangeSet)
{
foreach (var entityChange in entityChangeSet.EntityChanges)
{
var entityType = entityChange.EntityEntry.As<EntityEntry>().Entity.GetType();
foreach (var propertyChange in entityChange.PropertyChanges)
{
var property = entityType.GetProperty(propertyChange.PropertyName);
if (property.IsDefined(typeof(ToBeAuditedAttribute)))
{
await _auditedRepository.InsertAsync(new Audited
{
EntityId = JsonConvert.DeserializeObject<int>(entityChange.EntityId),
FieldName = propertyChange.PropertyName,
FieldValue = propertyChange.NewValue
});
}
}
}
}
}
接下来,替换服务并在你的模块的PreInitialize
方法中添加到Selectors
:
// using Abp.Configuration.Startup;
Configuration.ReplaceService<IEntityHistoryStore, MyEntityHistoryStore>();
Configuration.EntityHistory.Selectors.Add(
new NamedTypeSelector(
"ToBeAuditedEntities",
type => type.GetProperties().Any(p => p.IsDefined(typeof(ToBeAuditedAttribute)))
)
);
然后,照常Insert
:
_planRepository.Insert(new Plan
{
PlanName = 42
});
Audited
table:
我正在寻找一种方法将 class 的某些属性的值复制到数据库中的另一个 table。有没有什么特别的方法可以将 class 的属性标记为它们的值需要存储在两个地方(一个作为其自己的域对象的一部分,另一个在另一个域对象中,比如摘要)?
我有一个经过审计的实体如下:
public class Audited
{
public virtual int Id{ get; set; }
public virtual string FieldName{ get; set; }
public virtual string FieldValue{ get; set; }
}
以及其他实体,例如:
public class Plan : FullAuditedEntity
{
[ToBeAudited]
public virtual int PlanName{ get; set; }
public DateTime Date { get; set; }
}
我正在寻找一种能够使用属性(例如 [ToBeAudited])标记 classes 的属性的方法,以便将 属性 值复制到 Audited [=30] =] 在插入或更新时
我已将 [已审核] (Abp.Auditing
) 添加到 PlanName
并在日志中收到以下错误:
ERROR 2018-04-20 10:02:09,286 [5 ] Mvc.ExceptionHandling.AbpExceptionFilter - Object reference not set to an instance of an object. System.NullReferenceException: Object reference not set to an instance of an object. at Abp.EntityHistory.EntityHistoryHelper.ShouldSavePropertyHistory(PropertyEntry propertyEntry, Boolean defaultValue) at Abp.EntityHistory.EntityHistoryHelper.GetPropertyChanges(EntityEntry entityEntry) at Abp.EntityHistory.EntityHistoryHelper.CreateEntityChangeInfo(EntityEntry entityEntry) at Abp.EntityHistory.EntityHistoryHelper.CreateEntityChangeSet(ICollection
1 entityEntries) at Abp.Zero.EntityFrameworkCore.AbpZeroCommonDbContext
3.d__98.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.d__20.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.d__12.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.d__14.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Abp.Domain.Uow.UnitOfWorkBase.d__57.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Abp.AspNetCore.Mvc.Uow.AbpUowActionFilter.d__4.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__10.MoveNext() --- End of stack trace from previous location where exception was thrown --- at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__14.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__23.MoveNext()
您可以利用 ABP 的 Entity History 功能(无需此自定义即可使用)。
这个答案假设 Audited
是一个 Entity
以便于使用 IRepository
,但它不一定是:
public class Audited : Entity
{
public virtual int EntityId { get; set; }
public virtual string FieldName { get; set; }
public virtual string FieldValue { get; set; }
}
首先,执行IEntityHistoryStore
:
public class MyEntityHistoryStore : IEntityHistoryStore
{
private readonly IRepository<Audited> _auditedRepository;
public MyEntityHistoryStore(IRepository<Audited> auditedRepository)
{
_auditedRepository = auditedRepository;
}
public async Task SaveAsync(EntityChangeSet entityChangeSet)
{
foreach (var entityChange in entityChangeSet.EntityChanges)
{
var entityType = entityChange.EntityEntry.As<EntityEntry>().Entity.GetType();
foreach (var propertyChange in entityChange.PropertyChanges)
{
var property = entityType.GetProperty(propertyChange.PropertyName);
if (property.IsDefined(typeof(ToBeAuditedAttribute)))
{
await _auditedRepository.InsertAsync(new Audited
{
EntityId = JsonConvert.DeserializeObject<int>(entityChange.EntityId),
FieldName = propertyChange.PropertyName,
FieldValue = propertyChange.NewValue
});
}
}
}
}
}
接下来,替换服务并在你的模块的PreInitialize
方法中添加到Selectors
:
// using Abp.Configuration.Startup;
Configuration.ReplaceService<IEntityHistoryStore, MyEntityHistoryStore>();
Configuration.EntityHistory.Selectors.Add(
new NamedTypeSelector(
"ToBeAuditedEntities",
type => type.GetProperties().Any(p => p.IsDefined(typeof(ToBeAuditedAttribute)))
)
);
然后,照常Insert
:
_planRepository.Insert(new Plan
{
PlanName = 42
});
Audited
table: