Khi nào tôi nên tạo một DbContext () mới


83

Tôi hiện đang sử dụng một cái DbContexttương tự như thế này:

namespace Models
{
    public class ContextDB: DbContext
    {

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

        public ContextDB()
        {

        }
    }
}

Sau đó, tôi đang sử dụng dòng sau ở đầu TẤT CẢ các bộ điều khiển của tôi cần quyền truy cập vào cơ sở dữ liệu. Tôi cũng đang sử dụng nó trong Lớp UserRepository chứa tất cả các phương thức liên quan đến người dùng (chẳng hạn như đưa người dùng hoạt động, kiểm tra vai trò của anh ta, v.v.):

ContextDB _db = new ContextDB();

Suy nghĩ về điều này .. có những sự cố khi một khách truy cập có thể có nhiều DbContexts đang hoạt động .. tức là. nếu anh ấy truy cập một bộ điều khiển sử dụng UserRepository .. đây có thể không phải là ý tưởng tốt nhất và tôi có một vài câu hỏi về nó

  1. Khi nào tôi nên tạo một DbContext mới / tôi nên có một ngữ cảnh toàn cục mà tôi chuyển qua?
  2. Tôi có thể có một Ngữ cảnh toàn cầu mà tôi sử dụng lại ở mọi nơi không?
  3. Điều này có gây ra hiệu suất ăn khách không?
  4. Làm thế nào những người khác đang làm điều này?

Tôi đã phải cờ như trùng lặp - xem stackoverflow.com/questions/12871666/linq-and-datacontext cho một cuộc thảo luận tốt đẹp
SAJ14SAJ

2
Trong trường hợp này, tôi sẽ sử dụng phương thức tiêm phụ thuộc (ví dụ: Ninject), vì vậy nó sẽ tạo một tệp DbContextcho mỗi yêu cầu. Tôi cũng sẽ tạo lớp dịch vụ. Kiểm tra SO câu hỏi & câu trả lời này
Zbigniew

Câu trả lời:


82

Tôi sử dụng bộ điều khiển cơ sở để lộ thuộc DataBasetính mà bộ điều khiển dẫn xuất có thể truy cập.

public abstract class BaseController : Controller
{
    public BaseController()
    {
        Database = new DatabaseContext();
    }

    protected DatabaseContext Database { get; set; }

    protected override void Dispose(bool disposing)
    {
        Database.Dispose();
        base.Dispose(disposing);
    }
}

Tất cả các bộ điều khiển trong ứng dụng của tôi bắt nguồn từ BaseControllervà được sử dụng như sau:

public class UserController : BaseController
{
    [HttpGet]
    public ActionResult Index()
    {
        return View(Database.Users.OrderBy(p => p.Name).ToList());
    }
}

Bây giờ để trả lời câu hỏi của bạn:

Khi nào tôi nên tạo một DbContext mới / tôi nên có một ngữ cảnh toàn cục mà tôi chuyển qua?

Ngữ cảnh phải được tạo theo yêu cầu. Tạo bối cảnh, làm những gì bạn cần làm với nó sau đó loại bỏ nó. Với giải pháp lớp cơ sở mà tôi sử dụng, bạn chỉ phải lo lắng về việc sử dụng ngữ cảnh.

Không thử và có bối cảnh chung (đây không phải là cách các ứng dụng web hoạt động).

Tôi có thể có một Ngữ cảnh toàn cầu mà tôi sử dụng lại ở mọi nơi không?

Không, nếu bạn giữ một bối cảnh xung quanh nó sẽ theo dõi tất cả các cập nhật, bổ sung, xóa, v.v. và điều này sẽ làm chậm ứng dụng của bạn và thậm chí có thể gây ra một số lỗi khá nhỏ xuất hiện trong ứng dụng của bạn.

Bạn có thể nên chọn hiển thị kho lưu trữ của mình hoặc Bối cảnh của bạn cho bộ điều khiển của bạn nhưng không phải cả hai. Việc có hai ngữ cảnh được truy cập từ cùng một phương pháp sẽ dẫn đến lỗi nếu cả hai đều có ý tưởng khác nhau về trạng thái hiện tại của ứng dụng.

Cá nhân, tôi thích hiển thị DbContexttrực tiếp vì hầu hết các ví dụ về kho lưu trữ mà tôi đã thấy chỉ đơn giản là kết thúc như những lớp bọc mỏng xung quanh DbContext.

Điều này có gây ra hiệu suất ăn khách không?

Lần đầu tiên a DbContextđược tạo khá tốn kém nhưng khi điều này đã được thực hiện, rất nhiều thông tin sẽ được lưu vào bộ nhớ đệm để các lần khởi tạo tiếp theo nhanh hơn rất nhiều. bạn có nhiều khả năng gặp các vấn đề về hiệu suất do giữ một ngữ cảnh xung quanh hơn là do bạn khởi tạo một ngữ cảnh mỗi khi bạn cần truy cập vào cơ sở dữ liệu của mình.

