【问题标题】:Ninject MVC 2, EF 4, AutoMapper - ObjectContext has been disposedNinject MVC 2、EF 4、AutoMapper - ObjectContext 已被释放
【发布时间】:2012-05-18 13:31:00
【问题描述】:

完全修改:

好的,我使用带有 MVC 2 扩展的 Ninject 作为我的 DI 容器,并将 AutoMapper 作为我的实体 视图模型映射器。我在我的视图模型 -> 实体映射中收到“已处理 ObjectContext”错误。我的代码如下。

Ninject 绑定:

public class DIModule : NinjectModule
{
    public override void Load()
    {
        this.Bind<HGEntities>().ToSelf().InRequestScope();
        this.Bind<IArticleRepository>().To<HGArticleRepository>().InRequestScope();
        this.Bind<IGameRepository>().To<HGGameRepository>().InRequestScope();
        this.Bind<INewsRepository>().To<HGNewsRepository>().InRequestScope();
        this.Bind<ErrorController>().ToSelf().InRequestScope();
        this.Bind<HGController>().ToSelf().InRequestScope();
    }
}

我的仓库:

public class HGGameRepository : IGameRepository, IDisposable
{
    private HGEntities _context;

    public HGGameRepository(HGEntities context)
    {
        this._context = context;
    }

    // methods

    public void SaveGame(Game game)
    {
        if (game.GameID > 0)
        {
            _context.ObjectStateManager.ChangeObjectState(game, System.Data.EntityState.Modified);
        }
        else
        {
            _context.Games.AddObject(game);
        }

        _context.SaveChanges();
    }

    public void Dispose()
    {
        if (this._context != null)
        {
            this._context.Dispose();
        }
    }
}

我的控制器的构造函数,其中包括我的 AutoMapper 地图定义:

    public AdminController(IArticleRepository articleRepository, IGameRepository gameRepository, INewsRepository newsRepository)
    {
        _articleRepository = articleRepository;
        _gameRepository    = gameRepository;
        _newsRepository    = newsRepository;

        Mapper.CreateMap<Game, AdminGameViewModel>()
            .BeforeMap((s, d) =>
            {
                int platCount = s.Platforms.Count;
                var plats = s.Platforms.ToArray();
                d.PlatformIDs = new int[platCount];

                for (int i = 0; i < platCount; ++i)
                {
                    d.PlatformIDs[i] = plats[i].PlatformID;
                }
            })
            .ForMember(dest => dest.Pros, opt => opt.MapFrom(src => src.Pros.Split(new char[] {'|'})))
            .ForMember(dest => dest.Cons, opt => opt.MapFrom(src => src.Cons.Split(new char[] {'|'})))
            .ForMember(dest => dest.PlatformIDs, opt => opt.Ignore());

        Mapper.CreateMap<AdminGameViewModel, Game>()
            .BeforeMap((s, d) =>
            {
                if (d.Platforms != null && d.Platforms.Count > 0)
                {
                    var oldPlats = d.Platforms.ToArray();

                    foreach (var oldPlat in oldPlats)
                    {
                        d.Platforms.Remove(oldPlat);
                    }
                }

                foreach (var platId in s.PlatformIDs)
                {
                    var plat = _gameRepository.GetPlatform(platId);
                    d.Platforms.Add(plat);
                }
            })
            .ForMember(dest => dest.Platforms, opt => opt.Ignore())
            .ForMember(dest => dest.BoxArtPath, opt => opt.Ignore())
            .ForMember(dest => dest.IndexImagePath, opt => opt.Ignore())
            .ForMember(dest => dest.Cons, opt => opt.MapFrom(src => string.Join("|", src.Cons)))
            .ForMember(dest => dest.Pros, opt => opt.MapFrom(src => string.Join("|", src.Pros)))
            .ForMember(dest => dest.LastModified, opt => opt.UseValue(DateTime.Now));
    }

第二个映射在这里很重要。

