2011-07-05 2 views
3

우리는 다음을가집니다. UserId 및 User 테이블로 주문하십시오.Entity Framework 4.1에서 캐시 된 연결이 끊어진 엔터티 연결 코드 우선

모든 User 개체를 연결이 끊긴 엔터티로 캐시 한 다음 ASP.NET이나 웹 서비스 같은 환경에서 사용하려고한다고 가정 해 봅니다.

환경은 UnitOfWork 및 IOC 프레임 워크를 사용하기 때문에 컨텍스트 조작을 피하고자합니다. 단위 테스트는 다음과 같이 :

  1. 전체 주문 객체를 지속 사용자의 객체 또는 아이디를 통해 사용자를 연결합니다 주문 객체
  2. 상황을 만들기 연결이 끊긴 사용자 개체를 가져옵니다.

2 일 동안 맹렬한 인터넷 검색 결과를 얻지 못했습니다.

문제는 다음과 같습니다

1. 사용자 개체

우리는 사용자 개체를 연결하는 경우 (와 userId를 = 1) EF는 새로운 사용자이라고 생각 기본 동작과, 개체에 저장하고 새 사용자 개체를 db에 유지하려고합니다. db가 이미 userId == 1이기 때문에 불면합니다.

가능한 해결 방법은 DbContext.SaveChanges를 재정의하고 사용자가 "분리되어"강제로 컨텍스트에 첨부하는지 확인하는 것입니다. SaveChanges에서 호출 할 때 확장 메서드 EFExtensionMethods.IsAttached 또는 EFExtensionMethods.AttachItem이 T가 Object라고 생각하기 때문에이를 수행하는 방법을 알 수 없습니다.

2 USER_ID

이것은 당신이 지속 전체 개체를 다시로드하기 전에 Order.User 개체에 액세스 할 경우를 제외하고, 작동합니다.

그 위에 우리는 실제 개체 (Order.User)가 아닌 객체의 ID (즉, Order.UserId)만을 사용하여 저장하기 위해 API를 분할해야합니다. 객체를 다시로드 한 후에는 둘 다 사용할 수 있습니다. 그러나 API를 통해 실제로 적용 할 방법이 없다는 것을 알 수 있습니다.

3. BOTH 사용자 개체 및 사용자가 외부 키로 사용자 아이디를 사용하는 것으로 표시된 경우에도이 시나리오에서는

USER_ID는, EF는 여전히 1에서 설명하는 문제를 치는 상황에 사용자 개체를 저장하려고 . 내가 뭔가 (또는 많이)없는 것 같은

는 기본적인 외모와 질문은 다음과 같습니다

  • 은 무엇 당신이 할 추천?
  • 이는 DbContext에서 EFExtensionMethods.AttachItem 작업을 만드는 좋은 일반적인 방법이 있나요 DbContext.SaveChanges
  • 에서 EFExtensionMethods.IsAttached 작품을 만드는 좋은 일반적인 방법입니다.저장 변경

도움을 주시면 대단히 감사하겠습니다.

[TestMethod] 
public void Creating_Order_With_Both_User_And_Id() 
{ 
    int userCount = CountAll<User>(); 
    User user; 
    using (var db = GetContext()) { user = db.Users.AsNoTracking().First(); } 
    using (var db = GetContext()) 
    { 
     var o = CreateOrder(); 
     o.OrderUser = user;   // attach user entity 
     o.UserId = user.UserID;  // attach by id 
     db.Orders.Add(o); 
     db.SaveChanges(); 
    } 
    int newUserCount = CountAll<User>(); 
    Assert.IsTrue(userCount == newUserCount, string.Format("Expected {0} got {1}", userCount, newUserCount)); 
} 

컨텍스트 및 클래스 :

// ... 
using (var db = GetContext()) 
{ 
    var o = CreateOrder(); 

    db.Users.Attach(user); 

    o.OrderUser = user;   // attach user entity 
    o.UserId = user.UserID;  // attach by id 
    db.Orders.Add(o); 
    db.SaveChanges(); 
} 
// ... 

그렇지 않으면 EF가 Added 상태로 사용자를 넣어 것입니다 :

public class User 
{ 
    public User() {} 
    public int UserID {get;set;} 
    public string UserName {get;set;} 
} 

public class Order 
{ 
    public Order() { } 

    public int OrderID { get; set; } 
    public DateTime OrderDate { get; set; } 
    public string OrderName { get; set; } 

