Testing mocked EF context, context and unit of work with RhinoMocks and NUnit - asp.net-mvc

I am having real problems mocking my code to enable me to test my MVC controllers.
My repository implements the following interface
public interface IEntityRepository<T>
{
IQueryable<T> All { get; }
IQueryable<T> AllIncluding(params Expression<Func<T, object>>[] includeProperties);
void Delete(int id);
T Find(int id);
void InsertOrUpdate(T entity);
void InsertOrUpdateGraph(T entity);
}
Like so
public interface IMonkeyRepository : IEntityRepository<Monkey>
{
}
My EF context implements the following interface
public interface IMonkeyContext
{
IDbSet<Monkey> Monkeys { get; set; }
DbEntityEntry Entry(object entity);
IEnumerable<DbEntityValidationResult> GetValidationErrors();
int SaveChanges();
}
My unit of work interface is defined like so
public interface IUnitOfWork<TContext> : IDisposable
{
TContext Context { get; }
int Save();
}
And implemented
public class MonkeyUnitOfWork : IUnitOfWork<IMonkeyContext>
{
private readonly IMonkeyContext context;
private bool disposed;
public MonkeyUnitOfWork(IMonkeyContext context)
{
this.context = context;
}
public IMonkeyContext Context
{
get
{
return this.context;
}
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
public int Save()
{
var ret = this.context.SaveChanges();
return ret;
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
((DbContext)this.context).Dispose();
}
}
this.disposed = true;
}
}
I have a MonkeyController whos Create action I wish to test. I is defined
if (this.ModelState.IsValid)
{
this.repo.InsertOrUpdate(Mapper.Map<MonkeyModel, Monkey>(monkey));
this.uow.Save();
return this.RedirectToAction(MVC.Monkey.Index());
}
return this.View(monkey);
In my unit test I am using RhinoMocks and have defined the test
[TestFixture]
public class MonkeyControllerTests
{
MockRepository mocks = null;
private IMonkeyRepository monkeyRepository;
private IMonkeyContext context;
private MonkeyUnitOfWork unitOfWork;
private MonkeyController controller;
[SetUp]
public virtual void SetUp()
{
TestHelpers.SetupAutoMap();
this.monkeyRepository = this.Mocks.StrictMultiMock<IMonkeyRepository>(typeof(IEntityRepository<Monkey>));
this.context = this.Mocks.StrictMock<IMonkeyContext>();
this.unitOfWork = new MonkeyUnitOfWork(this.context);
this.controller = new MonkeyController(this.MonkeyRepository, this.unitOfWork);
}
[TearDown]
public virtual void TearDown()
{
if (this.mocks != null)
{
try
{
this.mocks.ReplayAll();
this.mocks.VerifyAll();
}
finally
{
this.mocks = null;
}
}
}
public MockRepository Mocks
{
get
{
if (mocks == null)
mocks = new MockRepository();
return mocks;
}
}
[Test]
public void MonkeyCreateShouldShouldDoSomeStuff()
{
var monkeyModel = ViewModelTestHelpers.CreateSingleMonkey();
var monkey = Mapper.Map<MonkeyModel, Monkey>(monkeyModel);
this.monkeyRepository.Expect(action => action.InsertOrUpdate(monkey));
this.context.Expect(action => action.SaveChanges()).Return(1);
var result = (RedirectToRouteResult)this.controller.Create(monkeyModel);
Assert.AreEqual(MVC.Monnkey.ActionNames.Index, result.RouteValues["action"]);
}
}
When I run my tests I either get the following errror
Previous method 'IMonkeyContext.SaveChanges();' requires a return value or an exception to throw.
Or it complains that the IEntityRepository.InsertOrUpdate expected 1 actual 0
I have tried so many casts, and incantations to get this to work but I am stumped. Does anyone know how to mock these object correctly? Or if I have fundamentaly missed something here?

