MVC, EF - экземпляр одиночного объекта DataContext Per-Web-Request в Unity

У меня есть веб-приложение MVC 3, в котором я использую Entity Framework для доступа к данным. Кроме того, я просто использовал шаблон репозитория, где, например, все, что связано с продуктом, обрабатывается в "ProductRepository" и все, что связано с пользователем, обрабатывается в "UserRepository".

Таким образом, я использую контейнер UNITY, чтобы создать одноэлементный экземпляр DataContext, который я вставляю в каждый из репозиториев. Быстрый поиск в Google, и все рекомендуют НЕ использовать одноэлементный экземпляр DataContext, так как это может привести к некоторым утечкам памяти в будущем.

Итак, вдохновленный этим сообщением, создание одноэлементного экземпляра DataContext для каждого веб-запроса является ответом (пожалуйста, поправьте меня, если я ошибаюсь!)

http://blogs.microsoft.co.il/blogs /gilf/archive/2010/05/18/how-to-manage-objectcontext-per-request-in-asp-net.aspx

Однако UNITY не поддерживает диспетчер времени жизни «для веб-запросов». Но можно реализовать свой собственный диспетчер времени жизни, который сделает это за вас. Фактически, это обсуждается в этой публикации:

Контекст синглтона на вызов (веб-запрос) в Unity

Вопрос в том, что теперь я реализовал настраиваемый менеджер времени жизни, как описано в вышеупомянутом сообщении, но я не уверен, что это способ сделать это. Мне также интересно, где находится экземпляр datacontext в предоставленном решении? Я что-то упускаю?

Есть ли на самом деле лучший способ решить мою «проблему»?

Спасибо!

** Добавлена ​​информация о моей реализации **

Ниже приведены фрагменты из моего Global. asax, Контроллер и Репозиторий. Это дает четкое представление о моей реализации.

Global.asax

  var container = new UnityContainer();
            container
                .RegisterType(new ContainerControlledLifetimeManager())
                .RegisterType(new ContainerControlledLifetimeManager())
                .RegisterType(new PerResolveLifetimeManager(), dbConnectionString)

Контроллер

private ProductsRepository _productsRepository;
private CategoryRepository _categoryRepository;

public ProductsController(ProductsRepository productsRepository, CategoryRepository categoryRepository)
{
   _productsRepository = productsRepository;
   _categoryRepository = categoryRepository;
}

public ActionResult Index()
{
   ProductCategory category = _categoryRepository.GetProductCategory(categoryId);
   . 
   . 
   . 
}

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

Репозиторий продукта

public class ProductsRepository : IDisposable
{

private MyEntities _db;

public ProductsRepository(MyEntities db)
{
    _db = db;
}

public Product GetProduct(Guid productId)
{
    return _db.Product.Where(x => x.ID == productId).FirstOrDefault();
}

public void Dispose()
{
    this._db.Dispose();
}

Завод контроллера

public class UnityControllerFactory : DefaultControllerFactory
{
    IUnityContainer _container;

    public UnityControllerFactory(IUnityContainer container)
    {
        _container = container;
    }

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
        {
            throw new HttpException(404, String.Format("The controller for path '{0}' could not be found" +
                "or it does not implement IController.",
                 requestContext.HttpContext.Request.Path));
        }

        return _container.Resolve(controllerType) as IController;
    }

}

Дополнительная информация Я реализовал собственный менеджер времени жизни, как описано в сообщении выше, но я не уверен, что это способ сделать это. Мне также интересно, где находится экземпляр datacontext в предоставленном решении? Я что-то упускаю?

Есть ли на самом деле лучший способ решить мою «проблему»?

Спасибо!

** Добавлена ​​информация о моей реализации **

Ниже приведены фрагменты из моего Global. asax, Контроллер и Репозиторий. Это дает четкое представление о моей реализации.

Global.asax

  var container = new UnityContainer();
            container
                .RegisterType(new ContainerControlledLifetimeManager())
                .RegisterType(new ContainerControlledLifetimeManager())
                .RegisterType(new PerResolveLifetimeManager(), dbConnectionString)

Контроллер

private ProductsRepository _productsRepository;
private CategoryRepository _categoryRepository;

public ProductsController(ProductsRepository productsRepository, CategoryRepository categoryRepository)
{
   _productsRepository = productsRepository;
   _categoryRepository = categoryRepository;
}

public ActionResult Index()
{
   ProductCategory category = _categoryRepository.GetProductCategory(categoryId);
   . 
   . 
   . 
}

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

Репозиторий продукта

