새 DbContext ()는 언제 만들어야합니까?


83

나는 현재 다음 DbContext과 비슷한 것을 사용하고 있습니다 .

namespace Models
{
    public class ContextDB: DbContext
    {

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

        public ContextDB()
        {

        }
    }
}

그런 다음 데이터베이스에 액세스해야하는 모든 컨트롤러 의 맨 위에 다음 줄을 사용 하고 있습니다. Im은 또한 사용자와 관련된 모든 메소드를 포함하는 UserRepository 클래스에서 사용합니다 (예 : 활성 사용자 가져 오기, 그가 가지고있는 역할 확인 등).

ContextDB _db = new ContextDB();

이것을 생각하면 .. 한 방문자가 여러 DbContext를 활성화 할 수있는 경우가 있습니다. 즉. 그가 UserRepository를 사용하는 컨트롤러를 방문한다면 .. 이것은 최선의 아이디어가 아닐 수 있으며 이에 대해 몇 가지 질문이 있습니다.

  1. 새 DbContext를 언제 만들어야하나요? / 내가 전달하는 하나의 전역 컨텍스트가 있어야하나요?
  2. 모든 위치에서 재사용 할 수있는 하나의 글로벌 컨텍스트를 가질 수 있습니까?
  3. 이로 인해 성능 저하가 발생합니까?
  4. 다른 사람들은 어떻게 이것을하고 있습니까?

중복으로 플래그를 지정해야했습니다 . 꽤 좋은 토론 은 stackoverflow.com/questions/12871666/linq-and-datacontext 참조
SAJ14SAJ

2
이 경우 종속성 주입 (예 : Ninject)을 사용하므로 DbContext요청 당 하나씩 생성 됩니다. 서비스 레이어도 만들 것입니다. 확인 이 SO 질문 및 답변
즈비그뉴

답변:


82

DataBase파생 컨트롤러가 액세스 할 수 있는 속성을 노출하는 기본 컨트롤러를 사용합니다 .

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);
    }
}

내 응용 프로그램의 모든 컨트롤러는 다음에서 파생되며 다음 BaseController과 같이 사용됩니다.

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

이제 질문에 답하십시오.

새 DbContext를 언제 만들어야하나요? / 내가 전달하는 하나의 전역 컨텍스트가 있어야하나요?

컨텍스트는 요청별로 생성되어야합니다. 컨텍스트를 만들고 필요한 작업을 수행 한 다음 제거하십시오. 기본 클래스 솔루션을 사용하면 컨텍스트 사용에 대해서만 걱정하면됩니다.

글로벌 컨텍스트를 시도하지 마십시오 (웹 애플리케이션의 작동 방식이 아님).

모든 위치에서 재사용 할 수있는 하나의 글로벌 컨텍스트를 가질 수 있습니까?

아니요, 컨텍스트를 유지하면 모든 업데이트, 추가, 삭제 등을 추적 할 수 있으며 이로 인해 애플리케이션 속도가 느려지고 애플리케이션에 매우 미묘한 버그가 나타날 수도 있습니다.

저장소 또는 컨텍스트를 컨트롤러에 노출하도록 선택해야 하지만 둘다는 아닙니다. 동일한 메서드에서 두 개의 컨텍스트에 액세스하면 둘 다 애플리케이션의 현재 상태에 대해 서로 다른 아이디어를 가지고있는 경우 버그가 발생합니다.

개인적으로 나는 내가 DbContext본 대부분의 저장소 예제가 DbContext어쨌든 얇은 래퍼로 끝날 때 직접 노출하는 것을 선호 합니다.

이로 인해 성능 저하가 발생합니까?

a를 처음 DbContext만들 때는 비용이 많이 들지만 일단 이렇게되면 많은 정보가 캐시되어 후속 인스턴스화가 훨씬 빨라집니다. 데이터베이스에 액세스해야 할 때마다 인스턴스화하는 것보다 컨텍스트를 유지함으로써 성능 문제를 볼 가능성이 더 큽니다.

다른 사람들은 어떻게 이것을하고 있습니까?

때에 따라 다르지.

