Có thể kiểm tra xem một đối tượng đã được đính kèm với ngữ cảnh dữ liệu trong Entity Framework chưa?


86

Tôi gặp lỗi sau khi cố gắng đính kèm một đối tượng đã được đính kèm vào ngữ cảnh nhất định qua context.AttachTo(...):

Một đối tượng có cùng khóa đã tồn tại trong ObjectStateManager. ObjectStateManager không thể theo dõi nhiều đối tượng với cùng một khóa.

Có cách nào để đạt được điều gì đó như sau:

context.IsAttachedTo(...)

Chúc mừng!

Biên tập:

Phương pháp mở rộng mà Jason nêu ra khá gần gũi, nhưng nó không hiệu quả với trường hợp của tôi.

Tôi đang cố gắng thực hiện một số công việc bằng cách sử dụng phương pháp được nêu trong câu trả lời cho một câu hỏi khác:

Làm cách nào để xóa một hoặc nhiều hàng khỏi bảng của tôi bằng cách sử dụng Linq to Entities * mà không * truy xuất các hàng trước?

Mã của tôi trông giống như sau:

var user = new User() { Id = 1 };
context.AttachTo("Users", user);
comment.User = user;
context.SaveChanges();

Điều này hoạt động tốt, ngoại trừ khi tôi làm điều gì đó khác cho người dùng đó khi tôi sử dụng cùng một phương pháp và cố gắng đính kèm một Userđối tượng giả . Điều này không thành công vì trước đó tôi đã đính kèm đối tượng người dùng giả đó. Làm thế nào tôi có thể kiểm tra điều này?

Câu trả lời:


57

Đây là những gì tôi đã kết thúc, nó hoạt động rất tốt:

public static void AttachToOrGet<T>(this ObjectContext context, string entitySetName, ref T entity)
    where T : IEntityWithKey
{
    ObjectStateEntry entry;
    // Track whether we need to perform an attach
    bool attach = false;
    if (
        context.ObjectStateManager.TryGetObjectStateEntry
            (
                context.CreateEntityKey(entitySetName, entity),
                out entry
            )
        )
    {
        // Re-attach if necessary
        attach = entry.State == EntityState.Detached;
        // Get the discovered entity to the ref
        entity = (T)entry.Entity;
    }
    else
    {
        // Attach for the first time
        attach = true;
    }
    if (attach)
        context.AttachTo(entitySetName, entity);
}

Bạn có thể gọi nó như sau:

User user = new User() { Id = 1 };
II.AttachToOrGet<Users>("Users", ref user);

Điều này hoạt động rất tốt vì nó giống như context.AttachTo(...)ngoại trừ bạn có thể sử dụng thủ thuật ID mà tôi đã trích dẫn ở trên mỗi lần. Bạn kết thúc với đối tượng được đính kèm trước đó hoặc đối tượng của riêng bạn được đính kèm. Gọi CreateEntityKeytheo ngữ cảnh đảm bảo rằng nó đẹp và chung chung và sẽ hoạt động ngay cả với các khóa tổng hợp mà không cần mã hóa thêm (vì EF đã có thể làm điều đó cho chúng tôi!).


Tôi đang gặp một vấn đề tương tự và điều này vừa giải quyết được vấn đề của tôi - tuyệt vời, chúc mừng! +1
RPM1984,

4
Sẽ tốt hơn nữa khi tham số chuỗi được thay thế bằng một hàm bộ chọn cho tập hợp thực thể thuộc về.
Jasper

1
Bất kỳ ý tưởng tại sao (T)entry.Entityđôi khi trả về null?
Tr1stan

1
Tôi không thể tìm ra những gì tôi nên đặt entitySetName của mình thành. Tôi tiếp tục nhận được một ngoại lệ. Tôi thực sự thất vọng vì tất cả những gì tôi muốn làm là xóa một người dùng mà tôi không muốn phải đối phó với quá nhiều ẩn ý làm nổ ứng dụng của tôi.
Julie

1
nếu Tđã có IEntityWithKey, bạn không thể chỉ sử dụng thuộc tính của nó entity.EntityKeythay vì tái tạo lại nó hoặc cần phải đoán / cung cấp EntitySetName?
drzaus

54

Một cách tiếp cận đơn giản hơn là:

 bool isDetached = context.Entry(user).State == EntityState.Detached;
 if (isDetached)
     context.Users.Attach(user);

1
Điều này làm việc cho tôi, chỉ cần tôi phải sử dụng "EntityState.Detached" thay vì chỉ "vô tư" ...
Marcelo Myara

22
Hmm đã thử giải pháp của bạn, nhưng đối với tôi thì isDetached là đúng, nhưng vẫn gặp lỗi tương tự khi tôi cố gắng Đính kèm mục nhập vào ngữ cảnh
Prokurors Ngày

