Một DbContext cho mỗi yêu cầu web tại sao?


398

Tôi đã đọc rất nhiều bài viết giải thích cách thiết lập Entity Framework DbContextđể chỉ một cái được tạo và sử dụng cho mỗi yêu cầu web HTTP bằng nhiều khung DI khác nhau.

Tại sao đây là một ý tưởng tốt ở nơi đầu tiên? Những lợi thế bạn có được bằng cách sử dụng phương pháp này? Có những tình huống nhất định trong đó đây sẽ là một ý tưởng tốt? Có những điều bạn có thể làm bằng cách sử dụng kỹ thuật này mà bạn không thể thực hiện khi khởi tạo DbContexts cho mỗi cuộc gọi phương thức kho lưu trữ?


9
Gueddari trong mehdi.me/ambient-dbcontext-in-ef6 gọi đối tượng DbContext cho mỗi phương thức kho lưu trữ gọi một antipotype. Trích dẫn: "Bằng cách này, bạn mất khá nhiều tính năng mà Entity Framework cung cấp thông qua DbContext, bao gồm bộ đệm cấp 1, bản đồ nhận dạng, đơn vị công việc và khả năng theo dõi thay đổi và tải lười biếng của nó . " Bài viết tuyệt vời với những gợi ý tuyệt vời để xử lý vòng đời của DBContexts. Chắc chắn đáng đọc.
Christoph

Câu trả lời:


565

LƯU Ý: Câu trả lời này nói về Khung thực thể DbContext, nhưng nó có thể áp dụng cho bất kỳ loại Đơn vị thực hiện Công việc nào, chẳng hạn như LINQ to SQL DataContextvà NHibernate ISession.

Hãy bắt đầu bằng cách lặp lại Ian: Có một cái duy nhất DbContextcho toàn bộ ứng dụng là một ý tưởng tồi. Tình huống duy nhất có ý nghĩa này là khi bạn có một ứng dụng đơn luồng và cơ sở dữ liệu chỉ được sử dụng bởi cá thể ứng dụng đó. Nó DbContextkhông an toàn cho luồng và vì DbContextdữ liệu lưu trữ, nó sẽ bị cũ khá sớm. Điều này sẽ khiến bạn gặp nhiều rắc rối khi nhiều người dùng / ứng dụng hoạt động trên cơ sở dữ liệu đó cùng một lúc (điều này rất phổ biến). Nhưng tôi hy vọng bạn đã biết điều đó và chỉ muốn biết tại sao không tiêm một ví dụ mới (tức là với lối sống thoáng qua) DbContextvào bất cứ ai cần nó. (để biết thêm thông tin về lý do tại sao một DbContexthoặc ngay cả trên ngữ cảnh trên mỗi luồng - là xấu, hãy đọc câu trả lời này ).

Hãy để tôi bắt đầu bằng cách nói rằng đăng ký DbContexttạm thời có thể hoạt động, nhưng thông thường bạn muốn có một phiên bản duy nhất của một đơn vị công việc như vậy trong một phạm vi nhất định. Trong một ứng dụng web, việc xác định phạm vi như vậy trên các ranh giới của một yêu cầu web có thể là thực tế; do đó, một lối sống trên mỗi yêu cầu web. Điều này cho phép bạn để cho cả một tập hợp các đối tượng hoạt động trong cùng một bối cảnh. Nói cách khác, họ hoạt động trong cùng một giao dịch kinh doanh.