Well it would seem to be a schoolboy error
RhinoMocks was correct when it said that IEntityRepository.InsertOrUpdate was not being called.
the line of code where I map from view model to model in my test
var monkey = Mapper.Map<MonkeyModel, Monkey>(monkeyModel);
and then use it in the expect
this.monkeyRepository.Expect(action => action.InsertOrUpdate(monkey));
was of course telling Rhino that the function should be called with this exact instance of monkey.
The function is of course called in the following way within the action
this.repo.InsertOrUpdate(Mapper.Map<MonkeyModel, Monkey>(monkey));
Not the same instance.
I have moved to the aaa syntax now and changed the test code to
this.monkeyRepository.Stub(r => r.InsertOrUpdate(Arg<Monkey>.Is.Anything));
and assert
this.monkeyRepository.AssertWasCalled(r => r.InsertOrUpdate(Arg<Monkey>.Is.Anything));
I will now go and hang my head in shame.

Related

No parameterless constructor defined for this object with Dependency Resolver

I'm currently following "Dependancy Injection on asp net mvc 5 tutorial" in youtube. https://www.youtube.com/watch?v=27DQn6kZDFM
I follow as he said but I'm having this error.
No parameterless constructor defined for this object.
My Controller is UnityDemoController
public class UnityDemoController : Controller
{
private readonly ILocalWeaherServiceProvider _localWeaherServiceProvider;
public UnityDemoController(ILocalWeaherServiceProvider localWeaherServiceProvider)
{
_localWeaherServiceProvider = localWeaherServiceProvider;
}
//
// GET: /UnityDemo/
public ActionResult Index()
{
string currentWeatherInMyArea = _localWeaherServiceProvider.GetLocalWeatherByZipCode("0006");
return View();
}
}
IocConfiguration.cs This Configuration is under App_Start folder
public static class IocConfiguration
{
public static void ConfigureIocUnityContaioner()
{
IUnityContainer container = new UnityContainer();
RegisterServices(container);
DependencyResolver.SetResolver(new MyUnityDependancyResolver(container));
}
private static void RegisterServices(IUnityContainer container)
{
container.RegisterType<ILocalWeaherServiceProvider, LocalWeatherServiceProvider>(); // This means when somebody call/need ILocalWeaherServiceProvider then provide new Instance of the LocalWeatherServiceProvider
}
}
MyUnityDependancyResolver.cs
public class MyUnityDependancyResolver : IDependencyResolver
{
private IUnityContainer _unityContainer;
public MyUnityDependancyResolver(IUnityContainer unityContainer)
{
_unityContainer = unityContainer;
}
public object GetService(Type serviceType)
{
try
{
return _unityContainer.Resolve(serviceType);
}
catch (Exception)
{
return null;
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
try
{
return _unityContainer.ResolveAll(serviceType);
}
catch (Exception)
{
return new List<object>();
}
}
}
Interface ILocalWeaherServiceProvider
public interface ILocalWeaherServiceProvider
{
string GetLocalWeatherByZipCode(string zipcode);
}
Service Class LocalWeatherServiceProvider
public class LocalWeatherServiceProvider : ILocalWeaherServiceProvider
{
public string GetLocalWeatherByZipCode(string zipcode)
{
return "Its is snowing right now in your Area : " + zipcode;
}
}
I have added Unity.
Can anyone tell me what went wrong here?
And avoid these kinds of error what are the things that I should look into these coding level?
I found out the solution by referring to below link.
https://cuttingedge.it/blogs/steven/pivot/entry.php?id=97
Change the UnityDemoController class as below.
public class UnityDemoController : Controller
{
private readonly ILocalWeaherServiceProvider _localWeaherServiceProvider;
public UnityDemoController() : this(new LocalWeatherServiceProvider())
{
}
public UnityDemoController(ILocalWeaherServiceProvider localWeaherServiceProvider)
{
_localWeaherServiceProvider = localWeaherServiceProvider;
}
//
// GET: /UnityDemo/
public ActionResult Index()
{
string currentWeatherInMyArea = _localWeaherServiceProvider.GetLocalWeatherByZipCode("0006");
return View();
}
}

Error while creating plugin in Nop Commerce

This is my first demo project in Nopcommerce and i have tried to make my own plugin but during the time of Build some error is seen. Below are some codes.
namespace Nop.Plugin.Aowi.Testimonial.Data
{
public class TestimonialRecordObjectContext : DbContext , IDbContext
{
public TestimonialRecordObjectContext(string nameOrConnectionString) : base(nameOrConnectionString) { }
#region Implementation of IDbContext
#endregion
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new TestimonialRecordMap());
base.OnModelCreating(modelBuilder);
}
public string CreateDatabaseInstallationScript()
{
return ((IObjectContextAdapter)this).ObjectContext.CreateDatabaseScript();
}
public void Install()
{
//It's required to set initializer to null (for SQL Server Compact).
//otherwise, you'll get something like "The model backing the 'your context name' context has changed since the database was created. Consider using Code First Migrations to update the database"
Database.SetInitializer<TestimonialRecordObjectContext>(null);
Database.ExecuteSqlCommand(CreateDatabaseInstallationScript());
SaveChanges();
}
public void Uninstall()
{
var dbScript = "DROP TABLE Testimonial";
Database.ExecuteSqlCommand(dbScript);
SaveChanges();
}
public new IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity
{
return base.Set<TEntity>();
}
public System.Collections.Generic.IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters) where TEntity : BaseEntity, new()
{
throw new System.NotImplementedException();
}
public System.Collections.Generic.IEnumerable<TElement> SqlQuery<TElement>(string sql, params object[] parameters)
{
throw new System.NotImplementedException();
}
public int ExecuteSqlCommand(string sql, bool doNotEnsureTransaction = false, int? timeout = null, params object[] parameters)
{
throw new System.NotImplementedException();
}
}
}
This is the Dependency registrar part
namespace Nop.Plugin.Aowi.Testimonial.Infastructure
{
public class DependencyRegistrar: IDependencyRegistrar
{
private const string CONTEXT_NAME ="nop_object_context_product_view_tracker";
public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder, NopConfig config)
{
//data context
this.RegisterPluginDataContext<TestimonialRecordObjectContext>(builder, CONTEXT_NAME);
//override required repository with our custom context
builder.RegisterType<EfRepository<TestimonialRecord>>()
.As<IRepository<TestimonialRecord>>()
.WithParameter(ResolvedParameter.ForNamed<IDbContext>(CONTEXT_NAME))
.InstancePerLifetimeScope();
}
public int Order
{
get { return 1; }
}
}
}
Even after cleaning and Building i am getting this error.
Can anyone help me with this. I have done all of this by watching a tutorial so if anyone can help me correct my mistake i will be really greatful.
You just need to implement this method and properties of IDbContext interface, which are described in error log, in your custom context.
for example, how it is done in one of the existing plugin Tax.CountryStateZip:
public void Detach(object entity)
{
if (entity == null)
throw new ArgumentNullException("entity");
((IObjectContextAdapter)this).ObjectContext.Detach(entity);
}
public virtual bool ProxyCreationEnabled
{
get { return this.Configuration.ProxyCreationEnabled; }
set { this.Configuration.ProxyCreationEnabled = value; }
}
public virtual bool AutoDetectChangesEnabled
{
get { return this.Configuration.AutoDetectChangesEnabled; }
set { this.Configuration.AutoDetectChangesEnabled = value; }
}
I cannot get an idea from your code that where is actual issue. But i suggest by an example.
make your install method code like:
public void Install()
{
//create the table
var dbScript = CreateDatabaseScript();
Database.ExecuteSqlCommand(dbScript);
SaveChanges();
}
Add a new class called EfStartUpTask and paste following code:
public class EfStartUpTask : IStartupTask
{
public void Execute()
{
//It's required to set initializer to null (for SQL Server Compact).
//otherwise, you'll get something like "The model backing the 'your context name' context has changed since the database was created. Consider using Code First Migrations to update the database"
Database.SetInitializer<YourContext>(null);
}
public int Order
{
//ensure that this task is run first
get { return 0; }
}
}
And your DependencyRegistrar :
public class DependencyRegistrar : IDependencyRegistrar
{
public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder)
{
builder.RegisterType<YourService>().As<YourserviceInterface>().InstancePerLifetimeScope();
//data context
this.RegisterPluginDataContext<YourContext>(builder, "nop_object_context_product_view_tracker");
//override required repository with our custom context
builder.RegisterType<EfRepository<YourEntityClass>>()
.As<IRepository<YourEntityClass>>()
.WithParameter(ResolvedParameter.ForNamed<IDbContext>("nop_object_context_product_view_tracker"))
.InstancePerLifetimeScope();
}
public int Order
{
get { return 1; }
}
}
Note: you have to change YourContext to your context name and same as for entity class
Hope this helps!

