2012-03-14 4 views
0

지난 2 일 동안 내 머리카락을 찢어 놓고 아무런 조치도 취하지 않아서 누군가가 도울 수 있기를 바랍니다. 필사적으로 제가 새로운 기술을 배우기 위해 그것을 사용하려고 할 때, 이익을 얻지 않고 빠르게 시간을 먹어가는 가족 프로젝트를 필사적으로 시도하고 있습니다.EF 4.3 1 대 다수 저장 오류 또는 중복 행

이것은 stackoverflow 등등에 몇몇 유사한 ish 포스트를 찾아 내고 그러나 지금 작동 해결책에 안내 한 것을 보이는 일반적인 문제점다는 것을 보인다.

데이터베이스에 오류나 중복 행이 생기면 단순화 된 모델이 일대 다 관계입니다. 하지만 모든 페이지로드시 데이터베이스 히트를 저장하기 위해 사용자 세션에 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는 하나의 제안 인 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 컬렉션이 비게됩니다. 엔티티 그래프를 분리하려면 딥 클론 (serialization/deserialization)을 만들어야합니다.

두 번째 문제는 전체 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

와우, 그 자리에 좋은 설명이 있습니다 ... 다른 엔티티를 참조하는 샘플 코드에 대해 유감스럽게 생각합니다. 실수를 간략화했지만 일부 비트를 빼는 것을 잊었습니다. 어쨌든 당신의 제안 된 변경 사항을 만들었고 작동합니다 !! 그리고 물건은 지금 훨씬 명확하다. .. 뉴질랜드에서의 과거의 자정이다. 그래서 취침 시간에 갔어야했다. 그러나 당신의 제안을 시험해 보는 데 흥미가 있었다. .. 내일 그들에게 다시 상세하게 갈 것이고, 나의 프로젝트와 함께 계속 될 것이다. 백만!! – user1259167