Nếu bạn không có mục tiêu có một tập hợp các hoạt động trong cùng một bối cảnh, thì trong trường hợp đó, lối sống thoáng qua là tốt, nhưng có một vài điều cần xem:

  • Vì mỗi đối tượng có thể hiện riêng của nó, mỗi lớp thay đổi trạng thái của hệ thống, cần phải gọi _context.SaveChanges()(nếu không các thay đổi sẽ bị mất). Điều này có thể làm phức tạp mã của bạn và thêm trách nhiệm thứ hai vào mã (trách nhiệm kiểm soát bối cảnh) và vi phạm Nguyên tắc Trách nhiệm duy nhất .
  • Bạn cần đảm bảo rằng các thực thể [được tải và lưu bởi a DbContext] không bao giờ rời khỏi phạm vi của một lớp như vậy, bởi vì chúng không thể được sử dụng trong trường hợp ngữ cảnh của một lớp khác. Điều này có thể làm phức tạp mã của bạn rất nhiều, bởi vì khi bạn cần những thực thể đó, bạn cần tải lại chúng bằng id, điều này cũng có thể gây ra vấn đề về hiệu suất.
  • Kể từ khi DbContextthực hiện IDisposable, có lẽ bạn vẫn muốn Loại bỏ tất cả các phiên bản đã tạo. Nếu bạn muốn làm điều này, về cơ bản bạn có hai lựa chọn. Bạn cần loại bỏ chúng theo cùng một phương thức ngay sau khi gọi context.SaveChanges(), nhưng trong trường hợp đó logic nghiệp vụ sẽ sở hữu một đối tượng mà nó được truyền từ bên ngoài. Tùy chọn thứ hai là Loại bỏ tất cả các phiên bản được tạo trên ranh giới của Yêu cầu http, nhưng trong trường hợp đó, bạn vẫn cần một số phạm vi để cho container biết khi nào các trường hợp đó cần được xử lý.

Một lựa chọn khác là không tiêm một DbContextchút nào. Thay vào đó, bạn tiêm một DbContextFactorycái có thể tạo một thể hiện mới (tôi đã từng sử dụng phương pháp này trong quá khứ). Bằng cách này logic kinh doanh kiểm soát bối cảnh rõ ràng. Nếu có thể trông như thế này:

public void SomeOperation()
{
    using (var context = this.contextFactory.CreateNew())
    {
        var entities = this.otherDependency.Operate(
            context, "some value");

        context.Entities.InsertOnSubmit(entities);

        context.SaveChanges();
    }
}

Điểm cộng của việc này là bạn quản lý cuộc sống DbContextmột cách rõ ràng và thật dễ dàng để thiết lập điều này. Nó cũng cho phép bạn sử dụng một bối cảnh duy nhất trong một phạm vi nhất định, có lợi thế rõ ràng, chẳng hạn như chạy mã trong một giao dịch kinh doanh duy nhất và có thể vượt qua các thực thể, vì chúng có nguồn gốc từ cùng một DbContext.

Nhược điểm là bạn sẽ phải chuyển DbContexttừ phương thức này sang phương thức khác (được gọi là Phương pháp tiêm). Lưu ý rằng theo một nghĩa nào đó, giải pháp này giống như phương pháp 'phạm vi', nhưng bây giờ phạm vi được kiểm soát trong chính mã ứng dụng (và có thể được lặp lại nhiều lần). Đây là ứng dụng chịu trách nhiệm tạo và xử lý đơn vị công việc. Vì DbContextbiểu đồ được tạo sau khi biểu đồ phụ thuộc được xây dựng, Con constructor Injection không có hình ảnh và bạn cần trì hoãn Phương thức tiêm khi bạn cần chuyển ngữ cảnh từ lớp này sang lớp khác.

Phương thức Tiêm không tệ, nhưng khi logic nghiệp vụ trở nên phức tạp hơn và có nhiều lớp tham gia hơn, bạn sẽ phải chuyển nó từ phương thức này sang phương thức và lớp này sang lớp khác, điều này có thể làm phức tạp mã (tôi đã thấy cái này trong quá khứ). Đối với một ứng dụng đơn giản, cách tiếp cận này sẽ làm tốt mặc dù.

Do những nhược điểm, cách tiếp cận nhà máy này dành cho các hệ thống lớn hơn, cách tiếp cận khác có thể hữu ích và đó là cách bạn để bộ chứa hoặc mã cơ sở hạ tầng / Thành phần gốc quản lý đơn vị công việc. Đây là phong cách mà câu hỏi của bạn là về.

Bằng cách để bộ chứa và / hoặc cơ sở hạ tầng xử lý việc này, mã ứng dụng của bạn không bị ô nhiễm bằng cách phải tạo, (tùy chọn) cam kết và Loại bỏ một cá thể UoW, giữ cho logic nghiệp vụ đơn giản và sạch sẽ (chỉ là một Trách nhiệm duy nhất). Có một số khó khăn với phương pháp này. Chẳng hạn, bạn có Cam kết và vứt bỏ ví dụ không?