Đánh giá xuất sắc. Thậm chí nó đã hoạt động với Kho lưu trữ Chung của tôi.
ATHER

5
@Prokurors Gần đây tôi đã biết lý do kiểm tra của Mosh không phải lúc nào cũng đủ để ngăn lỗi, đó là .Include()các thuộc tính điều hướng của 'ed cũng được cố gắng đính kèm khi bạn gọi .Attachhoặc đặt nó EntityStatethành EntityState.Unchanged- và chúng sẽ xung đột nếu bất kỳ thực thể nào tham chiếu đến cùng một thực thể. Tôi chưa tìm ra cách chỉ đính kèm thực thể cơ sở, vì vậy tôi phải thiết kế lại dự án một chút, để sử dụng các ngữ cảnh riêng biệt cho mỗi "giao dịch kinh doanh", giống như nó được thiết kế cho. Tôi sẽ lưu ý điều này cho các dự án trong tương lai.
Aske B.

18

Hãy thử phương pháp mở rộng này (đây là phương pháp chưa được kiểm tra và chưa được kiểm tra):

public static bool IsAttachedTo(this ObjectContext context, object entity) {
    if(entity == null) {
        throw new ArgumentNullException("entity");
    }
    ObjectStateEntry entry;
    if(context.ObjectStateManager.TryGetObjectStateEntry(entity, out entry)) {
        return (entry.State != EntityState.Detached);
    }
    return false;
}

Với tình huống mà bạn mô tả trong bản chỉnh sửa của mình, bạn có thể cần sử dụng quá tải sau để chấp nhận một EntityKeythay vì một đối tượng:

public static bool IsAttachedTo(this ObjectContext, EntityKey key) {
    if(key == null) {
        throw new ArgumentNullException("key");
    }
    ObjectStateEntry entry;
    if(context.ObjectStateManager.TryGetObjectStateEntry(key, out entry)) {
        return (entry.State != EntityState.Detached);
    }
    return false;
}

Để xây dựng một EntityKeytình huống của bạn, hãy sử dụng những điều sau đây làm kim chỉ nam:

EntityKey key = new EntityKey("MyEntities.User", "Id", 1);

Bạn có thể lấy EntityKeytừ một phiên bản hiện có của Userbằng cách sử dụng thuộc tính User.EntityKey(từ giao diện IEntityWithKey).


Điều này rất tốt, nhưng nó không hiệu quả với tôi trong hoàn cảnh của tôi ... Tôi sẽ cập nhật chi tiết câu hỏi. ps bạn muốn bool không phải boolean, và tĩnh, nhưng khác với phương thức mở rộng khá tuyệt vời đó!
joshcomley

@joshcomley: Tôi nghĩ rằng bạn có thể giải quyết bằng cách sử dụng quá tải TryGetObjectStateEntrychấp nhận một EntityKeythay vì một object. Tôi đã chỉnh sửa cho phù hợp. Hãy cho tôi biết nếu điều này không hữu ích và chúng ta sẽ quay lại bảng vẽ.
jason

Ah vừa thấy điều này - tôi đang nghiên cứu một giải pháp được nêu trong câu trả lời mà tôi vừa đăng. +1 cho sự trợ giúp và gợi ý của bạn !!
joshcomley

Bất cứ ý tưởng tại sao tôi đang nhận được một lỗi, các chi tiết ở đây stackoverflow.com/questions/6653050/...
Joper

6

Sử dụng khóa thực thể của đối tượng bạn đang cố gắng kiểm tra:

var entry = context.ObjectStateManager.GetObjectStateEntry("EntityKey");
if (entry.State == EntityState.Detached)
{
  // Do Something
}

Lòng tốt,

Dan


0

Điều này không trực tiếp trả lời câu hỏi của OP nhưng đây là cách tôi giải quyết vấn đề của mình.

Điều này dành cho những người đang sử dụng DbContextthay vì ObjectContext.

    public TEntity Retrieve(object primaryKey)
    {
        return DbSet.Find(primaryKey);
    }

Phương pháp DbSet.Find :

Tìm một thực thể với các giá trị khóa chính đã cho. Nếu một thực thể có các giá trị khóa chính đã cho tồn tại trong ngữ cảnh, thì nó sẽ được trả về ngay lập tức mà không cần đưa ra yêu cầu đối với cửa hàng. Nếu không, một thực thể có các giá trị khóa chính đã cho sẽ được gửi đến cửa hàng và thực thể này, nếu được tìm thấy, sẽ được gắn vào ngữ cảnh và được trả về. Nếu không tìm thấy thực thể nào trong ngữ cảnh hoặc cửa hàng, thì giá trị null được trả về.

Về cơ bản, nó trả về đối tượng đính kèm của đối tượng đã cho primaryKeynên bạn chỉ cần áp dụng các thay đổi trên đối tượng được trả về để giữ đúng thể hiện.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.