    public int UserId { get; set; } 
    public virtual User OrderUser { get; set; } 
} 

public class OrderConfiguration : EntityTypeConfiguration<Order> 
{ 
    public OrderConfiguration() 
    { 
     this.ToTable("ORDERS"); 
     this.Property(x => x.OrderName).HasMaxLength(200); 
     this.HasRequired(u => u.OrderUser).WithMany().HasForeignKey(u => u.UserId); 
    } 
} 

public static class EFExtensionMethods 
{ 
    // does not work, when called from DbContext.SaveChanges thinks T is an Object. 
    public static bool IsAttached<T>(this PPContext db, T entity) where T: class 
    { 
     return db.Set<T>().Local.Any(e => e == entity); 
    } 

    // does not work, when called from DbContext.SaveChanges thinks T is an Object. 
    public static void AttachItem<T>(this PPContext db, T entity) where T: class 
    { 
     db.Set<T>().Attach(entity); 
    } 
} 

public class PPContext : DbContext 
{ 
    public PPContext() : base() { } 
    public PPContext(DbConnection connection) : base(connection, true) { } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     modelBuilder.Configurations.Add(new OrderConfiguration()); 
    } 

    public override int SaveChanges() 
    { 
     var modified = ChangeTracker.Entries().Where(e => e.State == EntityState.Modified || e.State == EntityState.Added); 
     foreach (var item in modified) 
     { 
      ???? 
     } 
    } 

    public DbSet<User> Users {get;set;} 
} 

답변

6

사용자 개체에 마커 인터페이스를 추가했으며 모든 ICacheableEntity entites를 Entity로 표시했습니다. SaveChanges에서는 항목을 변경되지 않은 것으로 표시합니다. 그런 식으로 작동합니다.

public class User : ICacheableEntity 
{ 
    public User() { } 
    public int UserID {get;set;} 
    public string UserName {get;set;} 
} 

class PPContext 
{ 
    public bool IsSeeding {get;set;} 

    public override int SaveChanges() 
    { 
     var modified = ChangeTracker.Entries().Where(e => e.State == EntityState.Modified || e.State == EntityState.Added); 
     if (!IsSeeding) 
     { 
      foreach (var item in modified.Where(item => (item.Entity is ICacheableEntity))) 
      { 
       item.State = EntityState.Unchanged; 
      } 
     } 
    } 
} 
+0

멋진 솔루션입니다! – Slauma

+0

캐시 된 항목은 모두 불변입니까? 캐시 된 것처럼 보이므로 변경할 수 없습니까? 아니면 IsSeeding 플래그로 뭔가를하고 있습니까? – Jafin

+0

캐시의 불변성은이 연습의 범위에서 고려되지 않습니다. 그리고 그것에 대해 더 생각할수록 중요하지 않습니다. 정말 :) – b0rg

1

가 단순히 Attach이 TestMethod에서 누락되지인가 컨텍스트에 주문을 추가하면 신규 사용자 SaveChanges에 전화하십시오.

+0

감사합니다. Attach는 내가 피하려고했던 것입니다. 그리고 SaveChanges 메서드에있는 모든 "Added"엔티티를 추적하고이를 컨텍스트에 강제로 추가하는 것과 같은 다른 가능성이 있는지 확인하려고했습니다. – b0rg

+0

@ b0rg : 흠, 아마도 내 대답을 너무 빨리 받아 들였을거야. 나는 Attach라고 부르는 것 외에 다른 방법이 없다고 말하고 싶지 않았다. 나는 당신이 이것을 정확히 피하고 싶다는 것을 몰랐습니다. 아마도'SaveChanges' 메쏘드의'????'에 대해서'item.State = EntityState.Unchanged; '와 같은 것이 가능합니다. (하지만 둘 다'Added '상태이기 때문에 사용자와 순서를 구별해야합니다.) 그러나 나는 왜 당신이'Attach'를 피하고 싶은지 정확히 이해하지 못합니다. 그것은 나에게 훨씬 쉽게 보인다. – Slauma

+0

난 아직도이게 새로운데. 귀하의 대답은 정확합니다. 쉬운 방법이 없습니다. 그리고 많은 코드를 읽으려는 노력에 +1하고 싶었습니다. 우리 앱에서는 주문과 사용자가 아니기 때문에 20 개 이상의 캐쉬 된 "사전"객체를 가진 큰 객체와 매번 그 모든 객체를 첨부하는 것이 조금 지루하기 때문에 ... # – b0rg

관련 문제