Việc xử lý một đơn vị công việc có thể được thực hiện ở cuối yêu cầu web. Nhiều người tuy nhiên, sai giả định rằng điều này cũng là nơi để Commit đơn vị làm việc. Tuy nhiên, tại thời điểm đó trong ứng dụng, bạn chỉ cần xác định chắc chắn rằng đơn vị công việc thực sự phải được cam kết. ví dụ: Nếu mã lớp doanh nghiệp đã ném một ngoại lệ được bắt lên cao hơn trong bảng gọi, bạn chắc chắn không muốn Cam kết.

Giải pháp thực sự là một lần nữa để quản lý rõ ràng một số loại phạm vi, nhưng lần này thực hiện nó bên trong Root Root. Tóm tắt tất cả logic nghiệp vụ đằng sau mẫu lệnh / trình xử lý , bạn sẽ có thể viết một trình trang trí có thể được bao quanh mỗi trình xử lý lệnh cho phép thực hiện điều này. Thí dụ:

class TransactionalCommandHandlerDecorator<TCommand>
    : ICommandHandler<TCommand>
{
    readonly DbContext context;
    readonly ICommandHandler<TCommand> decorated;

    public TransactionCommandHandlerDecorator(
        DbContext context,
        ICommandHandler<TCommand> decorated)
    {
        this.context = context;
        this.decorated = decorated;
    }

    public void Handle(TCommand command)
    {
        this.decorated.Handle(command);

        context.SaveChanges();
    } 
}

Điều này đảm bảo rằng bạn chỉ cần viết mã cơ sở hạ tầng này một lần. Bất kỳ thùng chứa DI rắn nào cũng cho phép bạn định cấu hình một bộ trang trí như vậy được bao bọc xung quanh tất cả các ICommandHandler<T>cài đặt một cách nhất quán.


2
Wow - cảm ơn vì câu trả lời thấu đáo. Nếu tôi có thể upvote hai lần, tôi sẽ. Ở trên, bạn nói "... không có ý định cho phép một tập hợp các hoạt động trong cùng một bối cảnh, trong trường hợp đó lối sống thoáng qua là ổn ...". Bạn có ý nghĩa gì bởi "thoáng qua", cụ thể?
Andrew

14
@Andrew: 'Transient' là khái niệm Dependency Injection, có nghĩa là nếu một dịch vụ được cấu hình là tạm thời, một phiên bản mới của dịch vụ được tạo ra mỗi khi nó được đưa vào người tiêu dùng.
Steven

1
@ user981375: Đối với các thao tác CRUD, bạn có thể tạo chung CreateCommand<TEnity>và chung CreateCommandHandler<TEntity> : ICommandHandler<CreateCommand<TEntity>>(và thực hiện tương tự cho Cập nhật và Xóa và có một GetByIdQuery<TEntity>truy vấn duy nhất ). Tuy nhiên, bạn nên tự hỏi liệu mô hình này có phải là một sự trừu tượng hóa hữu ích cho các hoạt động CRUD hay không, hay liệu nó chỉ làm tăng thêm sự phức tạp. Tuy nhiên, bạn có thể được hưởng lợi từ khả năng dễ dàng thêm các mối quan tâm xuyên suốt (thông qua các nhà trang trí) bằng cách sử dụng mô hình này. Bạn sẽ phải cân nhắc những ưu và nhược điểm.
Steven

3
+1 Bạn có tin rằng tôi đã viết tất cả câu trả lời này trước khi thực sự đọc nó không? BTW IMO Tôi nghĩ rằng điều quan trọng đối với bạn là thảo luận về việc loại bỏ DbContext ở cuối (mặc dù thật tuyệt khi bạn không ở trong container)
Ruben Bartelink

