2012-03-14 6 views
0

本当に誰かが助けてくれることを願っています。私が何か新しい技術を学ぶためにそれを使用しようとしているので、私が無益にしている家族計画を進めることが必死になっています。EF 4.3セービングエラーが発生したり、行が重複したりする

これはよくある問題で、stackoverflowなどのいくつかの類似したish投稿を見つけたようですが、現在は私を実際の解決策に導いているようです。

データベースにエラーや重複行が発生すると、私の単純化モデルは1対多の関係になります。しかし、すべてのページの読み込み時にデータベースヒットを保存するために、ユーザーセッションにWebsiteUserSessionオブジェクトを格納しようとしています。

モデル:

public class WebsitePageTracking 
{ 
    [Key] 
    public long Id { get; set; } 
    public virtual WebsiteUserSession WebsiteUserSession { get; set; } 
    public DateTime DateTime { get; set; } 
    public string TrackUrl { get; set; } 
} 

public class WebsiteUserSession 
{ 
    [Key] 
    public long Id { get; set; } 
    public Guid Guid { get; set; } 
    public string IpAddress { get; set; } 
    public DateTime DateTime_Created { get; set; } 
    public DateTime DateTime_LastSessionStart { get; set; } 

    public virtual ICollection<WebsitePageTracking> WebsitePageTracking { get; set; } 
} 

次に、以下のように私のコードは、もう少し複雑ですが、うまくいけば、アイデアを得ます。 datacontextは、1つの提案であったHttpContext.Itemsに格納されていることに注意してください。

/// <summary> 
/// public method for fetching and persisting website user session 
/// </summary> 
/// <returns></returns> 
public static WebsiteUserSession GetWebsiteUserSession() 
{ 
    // set initial variables 
    Guid? websiteUserGuid = null; 
    string websiteUserIpAddress = HttpContext.Current.Request.UserHostAddress; 
    WebsiteUserSession websiteUserSession = null;    

    // first try and get from the session 
    if (HttpContext.Current.Session["WebsiteUserSession"] != null) 
    { 
     // load the website user session, and fetch out the guid 
     websiteUserSession = HttpContext.Current.Session["WebsiteUserSession"] as WebsiteUserSession; 
     websiteUserGuid = websiteUserSession.Guid; 

     // check to see if ip has changed, if so drop website user session 
     if (websiteUserSession.IpAddress != websiteUserIpAddress) 
      websiteUserSession = null; 
    } 
    else 
    { 
     // nothing so create brand new guid 
     websiteUserGuid = Guid.NewGuid(); 
    } 

    // if we don't have one which came from the session then get the website user session using guid/ip 
    if (websiteUserSession == null) 
     websiteUserSession = GetWebsiteUserSession((Guid)websiteUserGuid, websiteUserIpAddress); 

    // if not stored in the session add that now 
    if (HttpContext.Current.Session["WebsiteUserSession"] == null) 
     HttpContext.Current.Session.Add("WebsiteUserSession", websiteUserSession); 

    // return back website user session object 
    return websiteUserSession; 
} 

/// <summary> 
/// gets or creates a website user session record in the database 
/// </summary> 
/// <param name="guid">guid generated for the website user</param> 
/// <param name="ipAddress">public ip address of website user</param> 
/// <returns></returns> 
private static WebsiteUserSession GetWebsiteUserSession(Guid guid, string ipAddress) 
{ 
    DataContext dataContext = HttpContext.Current.Items["DataContext"] as DataContext; 

    // try and fetch a user session from database 
    WebsiteUserSession websiteUserSession = dataContext 
     .WebsiteUserSessions 
     .Where(x => x.Guid == guid && x.IpAddress == ipAddress) 
     .SingleOrDefault(); 

    if (websiteUserSession != null) 
    { 
     // if found update last session time 
     websiteUserSession.DateTime_LastSessionStart = DateTime.Now; 
    } 
    else 
    { 
     // create a new website user session 
     websiteUserSession = new WebsiteUserSession(); 
     websiteUserSession.Guid = guid; 
     websiteUserSession.IpAddress = ipAddress; 
     websiteUserSession.DateTime_Created = websiteUserSession.DateTime_LastSessionStart = DateTime.Now; 
     dataContext.WebsiteUserSessions.Add(websiteUserSession); 
    } 

    // persist changes 
    dataContext.SaveChanges();     

    return websiteUserSession; 
} 

