实体框架:跟踪FK关联的变化

Lef*_*une 7 entity-framework poco code-first ef-code-first entity-framework-4.1

我在我的DbContext上覆盖了SaveChanges以实现审计日志.使用多对多关系或独立关联相对容易,因为EF为这些关系的任何更改创建了ObjectStateEntries.

我正在使用外键关联,当实体之间的关系发生变化时,所有得到的是ObjectStateEnty,例如实体"Title"的"PublisherID"属性已更改.对于人类而言,这显然是Title实体中的外键,但我如何在运行时确定它?有没有办法将此更改转换为"PublisherID"属性,以使外键所代表的实体成为EntityKey?

我假设我正在处理看起来像这样的实体:

public sealed class Publisher
{
    public Guid ID { get; set; }
    public string Name { get; set; }
    public ICollection<Title> Titles { get; set; }
}

public class Title
{
    public Guid ID { get; set; }
    public string Name { get; set; }
    public Guid? PublisherID { get; set; }
    public Publisher Publisher { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

还有EF EntityConfiguration代码,用于定义关系和外键:

public TitleConfiguration()
{
    HasOptional<Publisher>(t => t.Publisher).WithMany(
            p => p.Titles).HasForeignKey(t => t.PublisherID);
}
Run Code Online (Sandbox Code Playgroud)

我现在正在做的事似乎有点太复杂了.我希望有更优雅的方式来实现我的目标.对于ObjectStateEntry中的每个修改过的属性,我查看当前实体的所有ReferentialConstraints,看看是否有任何一个将它用作外键.从SaveChanges()调用以下代码:

private void HandleProperties(ObjectStateEntry entry, 
        ObjectContext ctx)
{
    string[] changedProperties = entry.GetModifiedProperties().ToArray();
    foreach (string propertyName in changedProperties)
    {
        HandleForeignKey(entry, ctx, propertyName);
    }
}

private void HandleForeignKey(ObjectStateEntry entry, 
        ObjectContext ctx, string propertyName)
{
    IEnumerable<IRelatedEnd> relatedEnds = 
            entry.RelationshipManager.GetAllRelatedEnds();

    foreach (IRelatedEnd end in relatedEnds)
    {
        // find foreign key relationships
        AssociationType elementType = end.RelationshipSet.ElementType as 
                AssociationType;
        if (elementType == null || !elementType.IsForeignKey) continue;

        foreach (ReferentialConstraint constraint in 
                elementType.ReferentialConstraints)
        {
            // Multiplicity many means we are looking at a foreign key in a 
            // dependent entity
            // I assume that ToRole will point to a dependent entity, don't 
            // know if it can be FromRole
            Debug.Assert(constraint.ToRole.RelationshipMultiplicity == 
                    RelationshipMultiplicity.Many);
            // If not 1 then it is a composite key I guess. 
            // Becomes a lot more difficult to handle.
            Debug.Assert(constraint.ToProperties.Count == 1);
            EdmProperty prop = constraint.ToProperties[0];

            // entity types of current entity and foreign key entity 
            // must be the same
            if (prop.DeclaringType == entry.EntitySet.ElementType 
                    && propertyName == prop.Name)
            {
                EntityReference principalEntity = end as EntityReference;
                if (principalEntity == null) continue;

                EntityKey newEntity = principalEntity.EntityKey;
                // if there is more than one, the foreign key is composite
                Debug.Assert(newEntity.EntityKeyValues.Length == 1);

                // create an EntityKey for the old foreign key value
                EntityKey oldEntity = null;

                if (entry.OriginalValues[prop.Name] is DBNull)
                {
                    oldEntity = new EntityKey();
                    oldEntity.EntityKeyValues = new[] { 
                        new EntityKeyMember("ID", "NULL") 
                    };
                    oldEntity.EntitySetName = newEntity.EntitySetName;
                }
                else
                {
                    Guid oldGuid = Guid.Parse(
                            entry.OriginalValues[prop.Name].ToString());
                    oldEntity = ctx.CreateEntityKey(newEntity.EntitySetName, 
                            new Publisher()
                            {
                                ID = oldGuid
                            });
                }

                Debug.WriteLine(
                        "Foreign key {0} changed from [{1}: {2}] to [{3}: {4}]", 
                        prop.Name,
                        oldEntity.EntitySetName, oldEntity.EntityKeyValues[0],
                        newEntity.EntitySetName, newEntity.EntityKeyValues[0]);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我希望这有助于更好地说明我想要实现的目标.欢迎任何输入.

谢谢!

Lef*_*une 1

看来我的代码是解决这个问题的正确方法:/

我最终确实使用独立协会来完全避免这个问题。