1
Nhưng bạn không chuyển ngữ cảnh sang lớp trang trí, làm thế nào lớp được trang trí có thể hoạt động với cùng bối cảnh được truyền cho TransactionCommandHandlerDecorator? ví dụ nếu lớp được trang trí là InsertCommandHandlerlớp, làm thế nào nó có thể đăng ký thao tác chèn vào ngữ cảnh (DbContext trong EF)?
Masoud

35

Có hai khuyến nghị trái ngược nhau của microsoft và nhiều người sử dụng DbContexts theo cách hoàn toàn khác nhau.

  1. Một đề xuất là "Loại bỏ DbContexts ngay khi có thể thấy được" bởi vì có DbContext Alive chiếm các tài nguyên có giá trị như kết nối db, v.v ....
  2. Các tiểu bang khác cho rằng Một DbContext cho mỗi yêu cầu được đánh giá cao

Những điều này mâu thuẫn với nhau vì nếu Yêu cầu của bạn thực hiện nhiều việc không liên quan đến công cụ Db, thì DbContext của bạn sẽ được giữ mà không có lý do. Do đó, thật lãng phí khi giữ DbContext của bạn tồn tại trong khi yêu cầu của bạn chỉ chờ đợi những thứ ngẫu nhiên được thực hiện ...

Vì vậy, nhiều người tuân theo quy tắc 1 có DbContexts của họ bên trong "Mẫu lưu trữ" của họ và tạo Trường hợp mới cho mỗi Truy vấn cơ sở dữ liệu để X * DbContext mỗi Yêu cầu

Họ chỉ cần lấy dữ liệu của họ và xử lý bối cảnh càng sớm càng tốt. Điều này được NHIỀU người coi là một thực tế chấp nhận được. Mặc dù điều này có lợi ích khi chiếm tài nguyên db của bạn trong thời gian tối thiểu, nhưng rõ ràng nó hy sinh tất cả các đơn vị kẹo UnitOfWorkbộ nhớ đệm mà EF cung cấp.

Giữ cho DbContext duy trì một phiên bản đa mục đích tối đa hóa lợi ích của Bộ nhớ đệm nhưng vì DbContext không an toàn cho mỗi luồng và mỗi yêu cầu Web chạy trên luồng riêng của nó, DbContext mỗi Yêu cầu là thời gian dài nhất bạn có thể giữ.

Vì vậy, đề xuất nhóm của EF về việc sử dụng Bối cảnh 1 Db cho mỗi yêu cầu rõ ràng dựa trên thực tế là trong Ứng dụng web, UnitOfWork rất có thể sẽ nằm trong một yêu cầu và yêu cầu đó có một luồng. Vì vậy, một DbContext cho mỗi yêu cầu giống như lợi ích lý tưởng của UnitOfWork và bộ đệm.

Nhưng trong nhiều trường hợp điều này không đúng. Tôi coi việc ghi nhật ký một UnitOfWork riêng biệt do đó có một DbContext mới để ghi nhật ký yêu cầu sau trong các luồng không đồng bộ là hoàn toàn chấp nhận được

Vì vậy, Cuối cùng, nó chỉ ra rằng thời gian tồn tại của DbContext bị giới hạn ở hai tham số này. UnitOfWorkchủ đề


3
Nói một cách công bằng, các yêu cầu HTTP của bạn sẽ được hoàn thành khá nhanh (vài ms). Nếu chúng sẽ kéo dài hơn thế, thì bạn có thể muốn nghĩ về việc thực hiện một số xử lý nền với một cái gì đó giống như một lịch trình công việc bên ngoài để yêu cầu có thể trở lại ngay lập tức. Điều đó nói rằng, kiến ​​trúc của bạn không thực sự dựa vào HTTP. Nhìn chung, một câu trả lời tốt mặc dù.
nghiền nát

34

Không một câu trả lời nào ở đây thực sự trả lời câu hỏi. OP đã không hỏi về thiết kế DbContext đơn lẻ / mỗi ứng dụng, anh ấy đã hỏi về thiết kế yêu cầu trên mỗi trang web và những lợi ích tiềm năng nào có thể tồn tại.

Tôi sẽ tham khảo http://mehdi.me/ambient-dbcontext-in-ef6/ vì Mehdi là một tài nguyên tuyệt vời:

Hiệu suất có thể đạt được.