/// <summary> 
/// log a stock page view to database 
/// </summary> 
/// <param name="stockId">id of stock being viewed</param> 
public static void LogStockPageView(int stockId) 
{ 
    DataContext dataContext = HttpContext.Current.Items["DataContext"] as DataContext; 

    WebsiteUserSession websiteUserSession = GetWebsiteUserSession(); 

    //dataContext.WebsiteUserSessions.Attach(websiteUserSession); 
    //dataContext.Entry(websiteUserSession).State = EntityState.Unchanged; 

    WebsitePageTracking pageTrack = new WebsitePageTracking(); 
    pageTrack.WebsiteUserSession = websiteUserSession; 
    pageTrack.DateTime = DateTime.Now; 
    pageTrack.TrackUrl = HttpContext.Current.Request.Url.ToString(); 

    dataContext.Entry(client).State = EntityState.Unchanged; 
    dataContext.Entry(website).State = EntityState.Unchanged; 
    dataContext.Entry(websiteUserSession).State = EntityState.Unchanged; 

    //dataContext.Entry(websiteUserSession.WebsitePageTracking).State = EntityState.Detached; 

    //dataContext.WebsiteUserSessions.Attach(pageTrack.WebsiteUserSession);  

    dataContext.WebsitePageTracking.Add(pageTrack); 

    //dataContext.Entry(pageTrack.WebsiteUserSession).State = EntityState.Unchanged; 

    dataContext.SaveChanges(); 
} 

は、基本的に何が起こるべきことは、誰かが順番にいずれかのセッション、データベースからWebsiteUserSessionのエンティティを取得するか、新しいものを作成し、呼び出されるページ、LogStockPageViewを()閲覧しています。次に、WebsitePageTrackingエンティティがWebsiteUserSessionへの関連付けとともにデータベースに追加されます。

あなたはこの作業を取得しようとする試みのすべてのコメント行を見ることができますが、私は挿入されている重複行のエラーや多くを得るのいずれか:(

一つのそのようなエラーは、同じキーを持つオブジェクトが既に存在する」これですObjectStateManagerで同じキーを持つ複数のオブジェクトを追跡することはできません」。

(英語の場合は、実際にEFを使用していますが、

ありがとう、Carl

答えて

1

この単純なコードには、EFの仕組みのために多くの問題があります。最初の問題はセッションを使用することです。セッションはデータベースのクエリを節約しますが、予想されるすべてのシナリオでこの作業を行うには、アプリケーションに特別なコードをたくさん追加する必要があります。セッションに保存されたエンティティが決してデータ変更に使用されないシナリオでのみセッションを使用します(これはEFで再び使用されることはありません)。この場合、FKプロパティを使用して多くの痛みを避けることができます。

最初の問題は、GetWebsiteUserSessionに表示され、メソッドから添付されたエンティティが返されます。レイジーローディングを有効にしている場合は、次のリクエストでトラッキングを追加するとObjectDisposedExceptionになることは間違いないでしょう。その理由は、プロキシされた添付エンティティが、現在の要求の最後に配置されている現在のコンテキストを内部的に参照し続けるためです。エンティティがナビゲーションプロパティをロードしようとすると、エンティティはその参照を使用します。それは、単一のシナリオでの作業あなたのコードの残りの部分をできるようになりますので、あなたがプロキシの作成/遅延ロードが、よりよい解決策は、従来の方法からそれを返すにエンティティを外すことができますをオフにした場合に動作することができます

dataContext.Entry(websiteUserSession).State = EntityState.Detached; 

は注意してくださいその取り外しにより、WebSitePageTrackingコレクションが空になります。エンティティグラフをデタッチする場合は、ディープクローン(シリアル化/デシリアライゼーション)を作成する必要があります。

第2の問題は、全体がLogStockPageViewです。現在のセッションインスタンスがコンテキストにアタッチされている場合は、再度アタッチするか状態を変更する必要はありませんが、分離されたエンティティの場合は実行する必要があります。試してみてください:

public static void LogStockPageView(int stockId) 
{ 
    DataContext dataContext = HttpContext.Current.Items["DataContext"] as DataContext; 

    WebsiteUserSession websiteUserSession = GetWebsiteUserSession(); 

    dataContext.WebsiteUserSessions.Attach(websiteUserSession); 

    WebsitePageTracking pageTrack = new WebsitePageTracking(); 
    pageTrack.DateTime = DateTime.Now; 
    pageTrack.TrackUrl = HttpContext.Current.Request.Url.ToString(); 

    dataContext.WebsitePageTracking.Add(pageTrack); 

    // Make connection after both session and track are correctly configured and attached 
    pageTrack.WebsiteUserSession = websiteUserSession; 

    dataContext.SaveChanges(); 

    // again detach session 
    dataContext.Entry(websiteUserSession).State = EntityState.Detached; 
} 

サンプルコードには、他のエンティティへの複数の参照も含まれています。それらには同じ問題があります。あなたがセッションでそれらを保存し、彼らがどうにかあなたのセービングコードに関連している場合は、それらも処理する必要があります。

websiteUserSessionにセッション全体を保存したい場合は、分離する代わりにエンティティグラフの複製を使用する必要があるため、セッションが複雑になります。

+0

他のエンティティを参照しているサンプルコードで申し訳ありませんが、私の間違いを簡略化しましたが、いくつかのビットを外すのを忘れました。とにかく私はあなたの提案された変更を行い、それは動作します!今日はニュージーランドの深夜過ぎですので、寝る必要がありましたが、あなたの提案を試してみたいと思っていました...明日もう一度、詳細に行き、私のプロジェクトを続けるでしょう:)ありがとう100万!! – user1259167

関連する問題