어떤 사람들은 생성 될 때 자신의 컨텍스트의 구체적인 인스턴스를 컨트롤러에 전달하기 위해 종속성 주입 프레임 워크를 사용하는 것을 선호합니다. 두 옵션 모두 괜찮습니다. Mine은 사용중인 특정 데이터베이스가 변경되지 않을 것이라는 것을 알고있는 소규모 애플리케이션에 더 적합합니다.

어떤 사람들은 당신 이 이것을 알 수 없다고 주장 할 수 있으며 , 그것이 당신의 애플리케이션을 변화에 더 탄력적으로 만들어주기 때문에 의존성 주입 방법이 더 나은 이유입니다. 이에 대한 내 의견은 아마도 변경되지 않을 것이며 (SQL 서버 및 Entity Framework는 거의 모호하지 않음) 내 응용 프로그램에 맞는 코드를 작성하는 데 시간을 보내는 것이 가장 좋습니다.


4
나는 이것에 추가 할 것이 없다. 다른 사람들의 포스트에 내 자신의 선호도를 반영하는 것을 보는 것이 좋다. 나는 실제로 아무것도 추가하지 않고 DbContext에 또 다른 레이어를 추가하는 리포지토리 예제를 실제로 이해 한 적이 없으며 보호 된 DbContext를 노출하는 기본 클래스를 만드는 팬입니다.
dark_perfect 2013 년

4
이 답변은 훌륭하지만 이제는 사용할 때마다 컨텍스트를 자동으로 처리하는 EF6의 출시로 구식이라는 것을 추가하고 싶었습니다. 따라서 세션 당 (글로벌) 컨텍스트를 만드는 것이 괜찮은 시나리오가 있습니다. 저장 프로 시저에 대한 연결에 EF6을 사용하고 데이터 잠금이 문제가되지 않는 경우 기본 컨트롤러에서 컨텍스트를 한 번 만들고 데이터베이스 액세스가 필요한 모든 컨트롤러가 기본에서 상속되도록하는 것이 이상적 일 수 있습니다. 가장 정답은 기술을 파악하고 아키텍처에 올바른 패턴을 적용해야한다는 것입니다.
Atters

DbContext를 사용하는 컨트롤러 작업에서 일부 모델 메서드를 호출하려면 어떻게해야합니까? 모델 메서드에 어떻게 전달할 수 있습니까?
RMazitov

그런 다음 소속되지 않은 컨트롤러에서 데이터베이스 쿼리 및 비즈니스 로직을 수행합니다. 컨트롤러에서 호출되는 서비스를 만들어야합니다. 즉, UserController는 UserService에서 메서드를 호출합니다.
Fred

@Fred, 예, DbContext를 UserService에 간단한 매개 변수로 함수 또는 아니요로 전달하는 방법은 무엇입니까?
RMazitov

10

나는 내 경험으로 대답하려고 노력한다.

1. 언제 새로운 DbContext를 만들어야합니까? / 내가 전달하는 하나의 글로벌 컨텍스트가 있어야합니까?

컨텍스트는 종속성 주입에 의해 주입되어야하며 직접 인스턴스화해서는 안됩니다. 모범 사례는 종속성 주입에 의해 범위가 지정된 서비스로 생성되도록하는 것입니다. (질문 4에 대한 내 답변 참조)

Controller> BusinessLogic> Repository와 같은 적절한 계층화 된 애플리케이션 구조를 사용하는 것도 고려하십시오. 이 경우 컨트롤러가 db-context가 아니라 리포지토리를 대신받는 경우가됩니다. 컨트롤러에서 db-context를 주입 / 인스턴스화하면 애플리케이션 아키텍처가 한곳에서 많은 책임을 혼합한다는 것을 알 수 있습니다. 어떤 상황에서도 권장 할 수 없습니다.

2. 모든 위치에서 재사용 할 수있는 하나의 글로벌 컨텍스트를 가질 수 있습니까?

예 당신은 할 수 있습니다 만 문제는해야한다 " 해야 내가 가지고 ..."-> NO. 컨텍스트는 리포지토리를 변경 한 다음 다시 삭제하기 위해 요청마다 사용됩니다.