Mỗi phiên bản DbContext duy trì bộ đệm cấp đầu tiên của tất cả các thực thể mà nó tải từ cơ sở dữ liệu. Bất cứ khi nào bạn truy vấn một thực thể bằng khóa chính của nó, trước tiên DbContext sẽ cố gắng truy xuất nó từ bộ đệm cấp đầu tiên của nó trước khi mặc định truy vấn nó từ cơ sở dữ liệu. Tùy thuộc vào mẫu truy vấn dữ liệu của bạn, việc sử dụng lại cùng một DbContext trên nhiều giao dịch kinh doanh tuần tự có thể dẫn đến ít truy vấn cơ sở dữ liệu hơn được thực hiện nhờ vào bộ đệm cấp độ DbContext.

Nó cho phép tải nhanh.

Nếu dịch vụ của bạn trả về các thực thể liên tục (trái ngược với các mô hình xem trả về hoặc các loại DTO khác) và bạn muốn tận dụng việc lười tải trên các thực thể đó, thời gian tồn tại của đối tượng DbContext mà các thực thể đó được truy xuất phải vượt ra ngoài phạm vi của giao dịch kinh doanh. Nếu phương thức dịch vụ loại bỏ đối tượng DbContext mà nó đã sử dụng trước khi trả về, mọi nỗ lực đối với các thuộc tính tải chậm trên các thực thể được trả về sẽ không thành công (dù có sử dụng lười tải hay không thì đó là một cuộc tranh luận hoàn toàn khác mà chúng ta sẽ không tham gia đây). Trong ví dụ ứng dụng web của chúng tôi, tải lười biếng thường được sử dụng trong các phương thức hành động của bộ điều khiển trên các thực thể được trả về bởi một lớp dịch vụ riêng biệt. Trong trường hợp đó,

Hãy nhớ rằng có những khuyết điểm là tốt. Liên kết đó chứa nhiều tài nguyên khác để đọc về chủ đề này.

Chỉ đăng bài này trong trường hợp người khác vấp phải câu hỏi này và không bị cuốn hút vào những câu trả lời không thực sự giải quyết câu hỏi.


Liên kết tốt! Quản lý rõ ràng DBContext trông giống như cách tiếp cận an toàn nhất.
agssol

22

Tôi khá chắc chắn đó là vì DbContext hoàn toàn không an toàn. Vì vậy, chia sẻ điều này không bao giờ là một ý tưởng tốt.


Bạn có nghĩa là chia sẻ nó qua các yêu cầu HTTP không bao giờ là một ý tưởng tốt?
Andrew

2
Vâng Andrew đó là những gì anh ấy có nghĩa. Chia sẻ bối cảnh chỉ dành cho các ứng dụng máy tính để bàn duy nhất.
Elisabeth

10
Điều gì về việc chia sẻ bối cảnh cho một yêu cầu. Vì vậy, đối với một yêu cầu, chúng ta có thể có quyền truy cập vào các kho lưu trữ khác nhau và thực hiện giao dịch qua chúng bằng cách chia sẻ một và cùng một bối cảnh?
Lyubomir Velchev

16

Một điều không thực sự được đề cập trong câu hỏi hoặc cuộc thảo luận là thực tế là DbContext không thể hủy bỏ các thay đổi. Bạn có thể gửi các thay đổi, nhưng bạn không thể xóa cây thay đổi, vì vậy nếu bạn sử dụng bối cảnh theo yêu cầu, bạn sẽ không gặp may nếu bạn cần vứt bỏ các thay đổi vì bất kỳ lý do gì.

Cá nhân tôi tạo các phiên bản của DbContext khi cần - thường được gắn vào các thành phần kinh doanh có khả năng tạo lại bối cảnh nếu được yêu cầu. Bằng cách đó, tôi có quyền kiểm soát quá trình, thay vì bắt buộc phải có một trường hợp duy nhất. Tôi cũng không phải tạo DbContext tại mỗi lần khởi động bộ điều khiển bất kể nó có thực sự được sử dụng hay không. Sau đó, nếu tôi vẫn muốn có mỗi phiên bản yêu cầu, tôi có thể tạo chúng trong CTOR (thông qua DI hoặc thủ công) hoặc tạo chúng khi cần trong mỗi phương thức điều khiển. Cá nhân tôi thường sử dụng cách tiếp cận sau để tránh tạo ra các thể hiện DbContext khi chúng không thực sự cần thiết.