public class ProductsRepository : IDisposable
{

private MyEntities _db;

public ProductsRepository(MyEntities db)
{
    _db = db;
}

public Product GetProduct(Guid productId)
{
    return _db.Product.Where(x => x.ID == productId).FirstOrDefault();
}

public void Dispose()
{
    this._db.Dispose();
}

Завод контроллера

public class UnityControllerFactory : DefaultControllerFactory
{
    IUnityContainer _container;

    public UnityControllerFactory(IUnityContainer container)
    {
        _container = container;
    }

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
        {
            throw new HttpException(404, String.Format("The controller for path '{0}' could not be found" +
                "or it does not implement IController.",
                 requestContext.HttpContext.Request.Path));
        }

        return _container.Resolve(controllerType) as IController;
    }

}

Дополнительная информация Я реализовал собственный менеджер времени жизни, как описано в сообщении выше, но я не уверен, что это способ сделать это. Мне также интересно, где находится экземпляр datacontext в предоставленном решении? Я что-то упускаю?

Есть ли на самом деле лучший способ решить мою «проблему»?

Спасибо!

** Добавлена ​​информация о моей реализации **

Ниже приведены фрагменты из моего Global. asax, Контроллер и Репозиторий. Это дает четкое представление о моей реализации.

Global.asax

  var container = new UnityContainer();
            container
                .RegisterType(new ContainerControlledLifetimeManager())
                .RegisterType(new ContainerControlledLifetimeManager())
                .RegisterType(new PerResolveLifetimeManager(), dbConnectionString)

Контроллер

private ProductsRepository _productsRepository;
private CategoryRepository _categoryRepository;

public ProductsController(ProductsRepository productsRepository, CategoryRepository categoryRepository)
{
   _productsRepository = productsRepository;
   _categoryRepository = categoryRepository;
}

public ActionResult Index()
{
   ProductCategory category = _categoryRepository.GetProductCategory(categoryId);
   . 
   . 
   . 
}

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

Репозиторий продукта

public class ProductsRepository : IDisposable
{

private MyEntities _db;

public ProductsRepository(MyEntities db)
{
    _db = db;
}

public Product GetProduct(Guid productId)
{
    return _db.Product.Where(x => x.ID == productId).FirstOrDefault();
}

public void Dispose()
{
    this._db.Dispose();
}

Завод контроллера

public class UnityControllerFactory : DefaultControllerFactory
{
    IUnityContainer _container;

    public UnityControllerFactory(IUnityContainer container)
    {
        _container = container;
    }

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
        {
            throw new HttpException(404, String.Format("The controller for path '{0}' could not be found" +
                "or it does not implement IController.",
                 requestContext.HttpContext.Request.Path));
        }

        return _container.Resolve(controllerType) as IController;
    }

}

Дополнительная информация Мне также интересно, где находится экземпляр datacontext в предоставленном решении? Я что-то упускаю?

Есть ли на самом деле лучший способ решить мою «проблему»?

Спасибо!

** Добавлена ​​информация о моей реализации **

Ниже приведены фрагменты из моего Global. asax, Контроллер и Репозиторий. Это дает четкое представление о моей реализации.

Global.asax

  var container = new UnityContainer();
            container
                .RegisterType(new ContainerControlledLifetimeManager())
                .RegisterType(new ContainerControlledLifetimeManager())
                .RegisterType(new PerResolveLifetimeManager(), dbConnectionString)

Контроллер

private ProductsRepository _productsRepository;
private CategoryRepository _categoryRepository;

public ProductsController(ProductsRepository productsRepository, CategoryRepository categoryRepository)
{
   _productsRepository = productsRepository;
   _categoryRepository = categoryRepository;
}

public ActionResult Index()
{
   ProductCategory category = _categoryRepository.GetProductCategory(categoryId);
   . 
   . 
   . 
}

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

Репозиторий продукта

public class ProductsRepository : IDisposable
{

private MyEntities _db;

public ProductsRepository(MyEntities db)
{
    _db = db;
}

public Product GetProduct(Guid productId)
{
    return _db.Product.Where(x => x.ID == productId).FirstOrDefault();
}

public void Dispose()
{
    this._db.Dispose();
}

Завод контроллера

public class UnityControllerFactory : DefaultControllerFactory
{
    IUnityContainer _container;

    public UnityControllerFactory(IUnityContainer container)
    {
        _container = container;
    }

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
        {
            throw new HttpException(404, String.Format("The controller for path '{0}' could not be found" +
                "or it does not implement IController.",
                 requestContext.HttpContext.Request.Path));
        }

