postgresql 在Entity Framework中添加或更改其下层次结构中任何级别的子实体时,如何更新父实体LastUpdate属性?

aydmsdu9  于 2023-04-20  发布在  PostgreSQL
关注(0)|答案(2)|浏览(99)

我使用代码优先的方法创建了一个简单的一对多层次结构。层次结构中的所有实体都有一个Id(即Guid)以及CreatedOn和LastUpdate的时间戳。这些都包含在BaseEntity类中。每个实体可能有其他属性,但它们对这个问题并不重要。层次结构中的每个实体都有一个指向其父实体的外键,以及子实体的集合。
例如:

public class BaseEntity
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public Guid Id { get; set; }
        public DateTime CreatedOn { get; set; }
        public DateTime LastUpdate { get; set; }
    }
public class RootDirectory : BaseEntity
    {
        public string RootName { get; set; }
        public ICollection<SubDirectory> SubDirectories { get; set; }
    }

子目录类如下:

public class Subdirectory : BaseEntity
    {
        public string SubDirectoryName { get; set; }
        public Guid? RootId { get; set; }
        public RootDirectory Root { get; set; }
        public ICollection<File> Files { get; set; }
    }

File类如下所示:

public class File : BaseEntity
    {
        public string FileName { get; set; }
        public Guid? SubDirectoryId { get; set; }
        public SubDirectory SubDirectory { get; set; }
    }

这产生了我所期望的适当的外键关系。除了LastUpdate,所有操作都在这个系统中工作得很好。
当我在BaseRepo中使用以下泛型方法时,LastUpdate会按预期进行更新:

public virtual int Update(TEntity entity, bool persist = true)
    {
        Table.Update(entity);
        return persist ? SaveChanges() : 0;
    }

例如,当我对文件执行此操作时,LastUpdate会按照预期更改为Update调用的时间。但是,当文件更新时,子目录不会额外更新,反过来,根目录也不会更新。
从本质上讲,EF中是否有一种方法可以在子实体更新时使父实体更新?由于缺乏更好的术语,反向级联更新?(级联删除在较高级别删除时删除每个实体的所有子实体,我可以在较低级别更新并更新所有父实体吗?)
请注意,这是通用的是非常重要的,因为我想在其他实体层次结构上使用这个BaseRepo,而不是这个层次结构,这些层次结构可能有不同的级别和其他类似的差异。
我认为这是可能的,因为GitHub和DevOps上的存储库在文件被更改或添加时会这样做。被修改的文件具有Last change,但它的所有父文件夹也是如此,因为它们知道其中的某些内容已更新。
如果有什么不清楚的地方,请告诉我。就像我说的,EF的其他一切都很顺利,这只是一个小问题,我找不到任何文档或帖子,因为大多数人似乎想走另一个方向,在更新父项时更新子项。

htrmnn0y

htrmnn0y1#

这并不像您希望的那样自动化,但我向上下文添加了SavingChanges事件处理程序。处理程序获取添加或更新的实体的列表,并将其传递给处理例程。您可以看到用于父实体中存在UpdatedDateTime属性的情况的特殊代码。我认为您可以通过递归查找父实体来自动化大部分内容。但我只有几个案例,所以包含这些案例的特定代码会更简单。

/// <summary>
/// SavingChanges event handler to update UpdatedDateTime and UpdatedBy properties.
/// </summary>
/// <param name="sender">The ConferenceDbContext whose changes are about to be saved.</param>
/// <param name="e">Standard SavingChanges event arg object, not used here.</param>
private void AssignUpdatedByAndTime(object? sender, SavingChangesEventArgs e)
{
    //Get added or modified entities.
    List<EntityEntry> changedEntities = ChangeTracker.Entries()
        .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified)
        .ToList();

    //Assign UpdatedDateTime and UpdatedBy properties if they exist.
    foreach (EntityEntry entityEntry in changedEntities)
    {
        //Some subcategory entities have the updated date/by attributes in the parent entity.
        EntityEntry? parentEntry = null;
        string entityTypeName = entityEntry.Metadata.Name;  //Full class name, e.g., ConferenceEF.Models.Meeting
        entityTypeName = entityTypeName.Split('.').Last();
        switch (entityTypeName)
        {
            case nameof(Paper):
            case nameof(FlashPresentation):
                parentEntry = entityEntry.Reference(nameof(SessionItem)).TargetEntry;
                break;
            default:
                break;
        }
        AssignUpdatedByUserAndTime(parentEntry ?? entityEntry);
    }
}

private void AssignUpdatedByUserAndTime(EntityEntry entityEntry)
{
    if (entityEntry.Entity is BaseEntityWithUpdatedAndRowVersion
        || entityEntry.Entity is PaperRating)
    {
        PropertyEntry updatedDateTime = entityEntry.Property("UpdatedDateTime");
        DateTimeOffset? currentValue = (DateTimeOffset?)updatedDateTime.CurrentValue;
        //Avoid possible loops by only updating time when it has changed by at least 1 minute.
        //Is this necessary?
        if (!currentValue.HasValue || currentValue < DateTimeOffset.Now.AddMinutes(-1))
            updatedDateTime.CurrentValue = DateTimeOffset.Now;

        if (entityEntry.Entity is BaseEntityWithUpdatedAndRowVersion
            || entityEntry.Properties.Any(p => p.Metadata.Name == "UpdatedBy"))
        {
            PropertyEntry updatedBy = entityEntry.Property("UpdatedBy");
            string? newValue = CurrentUserName;  //ClaimsPrincipal.Current?.Identity?.Name;
            if (newValue == null && !updatedBy.Metadata.IsColumnNullable())
                newValue = string.Empty;
            if (updatedBy.CurrentValue?.ToString() != newValue)
                updatedBy.CurrentValue = newValue;
        }
    }
}
r6hnlfcb

r6hnlfcb2#

目前似乎没有现成的解决方案来实现这一点,所以现在我在递归更新中实现,我手动查看并更新已更新或添加的实体的所有父项。如果这在路上发生了变化,请随时放置一个新的答案。

相关问题