9

私は、他のエンティティとの2つのゼロから多くの関係を持つ、継承する基本クラスを持っています。

public abstract class WebObject
{
    public WebObject()
    {
        RelatedTags = new List<Tag>();
        RelatedWebObjects = new List<WebObject>();
    }

    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }

    public string MetaKeywords { get; set; }
    public string MetaDescription { get; set; }

    [InverseProperty("WebObjects")]
    public virtual WebSite WebSite { get; set; }

    [Required(ErrorMessage = "Every WebObject must be associated with a WebSite.")]
    public Guid WebSiteId { get; set; }

    public virtual ICollection<Tag> RelatedTags { get; set; }
    public IList<Guid> RelatedTagIds { get; set; }
    public virtual ICollection<WebObject> RelatedWebObjects { get; set; }
    public IList<Guid> RelatedWebObjectIds { get; set; }
}

SaveChanges中にChangeTrackerを使用してエンティティを表示するときに、これらの関係(RelatedWebObjectsおよびRelatedTags)の元の値を取得するのに問題があります。前後のすべてのスカラー値を見ることができ、新しい関係を見ることができますが、古い関係を見ることができません。MemberメソッドとCollectionメソッドを使用してみましたが、現在の値しか表示されません。古いものではありません。また、ナビゲーションプロパティの名前を知っている必要があるため、これらを使用するのは好きではありません。これは、十分に一般的ではありません。

関係が変更されている関連オブジェクトを見つけることができますが、もちろん、それらの関連オブジェクト内の値は変更されていないので、それも役に立ちません。

ChangeTrackerを使用したSaveChanges中に、エンティティの以前の関係を追跡するためのクリーンな方法はありますか?

以下は私が取り組んでいるコードのセクションです:

    public override int SaveChanges()
    {
        List<AuditObject> auditTrailList = new List<AuditObject>();

        foreach (DbEntityEntry entity in ChangeTracker.Entries().Where(obj => { return obj.State == EntityState.Added || obj.State == EntityState.Modified || obj.State == EntityState.Deleted; }))
        {
            if (!(entity.Entity is AuditObject))
            {
                AuditObject auditObject = new AuditObject();

                auditObject.Id = Guid.NewGuid();

                auditObject.RevisionStamp = DateTime.Now;

                auditObject.UserName = HttpContext.Current.User.Identity.Name;

                auditObject.EntityType = Utilities.GetCleanClassNameIfProxyClass(entity.Entity.GetType().Name);

                if (entity.State == EntityState.Added)
                    auditObject.Action = EntityState.Added.ToString();
                else if (entity.State == EntityState.Modified)
                    auditObject.Action = EntityState.Modified.ToString();
                else if (entity.State == EntityState.Deleted)
                    auditObject.Action = EntityState.Deleted.ToString();

                DbMemberEntry t1 = entity.Member("RelatedWebObjects");
                // cannot find original relationship collection...

                DbCollectionEntry t2 = entity.Collection("RelatedWebObjects");
                // cannot find original relationship collection...

                if (entity.State == EntityState.Added || entity.State == EntityState.Modified)
                {
                    XDocument currentValues = new XDocument(new XElement(auditObject.EntityType));

                    foreach (string propertyName in entity.CurrentValues.PropertyNames)
                    {
                        currentValues.Root.Add(new XElement(propertyName, entity.CurrentValues[propertyName]));
                    }

                    auditObject.NewData = Regex.Replace(currentValues.ToString(), @"\r\n+", " ");
                }

                if (entity.State == EntityState.Modified || entity.State == EntityState.Deleted)
                {
                    XDocument originalValues = new XDocument(new XElement(auditObject.EntityType));

                    foreach (string propertyName in entity.OriginalValues.PropertyNames)
                    {
                        originalValues.Root.Add(new XElement(propertyName, entity.OriginalValues[propertyName]));
                    }

                    auditObject.OldData = Regex.Replace(originalValues.ToString(), @"\r\n+", " ");
                }

                auditTrailList.Add(auditObject);
            }
        }

        foreach (var audit in auditTrailList)
            this.AuditObjects.Add(audit);

        return base.SaveChanges();
    }
4

2 に答える 2

13

さて、これは少し難しいです。まず、EFが提供する2種類の関係を変える必要があります。

  • 独立した協会(すべての多対多の関係といくつかの1対多の関係)
  • 外部キー協会(すべて1対1の関係と一部の1対多)

ここで、外部キーの関連付けの以前の値を知りたい場合は、外部キーのプロパティを公開した依存エンティティの変更を追跡する必要があります。これは、他のプロパティの変更を追跡するのとまったく同じです。

独立した関連付けで変更を追跡する場合、DbContext APIはそれらを追跡する操作を提供しないため、状況はより困難になります。ObjectContextAPIとそのに戻る必要がありますObjectStateManager

ObjectContext objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
foreach (ObjectStateEntry entry = objectContext.ObjectStateManager
                                               .GetObjectStateEntries(~EntityState.Detached)
                                               .Where(e => e.IsRelationship))
{
    // Track changes here
}

ObjectStateEntryこれで、関係のインスタンスにアクセスできます。これらのインスタンスは決して状態を持つべきではありませんModified。これらはAddedDeletedまたはUnchanged「変更」が古い関係の削除と新しい関係の追加として処理されるためです。とコレクションObjectStateEntryも含まれています。これらのコレクションには、リレーションの片側のエンティティをそれぞれ表す2つのアイテムも含まれている必要があります。CurrentValuesOriginalValuesEntityKey

于 2011-08-20T17:57:13.653 に答える
4

EF変更はグラフ内のすべてのオブジェクトを追跡するため、グラフ内の任意のインスタンスをいつでも変更トラッカーに渡すことができ、変更追跡値が提供されます。たとえば、次のコードは、AuditObjectのナビゲーションプロパティの元の値/現在の値を取得します。

DbMemberEntry t1 = entity.Member("RelatedWebObjects");
// cannot find original relationship collection....

AuditObject currentAuditObject = (AuditObject) entity;
var currValues = this.Entry(currentAuditObject.RelatedWebObjects).CurrentValues;
var orgValues = this.Entry(currentAuditObject.RelatedWebObjects).OriginalValues;

または、コレクションタイプのナビゲーションプロパティを処理するときに同じトリックを適用できます。

DbCollectionEntry t2 = entity.Collection("RelatedWebObjects");
// cannot find original relationship collection....

foreach (WebObject item in currentAuditObject.RelatedWebObjects)
{
    var currValues = this.Entry(item).CurrentValues;
}
于 2011-08-20T16:47:21.490 に答える