        return _container.Resolve(controllerType) as IController;
    }

}

Дополнительная информация Мне также интересно, где находится экземпляр datacontext в предоставленном решении? Я что-то упускаю?

Есть ли на самом деле лучший способ решить мою «проблему»?

Спасибо!

** Добавлена ​​информация о моей реализации **

Ниже приведены фрагменты из моего Global. asax, Контроллер и Репозиторий. Это дает четкое представление о моей реализации.

Global.asax

  var container = new UnityContainer();
            container
                .RegisterType(new ContainerControlledLifetimeManager())
                .RegisterType(new ContainerControlledLifetimeManager())
                .RegisterType(new PerResolveLifetimeManager(), dbConnectionString)

Контроллер

private ProductsRepository _productsRepository;
private CategoryRepository _categoryRepository;

public ProductsController(ProductsRepository productsRepository, CategoryRepository categoryRepository)
{
   _productsRepository = productsRepository;
   _categoryRepository = categoryRepository;
}

public ActionResult Index()
{
   ProductCategory category = _categoryRepository.GetProductCategory(categoryId);
   . 
   . 
   . 
}

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

Репозиторий продукта

public class ProductsRepository : IDisposable
{

private MyEntities _db;

public ProductsRepository(MyEntities db)
{
    _db = db;
}

public Product GetProduct(Guid productId)
{
    return _db.Product.Where(x => x.ID == productId).FirstOrDefault();
}

public void Dispose()
{
    this._db.Dispose();
}

Завод контроллера

public class UnityControllerFactory : DefaultControllerFactory
{
    IUnityContainer _container;

    public UnityControllerFactory(IUnityContainer container)
    {
        _container = container;
    }

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
        {
            throw new HttpException(404, String.Format("The controller for path '{0}' could not be found" +
                "or it does not implement IController.",
                 requestContext.HttpContext.Request.Path));
        }

        return _container.Resolve(controllerType) as IController;
    }

}

Дополнительная информация Я думаю, что это проблема Rails 3, но это может быть проблема Flex / AIR. Приложение Rails использует ...

У меня есть REST API в Rails 3, к которому иногда обращаются из приложения AIR, а иногда из браузера.

Я думаю , что это проблема Rails 3, но она может быть проблемой Flex / AIR.

Приложение Rails использует omniauth для аутентификации, cancan для авторизации и active_record_store. Я использую модель сеанса для хранения идентификаторов пользователя.

(Я не использую сеансы файлов cookie по той причине, что они связаны с AIR для Android, OAuth и StageWebView.)

Я использую Чарльз для мониторинга HTTP-трафика.

Большинство запросов работают нормально. Браузер (или клиент AIR) отправляет идентификатор сеанса на сервер, используя HTTP-заголовок Cookie, например:

_session_id=950dee7eca6732aa62b5f91876f66d15

И Rails находит сеанс, выясняет, кто пользователь, и делает свое дело.

Но при определенных обстоятельствах Rails генерирует новый сеанс перед отправкой ответа. Он добавляет сеанс в таблицу сеансов и возвращает клиенту заголовок Set-Cookie с новым идентификатором сеанса. Примерно так:

_session_id=e1489a6b610c0a1d13cec1454228ae47; path=/; HttpOnly

Обстоятельства, при которых это происходит:

  • Запрос исходит от клиента AIR
  • Запрос представляет собой POST

Это, очевидно, проблема, потому что на последующем запросов, Rails не может найти информацию о пользователе. Он создал новый сеанс без этой информации.

Итак, я смотрю на заголовки HTTP для запроса POST. Вот копия / вставка от Чарльза; Я вставил двоеточие после имени заголовка, чтобы его можно было прочитать.

Host: localhost.seti.hg94.com:3000
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en) AppleWebKit/531.9 (KHTML, like Gecko) AdobeAIR/2.6
Referer: app:/AndroidApplication.swf
X-Flash-Version: 10,2,152,22
Accept: */*
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Cookie: _session_id=950dee7eca6732aa62b5f91876f66d15
Content-Length: 84
Connection: keep-alive

Есть ли у кого-нибудь какое-нибудь представление о том, почему Rails генерирует новый сеанс в таких обстоятельствах? Похоже, что это происходит после моего кода контроллера, поскольку у меня есть правильная информация о сеансе в контроллере.

Я занят, пытаясь изолировать проблему дальше, контролирую заголовки из AIR и скоро. Работаю над этим багом почти неделю. Поэтому мы будем очень благодарны за любую информацию или предложения от сообщества.

6
задан Francis Potter 3 March 2011 в 22:46
поделиться