繁体   English   中英

是否有可能检测到EF6中实体的引用属性何时发生了变化?

[英]Is it possible to detect when a reference property has changed on an entity in EF6?

这似乎是一个常见的问题,但我找不到任何对我有意义的答案。

我想使用EF6在应用程序中创建一个Audit系统。 检测简单属性中的更改不是问题,并且在保存新值之前创建具有原始值的审计条目很好。

但是,我还需要跟踪对引用属性的更改,即国家(链接到不同实体)已更改。

dbChangeTracker不会选择这些更改,即使它确实没有获得原始值。

有些人指出ObjectContext的方向是一个解决方案,但我看不出它会有什么帮助。 有帮助吗?

其他人似乎表明我应该在实体上明确地放置FK属性,即Country_Id。

这是解决方案吗?

所有帮助非常感谢。

干杯迈克

实现一种方法,用于检索有关EntityObject的某些属性的元数据信息:

private static EdmMember GetEdmMember<TEntity>(this ObjectContext context, TEntity entityContainer, string propertyName)
 {
    EdmMember edmMember = null;
    EntityType entityType = context.MetadataWorkspace.GetItem<EntityType>(entityContainer.GetType().FullName, DataSpace.CSpace);
    IEnumerable<EdmMember> edmMembers = entityType.MetadataProperties.First(p => p.Name == "Members").Value as IEnumerable<EdmMember>;
    edmMember = edmMembers.FirstOrDefault(item => item.Name == propertyName);
    if (edmMember == null)
        throw new ArgumentException(
                            string.Format("Cannot find property metadata: property '{0}' in '{1}' entity object", propertyName, entityType.Name));
    return edmMember;
}

MetadataWorkspace类是一个中央运行时API,可用于在应用程序上下文中与实体数据模型(EDM)元数据进行交互。 有关详细信息,请参阅http://msdn.microsoft.com/en-us/library/bb387116%28v=vs.110%29.aspx

接下来,实现IsPropertyChanged方法,如下所示:

public static bool IsPropertyChanged<TEntity>(this ObjectContext context, TEntity entityContainer, string propertyName)
            where TEntity : IEntityWithKey, IEntityWithRelationships
{
    bool isModified = false;
    EdmMember edmMember = GetEdmMember(context, entityContainer, propertyName);

    switch (edmMember.BuiltInTypeKind)
    {
        case BuiltInTypeKind.NavigationProperty: /*navigation property*/
                        {
                            NavigationProperty navigationProperty = edmMember as NavigationProperty;
                            IRelatedEnd sourceRelatedEnd = entityContainer.RelationshipManager.GetRelatedEnd(navigationProperty.RelationshipType.FullName,
                                                                                                             navigationProperty.ToEndMember.Name) as IRelatedEnd;
                            EntityState state = (EntityState.Added | EntityState.Deleted);
                            IEnumerable<IGrouping<IRelatedEnd, ObjectStateEntry>> relationshipGroups = GetRelationshipsByRelatedEnd(context, entityContainer, state);
                            foreach (var relationshipGroup in relationshipGroups)
                            {
                                IRelatedEnd targetRelatedEnd = (IRelatedEnd)relationshipGroup.Key;
                                if (targetRelatedEnd.IsEntityReference()
                                    && targetRelatedEnd.IsRelatedEndEqual(sourceRelatedEnd))
                                {
                                    isModified = true;
                                    break;
                                }
                            }
                        } break;

        case BuiltInTypeKind.EdmProperty: /*scalar field*/
                        {
                            ObjectStateEntry containerStateEntry = null;
                            isModified = context.IsScalarPropertyModified(propertyName, entityContainer, out containerStateEntry);
                        } break;

        default:
                        {
                            throw new InvalidOperationException("Property type not supported");
                        }
    }

    return isModified;
}

以及标量属性更改跟踪的方法:

private static bool IsScalarPropertyModified(this ObjectContext context, string scalarPropertyName, IEntityWithKey entityContainer, out ObjectStateEntry containerStateEntry)
                {
                    bool isModified = false;
                    containerStateEntry = context.ObjectStateManager.GetObjectStateEntry(entityContainer.EntityKey);
                    IEnumerable<string> modifiedProperties = containerStateEntry.GetModifiedProperties();

                    string changedProperty = modifiedProperties.FirstOrDefault(element => (element == scalarPropertyName));
                    isModified = (null != changedProperty);

                    if (isModified) 
                    {
                        object originalValue = containerStateEntry.OriginalValues[changedProperty];
                        object currentValue = containerStateEntry.CurrentValues[changedProperty];
                        //sometimes property can be treated as changed even though you set the same value it had before
                        isModified = !object.Equals(originalValue, currentValue);
                    }

                    return isModified;
                }

跟踪标量属性很简单,对。 如果您的实体中包含外键,您可以随时检查这些外键是否已更改(确保对象中的FK字段与导航属性保持同步,这通常是一个问题)。 上面代码中的导航属性案例应该让您了解实现,在大多数情况下它应该可以工作。

PS。 如果您正在实施审计系统,那么您可能希望了解一下EF 6.1中引入的拦截器(只是一个想法) http://msdn.microsoft.com/en-US/data/jj556606#Interceptors

我建议在保存更改时跟踪FK属性的更改就足够了。 毕竟,如果我这样做,我将有兴趣改变一个实体与另一个实体的关系。 这是一个非常简单的实现,它假定所有FK属性都以“Id”结尾并且是整数。

foreach (var entry in context.ChangeTracker.Entries())
{
    foreach (var prop in entry.Entity.GetType().GetProperties())
    {
        if (prop.Name.EndsWith("Id") && 
            !prop.PropertyType.IsAssignableFrom(typeof(int)) && 
            context.Entry(entry.Entity).Property(prop.Name).IsModified)
        {
            // Log things like old value and new:
            var propEntry = context.Entry(entry.Entity).Property(prop.Name);
            var currentValue = propEntry.CurrentValue;
            var oldValue = propEntry.OriginalValue;
        }

    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM