Я работаю над проектом со следующими технологиями:
- Версия Entity Framework: 5
- Миграция Entity Framework
- СигналR версии 1.1.2
- IoC: Виндзорский замок
- Дотнет Фреймворк 4.5
- веб-API
- Визуальная студия 2012
- SQL Server экспресс 2012
я получаю сообщение об ошибке
The operation cannot be completed because the DbContext has been disposed
в классе ServerHub, где я поместил следующее:
// TODO: This is throwing the error: The operation cannot be completed because the DbContext has been disposed
Кто-нибудь знает, почему я получаю это? Я прочитал много ответов, но ничего из того, что я пытался сделать, похоже, не помогло.
Универсальный репозиторий EF (EF5)
public class EFRepository<T> : IRepository<T> where T : class
{
public EFRepository(DbContext dbContext)
{
if (dbContext == null)
throw new ArgumentNullException("dbContext");
DbContext = dbContext;
DbSet = DbContext.Set<T>();
}
protected DbContext DbContext { get; set; }
protected DbSet<T> DbSet { get; set; }
public virtual IQueryable<T> GetAll()
{
return DbSet;
}
public virtual IQueryable<T> GetAllIncluding(params Expression<Func<T, object>>[] includeProperties)
{
IQueryable<T> query = DbContext.Set<T>();
foreach (var includeProperty in includeProperties)
{
query = query.Include(includeProperty);
}
return query;
}
public virtual T GetById(long id)
{
return DbSet.Find(id);
}
public virtual IQueryable<T> GetByPredicate(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
{
IQueryable<T> query = DbContext.Set<T>().Where(predicate);
return query;
}
public virtual IQueryable<T> GetByPredicateIncluding(System.Linq.Expressions.Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includeProperties)
{
IQueryable<T> query = DbContext.Set<T>().Where(predicate);
foreach (var includeProperty in includeProperties)
{
query = query.Include(includeProperty);
}
return query;
}
public virtual void Upsert(T entity, Func<T, bool> insertExpression)
{
if (insertExpression.Invoke(entity))
{
Add(entity);
}
else
{
Update(entity);
}
}
public virtual void Add(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if (dbEntityEntry.State != EntityState.Detached)
{
dbEntityEntry.State = EntityState.Added;
}
else
{
DbSet.Add(entity);
}
}
public virtual void Update(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if (dbEntityEntry.State == EntityState.Detached)
{
DbSet.Attach(entity);
}
dbEntityEntry.State = EntityState.Modified;
}
public virtual void Delete(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if (dbEntityEntry.State != EntityState.Deleted)
{
dbEntityEntry.State = EntityState.Deleted;
}
else
{
DbSet.Attach(entity);
DbSet.Remove(entity);
}
}
public virtual void Delete(int id)
{
var entity = GetById(id);
if (entity == null) return; // not found; assume already deleted.
Delete(entity);
}
}
Установщик концентраторов
using Microsoft.AspNet.SignalR;
public class HubsInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Component
.For<RepositoryFactories>()
.ImplementedBy<RepositoryFactories>()
.LifestyleSingleton());
container.Register(Component
.For<IRepositoryProvider>()
.ImplementedBy<RepositoryProvider>()
.LifestylePerWebRequest());
container.Register(Component
.For<IGdpUow>()
.ImplementedBy<GdpUow>()
.LifestylePerWebRequest());
container.Register(Classes.FromThisAssembly()
.BasedOn<Hub>()
.LifestyleTransient());
}
}
IocConfig.cs
using System.Web.Routing;
using Microsoft.AspNet.SignalR;
namespace CompanyGdpSoftware.Server.Ui.Web
{
using System.Web.Http;
using System.Web.Mvc;
using Castle.Windsor;
using CommonServiceLocator.WindsorAdapter;
using Infrastructure;
using Microsoft.Practices.ServiceLocation;
public static class IocConfig
{
public static IWindsorContainer Container { get; private set; }
public static void RegisterIoc(HttpConfiguration config)
{
var signalrDependencyContainer = new WindsorContainer().Install(new HubsInstaller());
var signalrDependency = new SignalrDependencyResolver(signalrDependencyContainer.Kernel);
GlobalHost.DependencyResolver = signalrDependency;
//RouteTable.Routes.MapHubs(signalrDependency); // Needed to remove the parameter when moved from SignalR RC to 1.1.2
RouteTable.Routes.MapHubs(); // Used this one when moving to SignalR release update.
// Set the dependency resolver for Web API.
var webApicontainer = new WindsorContainer().Install(new WebWindsorInstaller());
GlobalConfiguration.Configuration.DependencyResolver = new WebApiWindsorDependencyResolver(webApicontainer);
// Set the dependency resolver for Mvc Controllers
Container = new WindsorContainer().Install(new ControllersInstaller());
DependencyResolver.SetResolver(new MvcWindsorDependencyResolver(Container));
ServiceLocator.SetLocatorProvider(() => new WindsorServiceLocator(Container));
var controllerFactory = new WindsorControllerFactory(Container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
}
}
}
UoW (единица работы)
public class GdpUow : IGdpUow, IDisposable
{
public GdpUow(IRepositoryProvider repositoryProvider)
{
CreateDbContext();
repositoryProvider.DbContext = DbContext;
RepositoryProvider = repositoryProvider;
}
public IRepository<Branch> Branches { get { return GetStandardRepo<Branch>(); } }
public void Commit()
{
DbContext.SaveChanges();
}
protected void CreateDbContext()
{
DbContext = new GdpSoftwareDbContext();
// Do NOT enable proxied entities, else serialization fails
DbContext.Configuration.ProxyCreationEnabled = false;
// Load navigation properties explicitly (avoid serialization trouble)
DbContext.Configuration.LazyLoadingEnabled = false;
// Because Web API will perform validation, I don't need/want EF to do so
DbContext.Configuration.ValidateOnSaveEnabled = false;
}
protected IRepositoryProvider RepositoryProvider { get; set; }
private IRepository<T> GetStandardRepo<T>() where T : class
{
return RepositoryProvider.GetRepositoryForEntityType<T>();
}
private T GetRepo<T>() where T : class
{
return RepositoryProvider.GetRepository<T>();
}
private GdpSoftwareDbContext DbContext { get; set; }
#region IDisposable
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposing)
{
return;
}
if (DbContext != null)
{
DbContext.Dispose();
DbContext = null;
}
}
#endregion
}
}
Извлечь из ServerHub
using System;
using System.Linq;
using System.Timers;
using Data.Contracts;
using Data.Model;
using Microsoft.AspNet.SignalR;
public class ServerHub : Hub
{
private static System.Timers.Timer aTimer;
public IGdpUow Uow { get; set; }
DateTime lastDate = DateTime.UtcNow;
public ServerHub()
{
// Create a timer with a ten second interval.
aTimer = new System.Timers.Timer(10000);
// Hook up the Elapsed event for the timer.
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
aTimer.Enabled = true;
// If the timer is declared in a long-running method, use
// KeepAlive to prevent garbage collection from occurring
// before the method ends.
GC.KeepAlive(aTimer);
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
SendNewMessage(e.SignalTime);
}
public void SendNewMessage(DateTime SignalTime)
{
// TODO: This is throwing the error: The operation cannot be completed because the DbContext has been disposed
var configurationsRecord = this.Uow.GdpConfigurations.GetByPredicate(a => a.Description.Equals("LastDateTimeCheck")).SingleOrDefault();
if (configurationsRecord == null)
{
throw new ApplicationException("Please set the LastDateTimeCheck value");
}
}
// Called from the client
public void GetAllMessages()
{
var MessagesList = Uow.Messages.GetAll().Select(
newMessage => new MessageDto
{
Country = newMessage.Country,
CountryId = newMessage.CountryId ?? 0,
MessageId = newMessage.MessageId
});
Clients.All.handleGetAll(MessagesList);
}
}
ОБНОВЛЕНИЕ Я добавил это, что предложил Дрю... до сих пор не повезло
using System;
using System.Linq;
using System.Timers;
using Data.Contracts;
using Data.Model;
using Microsoft.AspNet.SignalR;
public class ServerHub : Hub
{
private static System.Timers.Timer aTimer;
DateTime lastDate = DateTime.UtcNow;
public IHubHandler hubHandler { get; set; }
public ServerHub()
{
// Create a timer with a ten second interval.
aTimer = new System.Timers.Timer(10000);
// Hook up the Elapsed event for the timer.
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
aTimer.Enabled = true;
// If the timer is declared in a long-running method, use
// KeepAlive to prevent garbage collection from occurring
// before the method ends.
GC.KeepAlive(aTimer);
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
HubHandler.SendNewMessage(e.SignalTime);
}
// Called from the client
public void GetAllMessages()
{
var MessagesList = Uow.Messages.GetAll().Select(
newMessage => new MessageDto
{
Country = newMessage.Country,
CountryId = newMessage.CountryId ?? 0,
MessageId = newMessage.MessageId
});
Clients.All.handleGetAll(MessagesList);
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
hubHandler.SendNewMessage(e.SignalTime);
}
}
и новый класс
HubHandler
using System;
using System.Linq;
using Data.Contracts;
using Data.Model;
using Microsoft.AspNet.SignalR;
public class HubHandler : IHubHandler
{
public IGdpUow Uow { get; set; }
DateTime lastDate = DateTime.UtcNow;
public void SendNewMessage(DateTime signalTime)
{
// Get a hub context for ServerHub
var serverHub = GlobalHost.ConnectionManager.GetHubContext<ServerHub>();
// TODO: This is throwing the error: The operation cannot be completed because the DbContext has been disposed
var gdpConfigurationRecord = Uow.GdpConfigurations.GetByPredicate(a => a.Description.Equals("LastDateTimeMessagesCheck")).SingleOrDefault();
if (gdpConfigurationRecord == null)
{
throw new ApplicationException("Please set the LastDateTimeMessagesCheck value in GdpConfigurations");
}
var lastMessagesDateTimeCheck = gdpConfigurationRecord.DateTimeValue;
// Send a message to all the clients
serverHub.Clients.All.handleNewMessages("message");
gdpConfigurationRecord.DateTimeValue = signalTime.ToUniversalTime();
Uow.GdpConfigurations.Update(gdpConfigurationRecord);
}
}
}
ОБНОВЛЕНИЕ 2
Теперь я переместил таймер из Hub в HubHandler. Также я установил пакет Nuget для использования LifeStyle.HybridPerWebRequestTransient для GdpUow и RepositoryProvider. Все та же проблема.
Концентратор серверов
namespace GdpSoftware.App.Ui.Web.Hubs
{
using System;
using System.Linq;
using Data.Contracts;
using Data.Model;
using Microsoft.AspNet.SignalR;
public class ServerHub : Hub
{
public IGdpUow Uow { get; set; }
public IHubHandler hubHandler { get; set; }
public void GetAllMessages()
{
var messagesList = Uow.Messages.GetAll().Select(
newMessage => new MessageDto
{
MessageId = newMessage.MessageId,
Messagestatus = newMessage.MessageStatus.Description
});
hubHandler.SetClients(Clients);
hubHandler.StartTimer();
Clients.All.handleMessages(messagesList);
}
}
}
Установщик концентратора
public class HubsInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Component
.For<RepositoryFactories>()
.ImplementedBy<RepositoryFactories>()
.LifestyleSingleton());
container.Register(Component
.For<IRepositoryProvider>()
.ImplementedBy<RepositoryProvider>()
.LifeStyle.HybridPerWebRequestTransient());
container.Register(Component
.For<IGdpUow>()
.ImplementedBy<GdpUow>()
.LifeStyle.HybridPerWebRequestTransient());
container.Register(Component
.For<IHubHandler>()
.ImplementedBy<HubHandler>()
.LifestyleSingleton());
container.Register(Classes.FromThisAssembly()
.BasedOn<Hub>()
.LifestyleTransient());
}
}
HubHandler
public class HubHandler : IHubHandler
{
private static System.Timers.Timer aTimer;
private IHubConnectionContext Clients { get; set; }
public IGdpUow Uow { get; set; }
DateTime lastDate = DateTime.UtcNow;
public void SetClients(IHubConnectionContext clients)
{
Clients = clients;
}
public void StartTimer()
{
aTimer = new System.Timers.Timer(10000);
aTimer.Elapsed += new ElapsedEventHandler(SendNewMessage);
aTimer.Enabled = true;
//If the timer is declared in a long-running method, use KeepAlive to prevent garbage collection from occurring before the method ends.
GC.KeepAlive(aTimer);
}
public void SendNewMessage(object state, ElapsedEventArgs elapsedEventArgs)
{
// Get a hub context for ServerHub
var serverHub = GlobalHost.ConnectionManager.GetHubContext<ServerHub>();
// TODO: This is throwing the error: The operation cannot be completed because the DbContext has been disposed
var gdpConfigurationsRecord = Uow.GdpConfigurations.GetByPredicate(a => a.Description.Equals("LastDateTimeMessagesCheck")).SingleOrDefault();
if (gdpConfigurationsRecord == null)
{
throw new ApplicationException("Please set the LastDateTimeMessagesCheck value in GdpConfigurations");
}
// Send a message to all the clients
serverHub.Clients.All.handleSendNewMessages("");
}
}
RegisterHubs.cs
public static class RegisterHubs
{
public static void Start()
{
// Register the default hubs route: ~/signalr
var signalrDependencyContainer = new WindsorContainer().Install(new HubsInstaller());
var signalrDependency = new SignalrDependencyResolver(signalrDependencyContainer.Kernel);
GlobalHost.DependencyResolver = signalrDependency;
RouteTable.Routes.MapHubs();
}
}
ОБНОВЛЕНИЕ 3
Я получаю сообщение об ошибке от Виндзора...
Если я поставлю это как последнюю строку в IoC.config (которая вызывается в Application_Start)
webApicontainer.Resolve<IHubHandler>().StartTimer();
Я получил:
No component for supporting the service GdpSoftware.Server.Ui.Web.Hubs.IHubHandler was found
То же самое, если я удалю его из IoC.config и попытаюсь использовать его как последнюю строку в Application_Start.
Container.Resolve<IHubHandler>().StartTimer();
Если я добавлю
container.Register(Component
.For<IHubHandler>()
.ImplementedBy<HubHandler>()
.LifestyleSingleton());
в ControllersInstaller я получаю
Can't create component 'GdpSoftware.Server.Ui.Web.Hubs.HubHandler' as it has dependencies to be satisfied. (Service 'Castle.Windsor.IWindsorContainer' which was not registered)
Где/как я могу использовать
Container.Resolve<IHubHandler>().StartTimer();
Это мой текущий IoC.config
public static class IocConfig
{
public static IWindsorContainer Container { get; private set; }
public static void RegisterIoc(HttpConfiguration config)
{
var webApicontainer = new WindsorContainer().Install(new WebWindsorInstaller());
GlobalConfiguration.Configuration.DependencyResolver = new WebApiWindsorDependencyResolver(webApicontainer);
Container = new WindsorContainer().Install(new ControllersInstaller());
DependencyResolver.SetResolver(new MvcWindsorDependencyResolver(Container));
ServiceLocator.SetLocatorProvider(() => new WindsorServiceLocator(Container));
var controllerFactory = new WindsorControllerFactory(Container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
webApicontainer.Resolve<IHubHandler>().StartTimer();
}
}
и это мой Application_Start
protected void Application_Start()
{
RegisterHubs.Start();
IocConfig.RegisterIoc(GlobalConfiguration.Configuration);
GlobalHost.HubPipeline.AddModule(new LoggingPipelineModule());
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
GlobalConfig.CustomizeConfig(GlobalConfiguration.Configuration);
}
Я даже не понимаю, почему это должно быть в ControllersInstaller, а не в HubInstaller...