3. 이로 인해 성능 저하가 발생합니까?

예, DBContext가 단순히 글로벌이 아니기 때문에 그렇습니다. 파괴 될 때까지 입력되거나 쿼리 된 모든 데이터를 저장합니다. 즉, 전역 컨텍스트가 점점 더 커지고, 메모리 부족 예외가 발생하거나 모두 크롤링 속도가 느려 나이가들 때까지 작업이 느려지고 느려집니다.

또한 여러 스레드가 한 번에 동일한 컨텍스트에 액세스 할 때 예외와 많은 오류가 발생합니다.

4. 다른 사람들은 어떻게 이것을하고 있습니까?

팩토리에 의한 의존성 주입을 통해 주입 된 DBContext; 범위 :

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

나는 내 대답이 도움이되기를 바랍니다.


ConfigureServices 메서드 자체의 Startup.cs에서 DBContext를 사용하려면 어떻게해야합니까? DB에 액세스해야하는 OICD 미들웨어가 있지만 DBContext에 액세스 할 수 없거나 방법을 모릅니다.
bbrinck

configureServices 메서드에서 DBContext를 구성했기 때문에 사용할 수 없습니다. 런타임에 실제로 DBContext를 가져 오는 ServiceProvider는 먼저 Configure () ( "ConfigureServices"가 아닙니다!) 메서드에서 사용할 수 있습니다. 여기에서 "app.ApplicationServices.GetRequiredService <MyDbContext> ();"를 입력하여 ApplicationBuilder를 사용하여 컨텍스트를 요청할 수 있습니다. (MyDbContext를 자신의 컨텍스트의 클래스 이름으로 바꿉니다).
Ravior

컨트롤러에 리포지토리가 주입되면 컨트롤러 (컨트롤러)가 변경 사항을 어떻게 저장합니까? 데이터베이스에 무언가를 삽입하기 위해 POST 요청을 보내고 컨트롤러가 요청을 처리하고 저장소를 사용하여 새로 생성 된 객체 ..를 추가한다고 가정 해 보겠습니다. 누가 변화를 지속합니까?
MMalke

@MMalke Repository가 그렇게합니다. 일반적으로 "SaveData (Data myData)"함수가 있고 Repository는 Data-Class에 대한 Entity-Framework 형제 인스턴스를 만들고 dbcontext의 해당 dbset에 추가 한 다음 SaveChanges ()를 호출합니다. 컨트롤러가하는 유일한 일은 Repository.SaveData (myData) 함수를 호출하는 것입니다.
Ravior

1

지금은 사용하지 않는 작업을 호출 할 때 컨텍스트를 인스턴스화하지 않는이 접근 방식을 시도하고 있습니다.

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
귀하의 접근 방식에는 아무런 문제가 없지만 Entity Framework 컨텍스트가 모든 것을 게으른 방식으로 수행하고 실제로 데이터베이스에 액세스 할 때까지 실제 작업이 수행되지 않는다고 생각합니다. 따라서 EF 컨텍스트를 만드는 오버 헤드는 매우 적어야합니다.
Martin Liversage 2015

나는 약간의 조사를했고 그것이 옳은 것 같습니다. GC.GetTotalMemory()반환 된 값 (완벽하지는 않지만 내가 찾은 것) 을 비교하여 간단한 테스트를 수행했으며 그 차이는 8Kb보다 크지 않았습니다.
Andrew

0

이것은 분명히 오래된 질문이지만 DI를 사용하는 경우 이와 같은 작업을 수행하고 요청 수명 동안 모든 객체의 범위를 지정할 수 있습니다.

 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

각 Save () 작업 직후 컨텍스트를 삭제해야합니다. 그렇지 않으면 이후의 각 저장이 더 오래 걸립니다. 복잡한 데이터베이스 엔터티를 주기적으로 만들고 저장하는 프로젝트가있었습니다. 놀랍게도 사이클 내에서 "using (var ctx = new MyContext ()) {...}"을 이동 한 후 작업이 3 배 빨라졌습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.