Làm thế nào những người khác đang làm điều này?

Nó phụ thuộc.

Một số người thích sử dụng khung phụ thuộc để chuyển một phiên bản cụ thể của ngữ cảnh đến bộ điều khiển của họ khi nó được tạo. Cả hai lựa chọn đều ổn. Của tôi phù hợp hơn cho một ứng dụng quy mô nhỏ mà bạn biết rằng cơ sở dữ liệu cụ thể đang được sử dụng sẽ không thay đổi.

một số người có thể tranh luận rằng bạn không thể biết điều này và đó là lý do tại sao phương pháp tiêm phụ thuộc tốt hơn vì nó làm cho ứng dụng của bạn thay đổi linh hoạt hơn. Ý kiến ​​của tôi về điều này là nó có thể sẽ không thay đổi (SQL server & Entity Framework hầu như không bị che khuất) và tốt nhất tôi nên dành thời gian để viết mã dành riêng cho ứng dụng của mình.


4
Tôi không có gì để thêm vào điều này, ngoài việc rất vui khi thấy một số sở thích của riêng tôi được phản ánh trong các bài đăng của người khác. Tôi chưa bao giờ thực sự hiểu các ví dụ về kho lưu trữ dẫn đến việc một lớp khác được thêm vào DbContext mà không thực sự thêm bất kỳ thứ gì và tôi là người thích tạo một lớp cơ sở để lộ DbContext được bảo vệ.
dark_perfect

4
Tôi muốn nói thêm rằng câu trả lời này thật tuyệt nhưng hiện đã lỗi thời với việc phát hành EF6 tự động loại bỏ ngữ cảnh sau mỗi lần sử dụng. Do đó, có những trường hợp trong đó việc tạo ngữ cảnh cho mỗi phiên (toàn cầu) là được. Nếu bạn đang sử dụng EF6 để kết nối với Thủ tục đã lưu trữ và việc khóa dữ liệu không phải là vấn đề thì lý tưởng nhất là tạo ngữ cảnh một lần trong bộ điều khiển cơ sở và có tất cả các bộ điều khiển yêu cầu quyền truy cập cơ sở dữ liệu kế thừa từ cơ sở. Câu trả lời chính xác nhất là bạn cần bắt kịp công nghệ và áp dụng khuôn mẫu chính xác cho kiến ​​trúc của mình.
Atters

Điều gì sẽ xảy ra nếu tôi muốn gọi một số phương thức mô hình từ hành động của bộ điều khiển sử dụng DbContext? Làm cách nào để chuyển nó sang phương thức mô hình?
RMazitov

Sau đó, bạn đang thực hiện các truy vấn cơ sở dữ liệu và logic nghiệp vụ trong bộ điều khiển mà chúng không thuộc về. Bạn nên tạo một dịch vụ được gọi từ bộ điều khiển của bạn. Tức là UserController của bạn gọi một phương thức trong UserService của bạn.
Fred

@Fred, yes, và làm cách nào để tôi chuyển DbContext tới UserService, bằng tham số đơn giản cho hàm hay không?
RMazitov

10

Tôi cố gắng trả lời từ kinh nghiệm của riêng tôi.

1. Khi nào tôi nên tạo một DbContext mới / tôi nên có một ngữ cảnh toàn cục mà tôi chuyển qua?

Context nên được đưa vào bằng cách tiêm phụ thuộc và không nên tự khởi tạo. Thực tiễn tốt nhất là tạo nó như một dịch vụ có phạm vi bằng cách tiêm phụ thuộc. (Xem câu trả lời của tôi cho Câu hỏi 4)

Cũng vui lòng xem xét sử dụng cấu trúc ứng dụng nhiều lớp thích hợp như Controller> BusinessLogic> Repository. Trong trường hợp này sẽ không xảy ra trường hợp bộ điều khiển của bạn nhận được ngữ cảnh db mà thay vào đó là kho lưu trữ. Việc đưa / khởi tạo ngữ cảnh db trong bộ điều khiển cho tôi biết rằng kiến ​​trúc ứng dụng của bạn kết hợp nhiều trách nhiệm vào một nơi, điều này - trong mọi trường hợp - tôi không thể khuyến nghị.

2. Tôi có thể có một Ngữ cảnh toàn cầu mà tôi sử dụng lại ở mọi nơi không?

Có, bạn có thể nhưng câu hỏi nên là " Tôi có nên có ..." -> KHÔNG. Ngữ cảnh được sử dụng theo yêu cầu thay đổi kho lưu trữ của bạn và sau đó lại biến mất.

3. Điều này có gây ra hit hiệu suất không?

Có nó đúng vì DBContext đơn giản không được tạo ra để mang tính toàn cầu. Nó lưu trữ tất cả dữ liệu đã được nhập hoặc truy vấn vào nó cho đến khi nó bị phá hủy. Điều đó có nghĩa là bối cảnh toàn cầu sẽ ngày càng lớn hơn, các hoạt động trên nó sẽ ngày càng chậm hơn cho đến khi bạn thoát khỏi ngoại lệ bộ nhớ hoặc bạn chết vì tuổi tác vì tất cả đều chậm lại.

