简体   繁体   English

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

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

This seems to be a common problem but I cannot find any answer that makes sense to me. 这似乎是一个常见的问题,但我找不到任何对我有意义的答案。

I want to create an Audit system in an app using EF6. 我想使用EF6在应用程序中创建一个Audit系统。 Detectng changes in simple properties is not a problem and creating an audit entry with the original values in just before the new values are saved is fine. 检测简单属性中的更改不是问题,并且在保存新值之前创建具有原始值的审计条目很好。

However I also need to track changes to reference properties, ie The country (which links to a different entity) has changed. 但是,我还需要跟踪对引用属性的更改,即国家(链接到不同实体)已更改。

dbChangeTracker does not pick these changes up and even if it did there seems to be no way of getting at the original value. dbChangeTracker不会选择这些更改,即使它确实没有获得原始值。

Some people point in the direction of ObjectContext for a solution, but I cannot see how that will help. 有些人指出ObjectContext的方向是一个解决方案,但我看不出它会有什么帮助。 Any help? 有帮助吗?

Other people seem to indicate that I should put the FK property explicitly on the Entity, ie Country_Id. 其他人似乎表明我应该在实体上明确地放置FK属性,即Country_Id。

Is that the solution? 这是解决方案吗?

All help greatly appreciated. 所有帮助非常感谢。

Cheers Mike 干杯迈克

Implement a method for retrieving metadata information about certain property of EntityObject: 实现一种方法,用于检索有关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;
}

The MetadataWorkspace class is a central runtime API that you can use to interact with the Entity Data Model (EDM) metadata in the context of an application. MetadataWorkspace类是一个中央运行时API,可用于在应用程序上下文中与实体数据模型(EDM)元数据进行交互。 For more information, see http://msdn.microsoft.com/en-us/library/bb387116%28v=vs.110%29.aspx 有关详细信息,请参阅http://msdn.microsoft.com/en-us/library/bb387116%28v=vs.110%29.aspx

Next, implement the IsPropertyChanged method like this: 接下来,实现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;
}

and a method for scalar property changes tracking: 以及标量属性更改跟踪的方法:

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;
                }

Tracking scalar properties is simple, right. 跟踪标量属性很简单,对。 When you have foreign keys included in your entities you may always check if those have been changed (make sure FK fields in your objects remain in sync with navigation properties, which is often an issue). 如果您的实体中包含外键,您可以随时检查这些外键是否已更改(确保对象中的FK字段与导航属性保持同步,这通常是一个问题)。 The navigation property case in the code above should give you an idea of implementation, well it should work in most cases. 上面代码中的导航属性案例应该让您了解实现,在大多数情况下它应该可以工作。

PS. PS。 If you are working on implementation of audit system, interceptors introduced in EF 6.1 is what you may want to have a look at (just an idea) http://msdn.microsoft.com/en-US/data/jj556606#Interceptors 如果您正在实施审计系统,那么您可能希望了解一下EF 6.1中引入的拦截器(只是一个想法) http://msdn.microsoft.com/en-US/data/jj556606#Interceptors

I would suggest that tracking changes to the FK properties at the time you save changes is sufficient. 我建议在保存更改时跟踪FK属性的更改就足够了。 After all, if I were doing this I would be interested in change in how one entity relates to another. 毕竟,如果我这样做,我将有兴趣改变一个实体与另一个实体的关系。 Here's a really simple implementation of that which assumes that all FK properties end with "Id" and are integers. 这是一个非常简单的实现,它假定所有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