将属性值存储到另一个表中

时间:2018-04-18 05:25:43

标签: c# aspnetboilerplate

我正在寻找一种方法将类的某些属性的值复制到DB中的另一个表。有没有什么特别的方法可以将类的属性标记为需要将它们的值存储在2个位置(一个作为自己的域对象的一部分而另一个作为另一个域对象的一部分,比如摘要)?

我有一个经过审核的实体,如下所示:

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]),以便在插入或更新时将属性值复制到Audited表中

我已将[已审核](Abp.Auditing)添加到PlanName并在日志中收到以下错误:

  

ERROR 2018-04-20 10:02:09,286 [5] Mvc.ExceptionHandling.AbpExceptionFilter - 未将对象引用设置为对象的实例。   System.NullReferenceException:未将对象引用设置为对象的实例。      at Abp.EntityHistory.EntityHistoryHelper.ShouldSavePropertyHistory(PropertyEntry propertyEntry,Boolean defaultValue)      在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()   ---从抛出异常的先前位置开始的堆栈跟踪结束---      在System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)      在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)      at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.d__20.MoveNext()   ---从抛出异常的先前位置开始的堆栈跟踪结束---      在System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)      在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)      在Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.d__12.MoveNext()   ---从抛出异常的先前位置开始的堆栈跟踪结束---      在System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)      在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)      at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.d__14.MoveNext()   ---从抛出异常的先前位置开始的堆栈跟踪结束---      在System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)      在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)      在Abp.Domain.Uow.UnitOfWorkBase.d__57.MoveNext()   ---从抛出异常的先前位置开始的堆栈跟踪结束---      在System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)      在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)      at Abp.AspNetCore.Mvc.Uow.AbpUowActionFilter.d__4.MoveNext()   ---从抛出异常的先前位置开始的堆栈跟踪结束---      在System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)      在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)      在Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__10.MoveNext()   ---从抛出异常的先前位置开始的堆栈跟踪结束---      在Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context)      在Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next,Scope& scope,Object& state,Boolean& isCompleted)      在Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__14.MoveNext()   ---从抛出异常的先前位置开始的堆栈跟踪结束---      在System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)      在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)      在Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__23.MoveNext()

1 个答案:

答案 0 :(得分:1)

您可以利用ABP的Entity History功能(在没有此自定义的情况下已经可以使用)。

此答案假设AuditedEntity,可以轻松使用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表:

Auditeds