Entity Framework, Unity, and MVC

I have a multi tier application using Entity Framework, MVC and Unity.
The basic setup is like this:
EF Data Access Layer
public class MyDataProvider : DbContext, IMyDataProvider
{
public MyDataProvider(SqlConnection existingConnection, bool contextOwnsConnection)
: base(existingConnection,contextOwnsConnection)
{
((IObjectContextAdapter)this).ObjectContext.CommandTimeout = 60;
Configuration.LazyLoadingEnabled = true;
Configuration.ValidateOnSaveEnabled = true;
Configuration.ProxyCreationEnabled = true;
Configuration.AutoDetectChangesEnabled = true;
}
public new IDbSet<TModel> Set<TModel>() where TModel : class
{
return base.Set<TModel>();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new EmployeeMapping());
base.OnModelCreating(modelBuilder);
}
public abstract class ServiceBase<TModel> : IDisposable, IService<TModel> where TModel : class, IModel
{
[Dependency]
public IMyDataProvider MyDataProvider { get; set; }
...
}
All services inherit from this class
I then inject specific services into the Business Logic Layer like so:
public class GetEmployees
{
[Dependency("EmployeeService")]
public IEmployeeService EmployeeService { get; set;
public IQueryable<Employee> GetAllEmployees()
{
return EmployeeService.GetTable();
}
...
}
In MVC I use a controller factory
public class MyControllerFactory : DefaultControllerFactory
{
private IUnityContainer _container;
public MyControllerFactory(IUnityContainer container)
{
_container = container;
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType != null)
{
return _container.Resolve(controllerType) as IController;
}
else
{
return base.GetControllerInstance(requestContext, controllerType);
}
}
Global.asax
private static IUnityContainer InitContainer()
{
IUnityContainer unityContainer = new UnityContainer();
Bootstrapper bootstrapper = new Bootstrapper(unityContainer);
return unityContainer;
}
I pass the instance of UnityContainer into the Bootstrapper class. The Bootstrapper class self registers all assemblies.
In the MVC Controllers, I inject the Business Logic like so:
public class EmployeeController
{
[Dependency("GetEmployees")]
public IBusinessLogic GetEmployees_Operations { get; set; }
public ActionResult EmployeeMain()
{
var employees = GetEmployees_Operations.GetAllEmployees();
...
}
}
This all works great up to a point. Every so often I will get an exception thrown from MyDataProvider class: "EntityConnection can only be constructed with a closed DbConnection". This seems to happen during high use of the MVC site. The exception is simple enough to understand, but how should I go about fixing it?
I found that changing how I instantiate the business logic class from a field on the controller to inside the ActionResult method, I don't recieve the exception.
For example:
public class EmployeeController
{
//[Dependency("GetEmployees")]
//public IBusinessLogic GetEmployees_Operations { get; set; }
public ActionResult EmployeeMain()
{
IBusinessLogic GetEmployees_Operations = _ioc_Bootstrapper.Resolve(typeof(IBusinessLogic), "GetEmployees") as IBusinessLogic;
var employees = GetEmployees_Operations.GetAllEmployees();
...
}
}
Have I completely missed the boat on this and implemented Unity incorrectly?
Bootstrapper code
private void RegisterDAL(String assembly)
{
var currentAssembly = Assembly.LoadFrom(assembly);
var assemblyTypes = currentAssembly.GetTypes();
foreach (var assemblyType in assemblyTypes)
{
...
if (assemblyType.FullName.EndsWith("Provider"))
{
foreach (var requiredInterface in assemblyType.GetInterfaces())
{
if (requiredInterface.FullName.EndsWith("DataProvider"))
{
var typeFrom = assemblyType.GetInterface(requiredInterface.Name);
var typeTo = assemblyType;
var injector = GetInjectorConstructor(assemblyType.Module.Name);
RegisterType(typeFrom, typeTo, false, injector);
}
}
continue;
}
...
}
private InjectionConstructor GetInjectorConstructor(String moduleName)
{
...
connString = String.Concat("Data Source=MySqlServer, ";Initial Catalog=", catalogName, ";Application Name=", applicationName, ";Integrated Security=True; );
var conn = new SqlConnection(connString);
return new InjectionConstructor(conn, true);
}

Ninject Conditional Self bind to change scope (For Task-scheduler) not working properly?

Within MVC Web Application DbContext binding work properly with InRequestScope()
kernel.Bind<DbContext>().ToSelf().InRequestScope();
kernel.Bind<IUnitOfWork<DbContext>>().To<UnitOfWork<DbContext>>();
But from a Task Scheduler call DbContext in InRequestScope() unable to update Db Table (without any error), until I change Binding to InSingletonScope() OR InThreadScope()
Question: So is their any way change scope to InSingletonScope() / InThreadScope() for a Task Scheduler Call. ?
// For Task Scheduler Call, I tried bellow bind, but not working properly
kernel.Bind<DbContext>().ToSelf()
.When(request => request.Target.Type.Namespace.StartsWith("NameSpace.ClassName"))
.InSingletonScope();
** And probably I miss some thing. Need help.
Code Snippet Updated
#region Commented Code
public EmailTask() : this
( DependencyResolver.Current.GetService<IMessageManager>(),
, DependencyResolver.Current.GetService<IUnitOfWork<DbContext>>()) { }
#endregion
public EmailTask(IMessageManager messageManager, IUnitOfWork<DbContext> unitOfWork)
{
this._messageManager = messageManager;
this._unitOfWork = unitOfWork;
ProcessEmail();
}
public class NonRequestScopedParameter : IParameter { ... }
public void ProcessEmail()
{
var temp = SomeRepository.GetAll();
SendEmail(temp);
temp.Date = DateTime.Now;
SomeRepository.Update(temp);
unitOfWork.Commit();
}
public class ExecuteEmailTask : ITask
{
private readonly IResolutionRoot _resolutionRoot;
private int _maxTries = 5;
public ExecuteEmailTask(IResolutionRoot resolutionRoot)
{
_resolutionRoot = resolutionRoot;
}
public void Execute(XmlNode node)
{
XmlAttribute attribute1 = node.Attributes["maxTries"];
if (attribute1 != null && !String.IsNullOrEmpty(attribute1.Value))
{
this._maxTries = int.Parse(attribute1.Value);
}
/// send email messages
var task = _resolutionRoot.Get<EmailTask>(new NonRequestScopedParameter());
}
}
In Web.Config
<ScheduleTasks>
<Thread seconds="60">
<task name="ExecuteEmailTask" type="namespace.ExecuteEmailTask, AssemblyName" enabled="true" stopOnError="false" maxTries="5"/>
</Thread>
</ScheduleTasks>
In Global.asax
protected void Application_Start()
{
/* intialize Task */
TaskConfig.Init();
TaskManager.Instance.Initialize(TaskConfig.ScheduleTasks);
TaskManager.Instance.Start();
}
Ninject Bind Syntax
kernel.Bind<DbContext>().ToSelf().InRequestScope(); // Default bind
kernel.Bind<DbContext>().ToSelf()
.When(x => x.Parameters.OfType<NonRequestScopedParameter>().Any())
.InCallScope(); // For Scheduler
Note: EmailTask class also have SomeReposity as a Constructor Argument.
Queries:-
But what is the bind syntax to resolve TaskScheduler(IResolutionRoot resolutionRoot) ?
What is the configuration code to run TaskScheduler ?
As say to put IFakeDbContext directly into constructor, can this work with IUnitOfWork<FakeDbContext> ?
Problem
Task unable to call with Overloaded Constructor , it is only able to call TaskScheduler default Constructor.
Question 4: Can any way to invoke TaskScheduler(IResolutionRoot resolutionRoot) from TaskScheduler default constructor ?
Sample Code Snippet to create Task & run using System.Threading.Timer
private ITask createTask()
{
if (this.Enabled && (this._task == null))
{
if (this._taskType != null)
{
this._task = Activator.CreateInstance(this._taskType) as ITask;
}
this._enabled = this._task != null;
}
return this._task;
}
Question 5: Can I resolve TaskScheduler(IResolutionRoot resolutionRoot) here ?
Solved
public ExecuteEmailTask() :
this(DependencyResolver.Current.GetService<IResolutionRoot>())
OR
public ExecuteEmailTask() : this(new Bootstrapper().Kernel) { }
public ExecuteEmailTask(IResolutionRoot resolutionRoot)
{
_resolutionRoot = resolutionRoot;
}
First of, you should note that InSingletonScope() is usually a bad idea for DbContext's/Sessions. What happens if some other service changes data in the meantime? I would recommend investigating what effects this has.
For the scenario you first described, a correctly formulated .When(...) should work.
As an alternative to the .When(...) binding you could also use a .Named("FooBar") binding.
The constructor of the scheduled task would then need to look like:
ctor(Named["FooBar"] DbContext dbContext);
However, note, that this only (easily) works in case you need to inject the DbContext into a single constructor. If the task features dependencies and these need the same DbContext instance, too, it gets a bit tricker.
Since you updated your answer and say that this is the case, i would recommend an entirely different approach: Using a request parameter as basis for the When(...) condition combined with InCallScope binding. See below for an example.
Brace yourself, this is ab it of code :) The implementation requires the ninject.extensions.NamedScope extension (nuget).
I've also used xUnit and FluentAssertions nuget packages to execute the tests.
public class Test
{
// the two implementations are just for demonstration and easy verification purposes. You will only use one DbContext type.
public interface IFakeDbContext { }
public class RequestScopeDbContext : IFakeDbContext { }
public class CallScopeDbContext : IFakeDbContext { }
public class SomeTask
{
public IFakeDbContext FakeDbContext { get; set; }
public Dependency1 Dependency1 { get; set; }
public Dependency2 Dependency2 { get; set; }
public SomeTask(IFakeDbContext fakeDbContext, Dependency1 dependency1, Dependency2 dependency2)
{
FakeDbContext = fakeDbContext;
Dependency1 = dependency1;
Dependency2 = dependency2;
}
}
public class Dependency1
{
public IFakeDbContext FakeDbContext { get; set; }
public Dependency1(IFakeDbContext fakeDbContext)
{
FakeDbContext = fakeDbContext;
}
}
public class Dependency2
{
public IFakeDbContext FakeDbContext { get; set; }
public Dependency2(IFakeDbContext fakeDbContext)
{
FakeDbContext = fakeDbContext;
}
}
public class TaskScheduler
{
private readonly IResolutionRoot _resolutionRoot;
public TaskScheduler(IResolutionRoot resolutionRoot)
{
_resolutionRoot = resolutionRoot;
}
public SomeTask CreateScheduledTaskNow()
{
return _resolutionRoot.Get<SomeTask>(new NonRequestScopedParameter());
}
}
public class NonRequestScopedParameter : Ninject.Parameters.IParameter
{
public bool Equals(IParameter other)
{
if (other == null)
{
return false;
}
return other is NonRequestScopedParameter;
}
public object GetValue(IContext context, ITarget target)
{
throw new NotSupportedException("this parameter does not provide a value");
}
public string Name
{
get { return typeof(NonRequestScopedParameter).Name; }
}
// this is very important
public bool ShouldInherit
{
get { return true; }
}
}
[Fact]
public void FactMethodName()
{
var kernel = new StandardKernel();
// this is the default binding
kernel.Bind<IFakeDbContext>().To<RequestScopeDbContext>();
// this binding is _only_ used when the request contains a NonRequestScopedParameter
// in call scope means, that all objects built in the a single request get the same instance
kernel.Bind<IFakeDbContext>().To<CallScopeDbContext>()
.When(x => x.Parameters.OfType<NonRequestScopedParameter>().Any())
.InCallScope();
// let's try it out!
var task = kernel.Get<SomeTask>(new NonRequestScopedParameter());
// verify that the correct binding was used
task.FakeDbContext.Should().BeOfType<CallScopeDbContext>();
// verify that all children of the task get injected the same task instance
task.FakeDbContext.Should()
.Be(task.Dependency1.FakeDbContext)
.And.Be(task.Dependency2.FakeDbContext);
}
}
Since, as you say, the task scheduler does not make use of the IoC to create the task, it only supports a parameterless constructor. In that case you can make use DependencyResolver.Current (however, note that i'm in no way an expert on asp.net /MVC so i'm not making any claims that this is thread safe or working 100% reliably):
public class TaskExecutor : ITask
{
public TaskExecutor()
: this(DependencyResolver.Current.GetService<IResolutionRoot>())
{}
internal TaskExecutor(IResolutionRoot resolutionRoot)
{
this.resolutionRoot = resolutionRoot;
}
public void Execute()
{
IFooTask actualTask = this.resolution.Get<IFooTask>(new NonRequestScopedParameter());
actualTask.Execute();
}
}

is thread switching possible during request processing?

I have an MVC application, which also uses EF and a simple Unit of work pattern implementation.
Here's what my UnitOfWork looks like:
public class UnitOfWork : IUnitOfWork
{
[ThreadStatic]
private static UnitOfWork _current;
private MyContext _context;
public static UnitOfWork Current
{
get { return _current; }
}
public UnitOfWork()
{
_current = this;
}
public MyContext GetContext()
{
if(_context == null)
_context = new MyContext();
return _context;
}
public int Commit()
{
return _context == null ? 0 : _context.SaveChanges();
}
public void Dispose()
{
_current = null;
if(_context != null)
_context.Dispose();
}
}
I have a generic repository which encapsulates common db operations:
public class GenericRepository<TEntity, TEntityKey> where TEntity : class
{
private MyContext _context;
private MyContext Context
{
get { return _context ?? (_context = UnitOfWork.Current.GetContext()); }
}
public void Add(TEntity newEntity)
{
_context.Set<TEntity>().Add(newEntity);
}
//Other methods...
}
How it is used:
using(var unitOfWork = _unitOfWorkFactory.Create())
{
_repository.Add(newEntity);
unitOfWork.Commit();
}
So, the question is if it is possible, that the MVC framework internally switches threads while processing a request. Since the current UnitOfWork is thread static, such a switch will cause a NullReferenceException when calling UnitOfWork.Current (please correct if I'm not right).

Resources