Nó phụ thuộc từ góc độ bạn nhìn vào nó quá. Đối với tôi, ví dụ yêu cầu chưa bao giờ có ý nghĩa. DbContext có thực sự thuộc về Yêu cầu http không? Về mặt hành vi đó là sai chỗ. Các thành phần kinh doanh của bạn nên tạo bối cảnh của bạn, không phải yêu cầu http. Sau đó, bạn có thể tạo hoặc vứt bỏ các thành phần kinh doanh của mình khi cần và không bao giờ lo lắng về thời gian tồn tại của bối cảnh.


1
Đây là một câu trả lời thú vị và tôi một phần đồng ý với bạn. Đối với tôi, DbContext không cần phải gắn với yêu cầu web, nhưng nó luôn được nhập vào một 'yêu cầu' như trong: 'giao dịch kinh doanh'. Và khi bạn kết hợp bối cảnh với một giao dịch kinh doanh, việc hủy bỏ thay đổi trở nên thực sự kỳ lạ. Nhưng không có nó trên ranh giới yêu cầu web không có nghĩa là các thành phần kinh doanh (BC) sẽ tạo ra bối cảnh; Tôi nghĩ đó không phải là trách nhiệm của họ. Thay vào đó, bạn có thể áp dụng phạm vi sử dụng trang trí xung quanh BC của bạn. Bằng cách này, bạn thậm chí có thể thay đổi phạm vi mà không cần thay đổi mã.
Steven

1
Trong trường hợp đó, tiêm vào đối tượng kinh doanh nên đối phó với quản lý trọn đời. Theo quan điểm của tôi, đối tượng kinh doanh sở hữu bối cảnh và như vậy sẽ kiểm soát trọn đời.
Rick Strahl

Tóm lại, ý của bạn là gì khi bạn nói "khả năng tái tạo bối cảnh nếu được yêu cầu"? bạn đang lăn khả năng rollback của riêng bạn? bạn có thể xây dựng một chút?
tntwyckoff

2
Cá nhân, tôi nghĩ rằng có một chút rắc rối khi buộc DbContext khi bắt đầu ở đó. Không có gì đảm bảo rằng bạn thậm chí cần phải nhấn cơ sở dữ liệu. Có thể bạn đang gọi một dịch vụ bên thứ 3 thay đổi trạng thái ở bên đó. Hoặc có thể bạn thực sự có 2 hoặc 3 cơ sở dữ liệu bạn đang làm việc cùng một lúc. Bạn sẽ không tạo ra một loạt DbContexts khi bắt đầu chỉ trong trường hợp bạn kết thúc việc sử dụng chúng. Doanh nghiệp biết dữ liệu mà nó đang làm việc, vì vậy nó thuộc về dữ liệu đó. Chỉ cần đặt một Giao diện khi bắt đầu nếu cần thiết. Tôi không nghĩ rằng tất cả các cuộc gọi cần một. Nó có tài nguyên.
Daniel Lorenz

Đó là câu hỏi liệu bạn có cho phép bộ chứa kiểm soát thời gian tồn tại của dbcontext hay không, sau đó kiểm soát thời gian tồn tại của các điều khiển cha, đôi khi là quá mức. Nói rằng nếu tôi muốn một đơn dịch vụ đơn giản được tiêm vào bộ điều khiển của mình thì tôi sẽ không thể sử dụng nội dung tiêm chích do theo ngữ nghĩa của mỗi yêu cầu.
davidcarr

10

Tôi đồng ý với ý kiến ​​trước đây. Thật tốt khi nói rằng, nếu bạn định chia sẻ DbContext trong ứng dụng chủ đề đơn, bạn sẽ cần thêm bộ nhớ. Ví dụ: ứng dụng web của tôi trên Azure (một ví dụ nhỏ thêm) cần thêm 150 MB bộ nhớ và tôi có khoảng 30 người dùng mỗi giờ. Ứng dụng chia sẻ DBContext trong Yêu cầu HTTP

