首页 > 解决方案 > EF Core 5 DTO 映射规则和常规 CRUD 操作

问题描述

在一个大型项目中,我目前正在实现我的基础存储库和服务层。我不确定我目前正在做的事情是否矫枉过正(或者也许有更好/更简单的方法来做到这一点)。

这是我的情况:有些属性是用户永远无法更改的。因此,对于那些类型的属性,我相应地将映射规则设置为我的 DTO。但是,我不想将这些规则硬核到存储库中,因为这会使我在存储库层重复相同的规则,而且系统服务应该能够更改锁定的属性。

所以为了解决这个问题,我在上下文中禁用了延迟加载和代理创建,并在 Post-Sharp 中实现了一个轻量级的更改跟踪器。这是一个模型的例子。

[Tracking]
public class Currency: ModelBase
{
    [Required]
    [StringLength(20)]
    public string Name { get; set; }
    [Required]
    [StringLength(10)]
    public string Symbol { get; set; }
    [Required]
    [StringLength(10)]
    public string Code { get; set; }
    public string Comment { get; set; }
    public string PrimaryAccount { get; set; }
    public string SecondaryAccount { get; set; }
}

这是跟踪方面的实现。

[AttributeUsage(AttributeTargets.Class)]
[MulticastAttributeUsage(MulticastTargets.Property, TargetMemberAttributes = MulticastAttributes.Instance)]
[PSerializable]
public class TrackingAttribute : LocationInterceptionAspect
{     
    public override void OnSetValue(LocationInterceptionArgs args)
    {
        if(args.Instance is ITracking)
        {
            var model = (ITracking)args.Instance;
            model.ChangedProperties.Add(args.Location.Name);
            base.OnSetValue(args);
        }
        else
        {
            throw new IncorrectAttributeUsage("TracingAttribute can only be used on classes implementing ITracking interface.");
        }
    }
}

ModelBase类似的东西在哪里

[Tracking]
public class ModelBase : ITracking
{
    [NotMapped]
    public HashSet<string> ChangedProperties { get; } = new HashSet<string>();

    public int Id { get; set; }

    [Required]
    public DateTime CreateDate { get; set; } = DateTime.Now;
    [Required]
    public DateTime UpdateDate { get; set; } = DateTime.Now;
    [Required]
    public string CreatedBy { get; set; }
    [Required]
}

例如在我的存储库中,我有如下方法:

    public void UpdateEntity(T entity,string[] changedProperties, params Expression<Func<T, object>>[]ignoredProperties)
    {
        var entityEntry = db.Entry(entity);
        entityEntry.State = EntityState.Modified;
        var ignoredNames = new List<string>();
        ignoredNames.Add(nameof(ModelBase.Id));

        if (ignoredProperties != null && ignoredProperties.Length > 0)
        {
            foreach (var expression in ignoredProperties)
            {
                var propertyName = "";
                propertyName = expression.GetMemberInfo().Member.Name;                  
                ignoredNames.Add(propertyName);
            }
        }

        var type = typeof(T);
        var simpleProperties = type.GetProperties().Where(v => v.PropertyType.IsSimple() && !ignoredNames.Contains(v.Name));            
        var changedNames = entityEntry.Properties.Where(v => changedProperties?.Contains(v.Metadata.Name) != false && !ignoredNames.Contains(v.Metadata.Name)).Select(v=>v.Metadata.Name);

        foreach (var prop in simpleProperties)
        {
            if (changedNames.Contains(prop.Name))
            {
                entityEntry.Property(prop.Name).IsModified = true;
            }
            else
            {
                entityEntry.Property(prop.Name).IsModified = false;
            }
        }
     }

     public async Task<T> UpdateChangedAsync(T entity)
     {
        UpdateEntity(entity,entity.ChangedProperties.ToArray());
        return entity;
        // return await UpdateChangedAsync(entity, null);
     }

这是他们在服务中的用法

   public async virtual Task<TDTO> UpdateAsync(TDTO entity)
   {
        var item = Mapper.Map<TEntity>(entity);            
        await Repository.UpdateChangedAsync(item);
        await UnitOfWork.SaveChangesAsync();
        return Mapper.Map<TDTO>(item);
   }

这允许我只更新映射到项目的字段,而无需将任何逻辑或模型特定属性硬编码到存储库中。在一些小情况下,服务层中可能有几个。

我觉得可能有更好的方法来实现这一点,因为实施变更跟踪器并使用相当数量的反射感觉有点矫枉过正。然而,这是我处理过的最大的项目之一。当事情开始累加时,我真的不想重复任何逻辑不止一次它可能无法维护。

编辑:我不打算使用参考进行更新。我可能会用它们来添加对象。

标签: c#entity-framework-coreautomapperrepository-pattern

解决方案


推荐阅读