Bạn cũng sẽ nhận được ngoại lệ và nhiều lỗi khi nhiều luồng truy cập cùng một ngữ cảnh cùng một lúc.

4. Mọi người khác làm việc này như thế nào?

DBContext được đưa vào thông qua việc tiêm phụ thuộc bởi một nhà máy; phạm vi:

services.AddDbContext<UserDbContext>(o => o.UseSqlServer(this.settings.DatabaseOptions.UserDBConnectionString));

Tôi hy vọng câu trả lời của tôi nơi giúp đỡ.


Điều gì sẽ xảy ra nếu tôi muốn sử dụng DBContext trong Startup.cs trong chính phương thức ConfigureServices? Tôi có Phần mềm trung gian OICD, nơi tôi cần truy cập vào DB, nhưng tôi không thể truy cập DBContext hoặc tôi không biết làm thế nào?
bbrinck

Trong phương thức configServices, DBContext của bạn có thể không khả dụng vì bạn đã định cấu hình nó ở đó. ServiceProvider mà từ đó bạn thực sự nhận được DBContext của mình trong thời gian chạy, trước tiên sẽ có sẵn trong phương thức Configure () (Không phải "ConfigureServices"!). Tại đó, bạn có thể yêu cầu Context bằng ApplicationBuilder bằng cách gõ "app.ApplicationServices.GetRequiredService <MyDbContext> ();" (Thay thế MyDbContext bằng tên lớp của ngữ cảnh của riêng bạn).
Ravior

Nếu Bộ điều khiển được đưa vào Kho lưu trữ, làm cách nào để họ (Bộ điều khiển) lưu các thay đổi? Giả sử tôi đang gửi một yêu cầu POST để chèn một cái gì đó vào cơ sở dữ liệu, bộ điều khiển xử lý yêu cầu, sử dụng kho lưu trữ để thêm đối tượng mới được tạo .. sau đó thì sao? Ai là người duy trì những thay đổi?
MMalke

@MMalke Kho lưu trữ thực hiện điều đó. Nó thường có một chức năng "SaveData (Data myData)" và sau đó Kho lưu trữ tạo một phiên bản của Entity-Framework anh em của nó cho Data-Class, thêm nó vào dbset theo trong dbcontext và sau đó nó gọi SaveChanges (). Điều duy nhất mà bộ điều khiển làm là gọi hàm Repository.SaveData (myData).
Ravior

1

Ngay bây giờ tôi đang thử cách tiếp cận này, cách này tránh tạo bối cảnh khi bạn gọi các hành động không sử dụng nó.

public abstract class BaseController : Controller
{
    public BaseController() { }

    private DatabaseContext _database;
    protected DatabaseContext Database
    {
        get
        {
            if (_database == null)
                _database = new DatabaseContext();
            return _database;
        }
    }

    protected override void Dispose(bool disposing)
    {
        if (_database != null)
            _database.Dispose();
        base.Dispose(disposing);
    }
}

2
Mặc dù không có gì sai với cách tiếp cận của bạn, tôi tin rằng ngữ cảnh Entity Framework sẽ làm mọi thứ theo kiểu lười biếng và không có công việc thực sự nào được thực hiện cho đến khi bạn thực sự truy cập vào cơ sở dữ liệu. Vì vậy, chi phí tạo ngữ cảnh EF sẽ rất nhỏ.
Martin Liversage

Tôi đã thực hiện một số nghiên cứu và có vẻ như điều đó là chính xác. Tôi đã thực hiện một số bài kiểm tra đơn giản bằng cách so sánh những gì GC.GetTotalMemory()trả về (không hoàn hảo nhưng đó là những gì tôi tìm thấy) và sự khác biệt không bao giờ lớn hơn 8Kb.
Andrew

0

Đây rõ ràng là một câu hỏi cũ hơn nhưng nếu bạn sử dụng DI, bạn có thể làm điều gì đó như thế này và phạm vi tất cả các đối tượng của bạn trong suốt thời gian của yêu cầu

 public class UnitOfWorkAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            var context = IoC.CurrentNestedContainer.GetInstance<DatabaseContext>();
            context.BeginTransaction();
        }

        public override void OnActionExecuted(HttpActionExecutedContext actionContext)
        {
            var context = IoC.CurrentNestedContainer.GetInstance<DatabaseContext>();
            context.CloseTransaction(actionContext.Exception);
        }
    }

0

Bạn nên loại bỏ ngữ cảnh ngay sau mỗi thao tác Save (). Nếu không, mỗi lần Lưu tiếp theo sẽ lâu hơn. Tôi đã có một dự án tạo và lưu các thực thể cơ sở dữ liệu phức tạp trong một chu kỳ. Tôi ngạc nhiên là thao tác này nhanh hơn gấp ba lần sau khi tôi chuyển "using (var ctx = new MyContext ()) {...}" trong chu trình.

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.