Đây là hình ảnh ví dụ thực tế: ứng dụng đã được triển khai trong 12 giờ tối


Có thể ý tưởng là chia sẻ bối cảnh cho một yêu cầu. Nếu chúng ta truy cập các kho lưu trữ khác nhau và - Các lớp DBset và muốn các hoạt động với chúng là giao dịch thì đó là một giải pháp tốt. Hãy xem dự án nguồn mở mvcforum.com Tôi nghĩ rằng điều đó được thực hiện trong việc triển khai mẫu thiết kế Unit Of Work của họ.
Lyubomir Velchev

3

Điều tôi thích ở đây là nó sắp xếp đơn vị công việc (như người dùng nhìn thấy - tức là một trang gửi) với đơn vị công việc theo nghĩa ORM.

Do đó, bạn có thể thực hiện giao dịch gửi toàn bộ trang mà bạn không thể thực hiện nếu bạn tiếp xúc với các phương thức CRUD với mỗi phương thức tạo bối cảnh mới.


3

Một lý do khác cho việc không sử dụng DbContext đơn lẻ, ngay cả trong một ứng dụng người dùng đơn luồng, là do mẫu bản đồ nhận dạng mà nó sử dụng. Điều đó có nghĩa là mỗi khi bạn truy xuất dữ liệu bằng truy vấn hoặc theo id, nó sẽ giữ các thể hiện thực thể được truy xuất trong bộ đệm. Lần sau khi bạn truy xuất cùng một thực thể, nó sẽ cung cấp cho bạn phiên bản được lưu trong bộ nhớ cache của thực thể, nếu có, với bất kỳ sửa đổi nào bạn đã thực hiện trong cùng một phiên. Điều này là cần thiết để phương thức SaveChanges không kết thúc với nhiều phiên bản thực thể khác nhau của cùng một bản ghi cơ sở dữ liệu; mặt khác, bối cảnh sẽ phải bằng cách nào đó hợp nhất dữ liệu từ tất cả các thực thể đó.

Lý do là một vấn đề là một DbContext đơn lẻ có thể trở thành một quả bom hẹn giờ cuối cùng có thể lưu trữ toàn bộ cơ sở dữ liệu + chi phí hoạt động của các đối tượng .NET trong bộ nhớ.

Có nhiều cách xung quanh hành vi này bằng cách chỉ sử dụng truy vấn Linq với .NoTracking()phương thức mở rộng. Ngoài ra những ngày này PC có rất nhiều RAM. Nhưng thường thì đó không phải là hành vi mong muốn.


Điều này là chính xác, nhưng bạn phải cho rằng Garbage Collector sẽ hoạt động, làm cho vấn đề này trở nên ảo hơn thực tế.
tocqueville

3
Trình thu gom rác sẽ không thu thập bất kỳ trường hợp đối tượng nào được giữ bởi một đối tượng tĩnh / đơn lẻ đang hoạt động. Họ sẽ kết thúc trong gen 2 của heap.
Dmitry S.

1

Một vấn đề khác cần chú ý với Entity Framework cụ thể là khi sử dụng kết hợp tạo các thực thể mới, tải chậm, sau đó sử dụng các thực thể mới đó (từ cùng một bối cảnh). Nếu bạn không sử dụng IDbSet.Create (so với chỉ mới), thì việc tải nhanh trên thực thể đó không hoạt động khi nó được lấy ra khỏi bối cảnh mà nó được tạo ra. Ví dụ:

 public class Foo {
     public string Id {get; set; }
     public string BarId {get; set; }
     // lazy loaded relationship to bar
     public virtual Bar Bar { get; set;}
 }
 var foo = new Foo {
     Id = "foo id"
     BarId = "some existing bar id"
 };
 dbContext.Set<Foo>().Add(foo);
 dbContext.SaveChanges();

 // some other code, using the same context
 var foo = dbContext.Set<Foo>().Find("foo id");
 var barProp = foo.Bar.SomeBarProp; // fails with null reference even though we have BarId set.
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.