接下来是我的编辑方法:

    [HttpPost]
    public ActionResult EditGame([Bind(Prefix="GameData")]AdminGameViewModel formData)
    {
        Game game = _gameRepository.GetGame(formData.GameID);

        if (ModelState.IsValid)
        {
            game = AutoMapper.Mapper.Map<AdminGameViewModel, Game>(formData, game);

    // it dies here, so the rest of the method is immaterial
    }

最后,堆栈跟踪:

[ObjectDisposedException: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.]
System.Data.Objects.ObjectContext.EnsureConnection() +87
System.Data.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption) +90
System.Data.Objects.ObjectQuery`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator() +96
System.Linq.Enumerable.FirstOrDefault(IEnumerable`1 source) +182
System.Data.Objects.ELinq.ObjectQueryProvider.<GetElementFunction>b__1(IEnumerable`1 sequence) +74
System.Data.Objects.ELinq.ObjectQueryProvider.ExecuteSingle(IEnumerable`1 query, Expression queryRoot) +95
System.Data.Objects.ELinq.ObjectQueryProvider.System.Linq.IQueryProvider.Execute(Expression expression) +163
System.Linq.Queryable.FirstOrDefault(IQueryable`1 source, Expression`1 predicate) +300
HandiGamer.Domain.Concrete.HGGameRepository.GetPlatform(Int32 id) in C:\Users\Kevin\documents\visual studio 2010\Projects\HandiGamer\HandiGamer.Domain\Concrete\HGGameRepository.cs:68
HandiGamer.WebUI.Controllers.AdminController.<.ctor>b__a(AdminGameViewModel s, Game d) in C:\Users\Kevin\documents\visual studio 2010\Projects\HandiGamer\HandiGamer\Controllers\AdminController.cs:56
AutoMapper.<>c__DisplayClass1b.<BeforeMap>b__1a(Object src, Object dest) +139
AutoMapper.TypeMap.<get_BeforeMap>b__0(Object src, Object dest) +118
AutoMapper.Mappers.PropertyMapMappingStrategy.Map(ResolutionContext context, IMappingEngineRunner mapper) +196
AutoMapper.Mappers.TypeMapMapper.Map(ResolutionContext context, IMappingEngineRunner mapper) +256
AutoMapper.MappingEngine.AutoMapper.IMappingEngineRunner.Map(ResolutionContext context) +459

[AutoMapperMappingException: 

Mapping types:
AdminGameViewModel -> Game
HandiGamer.WebUI.ViewModels.AdminGameViewModel -> HandiGamer.Domain.Entities.Game

Destination path:
Game

Source value:
HandiGamer.WebUI.ViewModels.AdminGameViewModel]
AutoMapper.MappingEngine.AutoMapper.IMappingEngineRunner.Map(ResolutionContext context) +537
AutoMapper.MappingEngine.Map(Object source, Object destination, Type sourceType, Type destinationType, Action`1 opts) +179
AutoMapper.MappingEngine.Map(TSource source, TDestination destination, Action`1 opts) +190
AutoMapper.MappingEngine.Map(TSource source, TDestination destination) +146
AutoMapper.Mapper.Map(TSource source, TDestination destination) +105
HandiGamer.WebUI.Controllers.AdminController.EditGame(AdminGameViewModel formData) in C:\Users\Kevin\documents\visual studio 2010\Projects\HandiGamer\HandiGamer\Controllers\AdminController.cs:323
lambda_method(Closure , ControllerBase , Object[] ) +162
System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) +51
System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) +409
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +52
System.Web.Mvc.<>c__DisplayClassd.<InvokeActionMethodWithFilters>b__a() +127
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation) +436
System.Web.Mvc.<>c__DisplayClassf.<InvokeActionMethodWithFilters>b__c() +61
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +305
System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +830
System.Web.Mvc.Controller.ExecuteCore() +136
System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +111
System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +39
System.Web.Mvc.<>c__DisplayClass8.<BeginProcessRequest>b__4() +65
System.Web.Mvc.Async.<>c__DisplayClass1.<MakeVoidDelegate>b__0() +44
System.Web.Mvc.Async.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _) +42
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +141
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +54
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +40
System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +52
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +38
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +690
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +194

按照调试器中的过程,我的 ObjectContext 的 (HGEntities) 连接保持不变,直到我的视图模型 -> 游戏地图被调用。出于某种原因,连接被设置在那个点上。关于为什么会发生这种情况的任何想法?

【问题讨论】:

  • 尝试手动进行映射,看看是否可行
  • 为什么要在存储库中处理上下文?你不应该那样做。让 Ninject 处理它。为什么要为控制器定义规则?这不是必需的。

标签: c#-4.0 asp.net-mvc-2 entity-framework-4 automapper ninject.web.mvc


【解决方案1】:

我发现 IDisposable 没有正确实现。也许这可能是您处理相关异常的原因?

【讨论】:

    【解决方案2】:

    我自己的静态映射器完美运行:

    public static class GameMapper
    {
        public static Game MapFromEditModelToGame(IGameRepository repo, AdminGameViewModel formData, Game newGame)
        {
            newGame.GameID = formData.GameID;
            newGame.GameTitle = formData.GameTitle;
            newGame.GenreID = formData.GenreID;
            newGame.LastModified = DateTime.Now;
            newGame.ReviewScore = (short)formData.ReviewScore;
            newGame.ReviewText = formData.ReviewText;
            newGame.Cons = String.Join("|", formData.Cons);
            newGame.Pros = String.Join("|", formData.Pros);
            newGame.Slug = formData.Slug;
    
            if (newGame.Platforms != null && newGame.Platforms.Count > 0)
            {
                var oldPlats = newGame.Platforms.ToArray();
    
                foreach (var oldPlat in oldPlats)
                {
                    newGame.Platforms.Remove(oldPlat);
                }
            }
    
            foreach (var platId in formData.PlatformIDs)
            {
                var plat = repo.GetPlatform(platId);
                newGame.Platforms.Add(plat);
            }
    
            return newGame;
        }
    }
    

    调用为:

    game = GameMapper.MapFromEditModelToGame(_gameRepository, formData, game);
    

    将以上内容与我的 AutoMapper 地图定义进行比较:

    Mapper.CreateMap<AdminGameViewModel, Game>()
    .BeforeMap((s, d) =>
    {
        if (d.Platforms != null && d.Platforms.Count > 0)
        {
            var oldPlats = d.Platforms.ToArray();
    
            foreach (var oldPlat in oldPlats)
            {
                d.Platforms.Remove(oldPlat);
            }
        }
    
        foreach (var platId in s.PlatformIDs)
        {
            var plat = _gameRepository.GetPlatform(platId);
            d.Platforms.Add(plat);
        }
    })
    .ForMember(dest => dest.Platforms, opt => opt.Ignore())
    .ForMember(dest => dest.BoxArtPath, opt => opt.Ignore())
    .ForMember(dest => dest.IndexImagePath, opt => opt.Ignore())
    .ForMember(dest => dest.Cons, opt => opt.MapFrom(src => string.Join("|", src.Cons)))
    .ForMember(dest => dest.Pros, opt => opt.MapFrom(src => string.Join("|", src.Pros)))
    .ForMember(dest => dest.LastModified, opt => opt.UseValue(DateTime.Now));
    

    唯一真正的区别是我将 repo 作为参数传递。

    所有其他代码 - 我的控制器、我的 DI 东西等 - 完全相同。 唯一的区别是我使用了我自己的笨拙的映射器。不是一个理想的情况,但有些东西。

    【讨论】:

      【解决方案3】:

      基于documentation,他们实际上不鼓励您使用工厂。我使用它们,但现在它可能会解决您的问题。我在自己的 DI 中使用 ninject 旅程时遇到了这个错误,但很难说你为什么会得到你的错误。对我来说,这是由关系和延迟加载引起的。启用延迟加载后,您的对象会保留对 ObjectContext 的引用,因此它们可以填充相关对象和相关对象的集合。

      归根结底,我认为如果您发布错误的堆栈跟踪信息会有所帮助,以便我们可以帮助您准确了解错误发生的时间。

      希望这会有所帮助。

      编辑:我认为问题出在您的保存方法上。如果您在让游戏对其进行编辑时处置您的对象上下文。我不认为它会在 ObjectStateManager 中看到你的行:

      _context.ObjectStateManager.ChangeObjectState(game, System.Data.EntityState.Modified);
      

      您从 AutoMapper 获得的当前游戏对象未附加到任何 ObjectContext。这是我用于更新的方法:

      public void Update(TEntity entity)
      {
          object originalItem;
      
          EntityKey key = Context.CreateEntityKey(Context.GetEntitySet<TEntity>().Name, entity);
      
          if (Context.TryGetObjectByKey(key, out originalItem))
          {
              Context.ApplyCurrentValues(key.EntitySetName, entity);
          }
      }
      

      别忘了致电Context.SaveChanges();

      我没有想出它自己在网络上的某个地方找到了它。这对我来说效果很好。我会调整你的保存方法来合并它。如果您需要帮助,请告诉我。我推荐的另一件事(小)是在您的 dispose 方法中将您的 Object 上下文设置为 null。

      祝你好运!

      编辑:好的,再试一次...根据您的管理控制器的构造函数,您的存储库是否有私有变量?

      private IGameRepository _gameRepository;
      
      public AdminController(IArticleRepository articleRepository, IGameRepository gameRepository, INewsRepository newsRepository)
          {
              _articleRepository = articleRepository;
              _gameRepository    = gameRepository;
              _newsRepository    = newsRepository;
      

      控制器是由 ninject 创建的吗?我在您的绑定列表中没有看到 AdminController。我没有在您的任何构造函数上看到 [Inject] 属性。您是在发布到堆栈时忽略了它还是它们不存在?

      我的另一个想法是,如果您的上下文中的应用程序 InSingletonScope() 没有任何内存问题(泄漏)可能是合适的答案。

      抱歉所有问题,但我只是想更好地理解这个问题。

      【讨论】:

      • 今天晚些时候,当我可以访问我的开发机器时,我将发布堆栈跟踪。当我测试时,使用 InSingletonScope() 注入 ObjectContext 会“修复”它,让我可以毫无例外地更新实体。我唯一关心的是缓存/线程。我不完全确定线程是如何工作的,因为我来自 PHP 背景。
      • 这是有道理的。 (InSingletonScope()) 只需在您发布编辑后添加评论。
      • 还发现,如果我的控制器设置为 InRequestScope(),但我的 ObjectContext 设置为 InSingletonScope(),我会遇到同样的处置错误,就在前面。只有当控制器在默认的 InTransientScope() 中并且 OC 是 InSingletonScope() 时,它似乎才有效。
      • 您好,感谢您的回复。不幸的是,在映射之后,在控制器的 Edit 方法的最后,我不会调用 Save。 GetGame 只是简单地返回 _context.Games.SingleOrDefault(g => g.id == id);调试器还会在该语句之后验证 ObjectContext 连接是否存在。它仅在到达映射代码时才被处理,甚至在我尝试调用 GetPlatform 之前。这几乎就像 AutoMapper 导致 HtmlContext.Current 改变它的值,或者还有其他一些奇怪的范围问题在起作用。
      • 控制器派生自 HGController,后者派生自基本 Controller 类。如果某些事情没有按计划进行,HGController 允许我抛出特定的 HTTP 代码(例如,如果用户请求不存在的内容,则为 404,或者更普遍的错误为 500)。 HGController 在我的 Ninject 模块 InRequestScope 中绑定到自身。据我所见,实际上所有东西都被正确注入了。 IRepository -> ConcreteRepository 绑定所有工作。只是 ObjectContext 搞砸了工作。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-07
      • 1970-01-01
      相关资源
      最近更新 更多