What is syntax to map both ways in AutoMapper Net Core2?
I need to map ProductViewModel and ProductDto both ways. This is not working,
Startup.cs
var config = new MapperConfiguration
(
cfg => cfg.CreateMap<Models.ProductDto, Models.ProductViewModel>(),
cfg => cfg.CreateMap<Models.ProductViewModel, Models.ProductDto>()
);
var mapper = config.CreateMapper();
I'd rather create a separate initializer and mapper. e.g here is my AutoMapperStartupTask class.
public static class AutoMapperStartupTask
{
public static void Execute()
{
Mapper.Initialize(
cfg =>
{
cfg.CreateMap<ProductViewModel, ProductDto>()
.ReverseMap();
});
}
}
And Mapper
public static class DtoMapping
{
public static ProductViewModel ToModel(this ProductDto dto)
{
return Mapper.Map<ProductDto, ProductViewModel>(dto);
}
public static ProductDto ToDto(this ProductViewModel dto)
{
return Mapper.Map<ProductViewModel, ProductDto>(dto);
}
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
AutoMapperStartupTask.Execute();
}
Use in Controller
var dto = productModel.ToDto();
var model = productDto.ToModel();
Related
I am implementing custom [Authorize] attribute. Inside the OnAuthorization method in IdentityAuthorizeFilter Class, I need to have access to DBContext to perform Database checks. I can not pass the context in the constructor of the Class. How can I access DBContext inside this class ?
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<SecureContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("SecureContext")));
}
CustomAuthorize:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class IdentityAuthorizeAttribute : TypeFilterAttribute
{
public IdentityAuthorizeAttribute(string permissions)
: base(typeof(IdentityAuthorizeFilter))
{
Arguments = new object[] { permissions };
}
}
public class IdentityAuthorizeFilter : IAuthorizationFilter
{
public IdentityAuthorizeFilter(string permissions) => Permissions = permissions;
public string Permissions { get; set; }
[Authorize]
public void OnAuthorization(AuthorizationFilterContext context)
{
var claims = context.HttpContext.User.Claims.ToList();
var auth = context.HttpContext.User.Identity.IsAuthenticated;
//Access DB Context
if (!isAuthorized)
context.Result = new UnauthorizedResult();
}
}
Does this work?
var dbContext = context.HttpContext.RequestServices.GetRequiredService<SecureContext>();
I'm not sure if it will, but I did something similar using Microsoft's AddMicrosoftIdentityWebApp method like this:
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(
options =>
{
configuration.Bind("AzureAD", options);
options.Events ??= new OpenIdConnectEvents();
options.Events.OnTokenValidated += async tokenValidatedContext =>
{
var dbContext = tokenValidatedContext.HttpContext.RequestServices.GetRequiredService<dbContext>();
// Do stuff with db context here
};
});
Will anything bad happen if there are multiple registrations of services in .net core's DI? For example let's say we have the following code
public void ConfigureServices(IServiceCollection services)
{
//....
services.AddHealthChecks();
//...
}
And in another (extension maybe) class we use services.AddHealthChecks() again. Will this mess the DI's container or not?
Thanks in advance
You can register a service several times without an exception has thrown. The problem is when you register a service several times with different scope. consider following example:
public interface IMyInterface
{
void Print();
}
public class MyInterface : IMyInterface
{
public void Print()
{
}
}
And let's register IMyInterface with two different scopes:
internal class Program
{
private static void Main(string[] args)
{
IServiceCollection services = new ServiceCollection();
services.AddScoped<IMyInterface, MyInterface>();
services.AddSingleton<IMyInterface, MyInterface>();
var provider = services.BuildServiceProvider();
for (var i = 0; i < 5; i++)
{
var scope = provider.CreateScope();
using (scope)
{
var myInterface = scope.ServiceProvider.GetService<IMyInterface>();
Console.WriteLine(myInterface.GetHashCode());
}
}
}
}
First, register IMyInterface in the following order:
services.AddScoped<IMyInterface, MyInterface>();
services.AddSingleton<IMyInterface, MyInterface>();
As you can see we get a singleton instance of MyInterface and the hashcode is the same.
Now let's change it to this:
services.AddSingleton<IMyInterface, MyInterface>();
services.AddScoped<IMyInterface, MyInterface>();
Now we get the scoped type of MyInterface and the hashcode is different each time. You always get the last registered scope of your type.
Look at AddHealthChecks code:
public static IHealthChecksBuilder AddHealthChecks(this IServiceCollection services)
{
services.TryAddSingleton<HealthCheckService, DefaultHealthCheckService>();
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService, HealthCheckPublisherHostedService>());
return new HealthChecksBuilder(services);
}
By adding services.AddHealthChecks(); more than one time, you just registering HealthCheckService and IHostedService as a singleton services and I think it doesn't affect the health check functionality.
With the following extension, you can find duplicate registration:
public static class ServiceDescription
{
private static List<IGrouping<Type, ServiceDescriptor>> Descriptors;
public static IHostBuilder ConfigureServiceDescriptionCheck(this IHostBuilder hostBuilder)
{
hostBuilder.ConfigureServices(services =>
{
Descriptors = services.Where(i => !i.ServiceType.Assembly.FullName.Contains("Microsoft"))
.GroupBy(p => p.ServiceType)
.Where(x => x.Count() > 1).ToList();
});
return hostBuilder;
}
public static IHost UseServiceDescriptionCheck(this IHost host)
{
var logger = host.Services.GetRequiredService<ILogger<Program>>();
Descriptors.ForEach(item =>
{
var count = item.Count();
logger.LogWarning("Service of type {Key} has been registered {count} times", item.Key, count);
});
return host;
}
}
And use it in this way:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().UseServiceDescriptionCheck().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.ConfigureServiceDescriptionCheck();
}
Read this article to get more details.
Does the SimpleInjectorContainerAdapter support Sagas that are registered through the SimpleInjector container, using the following code I always get the exception;
type RebusPlaypen.MyMessageA, RebusPlaypen could not be dispatched to any handl
ers
The following code demonstrates the issue. Could this be that I am implementing the Saga registration incorrectly, or does the SimpleInjectorContainerAdapter not support this type of registration ?
using Rebus.Bus;
using Rebus.Config;
using Rebus.Handlers;
using Rebus.Retry.Simple;
using Rebus.Routing.TypeBased;
using Rebus.Sagas;
using Rebus.SimpleInjector;
using Rebus.Transport.InMem;
using SimpleInjector;
using System;
using System.Threading.Tasks;
// Rebus.3.1.2
// Rebus.SimpleInjector.3.0.0
namespace RebusPlaypen
{
public interface IMyDependency
{
void DoSomethingGood();
}
public class MyDependency : IMyDependency
{
public void DoSomethingGood()
{
Console.WriteLine("I've done something");
}
}
public class MyMessageA
{
public Guid CollationId { get; set; }
public string FaveIceCreamFlavour { get; set; }
}
public class MyMessageB
{
public Guid CollationId { get; set; }
public string FaveBand{ get; set; }
}
public class MyMessageSagaData : ISagaData
{
public Guid Id {get;set;}
public int Revision {get;set;}
public Guid CollationId {get;set;}
public bool HasFaveBand { get; set; }
}
public interface IMyMessageSaga : IAmInitiatedBy<MyMessageA>,
IHandleMessages<MyMessageB>
{
}
public class MyMessageSaga: Saga<MyMessageSagaData>,
IMyMessageSaga
{
readonly IMyDependency _myDependency;
readonly IBus _bus;
public MyMessageSaga(IMyDependency myDependency,
IBus bus)
{
_myDependency = myDependency;
_bus = bus;
}
protected override void CorrelateMessages(ICorrelationConfig<MyMessageSagaData> config)
{
config.Correlate<MyMessageA>(s => s.CollationId, d => d.CollationId);
config.Correlate<MyMessageB>(s => s.CollationId, d => d.CollationId);
}
public async Task Handle(MyMessageA message)
{
Console.WriteLine("Handled MyMessageA");
_myDependency.DoSomethingGood();
await _bus.Send(new MyMessageB { CollationId = message.CollationId, FaveBand = "Depeche Mode" });
await PossiblyPerformCompleteAction();
}
public async Task Handle(MyMessageB message)
{
Console.WriteLine("Handled MyMessageB");
_myDependency.DoSomethingGood();
Data.HasFaveBand = true;
await PossiblyPerformCompleteAction();
}
async Task PossiblyPerformCompleteAction()
{
if (Data.HasFaveBand)
{
MarkAsComplete();
}
}
}
public static class RebusSimpleInjectorSagaDemo
{
public static void Run()
{
var container = new Container();
container.Register<IMyDependency, MyDependency>();
container.Register<MyMessageSaga>(Lifestyle.Transient);
container.Register<IMyMessageSaga>(() => container.GetInstance<MyMessageSaga>(), Lifestyle.Transient);
var network = new InMemNetwork(true);
var adapter = new SimpleInjectorContainerAdapter(container);
var _bus = Configure
.With(adapter)
.Logging(l => l.ColoredConsole(Rebus.Logging.LogLevel.Error))
.Transport(t => t.UseInMemoryTransport(network,"my_nice_queue"))
.Routing(r => r.TypeBased().MapAssemblyOf<MyMessageA>("my_nice_queue"))
.Options(o =>
{
o.SetNumberOfWorkers(1);
o.SetMaxParallelism(1);
o.SimpleRetryStrategy(maxDeliveryAttempts: 1);
})
.Start();
container.Verify();
_bus.Send(new MyMessageA { CollationId = Guid.NewGuid(), FaveIceCreamFlavour = "Strawberry" }).Wait();
Console.WriteLine("Running");
Console.ReadLine();
}
}
}
For completeness, the following changes allowed the code in the original question to work correctly with the Simple Injector container;
public static class RebusSimpleInjectorSagaDemo
{
public static void Run()
{
var container = new Container();
container.Register<IMyDependency, MyDependency>();
// The missing registration
container.RegisterCollection(typeof(IHandleMessages<>), new [] {Assembly.GetExecutingAssembly()});**
var network = new InMemNetwork(true);
var adapter = new SimpleInjectorContainerAdapter(container);
var _bus = Configure
.With(adapter)
.Logging(l => l.ColoredConsole(Rebus.Logging.LogLevel.Error))
.Transport(t => t.UseInMemoryTransport(network,"my_nice_queue"))
.Routing(r => r.TypeBased().MapAssemblyOf<MyMessageA>("my_nice_queue"))
.Options(o =>
{
o.SetNumberOfWorkers(1);
o.SetMaxParallelism(1);
o.SimpleRetryStrategy(maxDeliveryAttempts: 1);
})
.Start();
container.Verify();
_bus.Send(new MyMessageA { CollationId = Guid.NewGuid(), FaveIceCreamFlavour = "Strawberry" }).Wait();
Console.WriteLine("Running");
Console.ReadLine();
}
}
No matter which IoC container you use, you must ensure that your handlers are resolved by the IHandleMessages<TMessage> implementations they provide.
If you try and
container.GetAllInstances<IHandleMessages<MyMessageA>>();
or
container.GetAllInstances<IHandleMessages<MyMessageB>>();
you will see that no handlers are returned. That's why Rebus cannot find any handlers to dispatch your messages to :)
I'm new to NHibernate and trying to use it in ASP.NET WEB API. Firstly I used it successfully with one table named "Category" which the controller class is as follow:
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using TestMVCProject.Web.Api.HttpFetchers;
using TestMVCProject.Web.Api.Models;
using TestMVCProject.Web.Api.TypeMappers;
using TestMVCProject.Web.Common;
//using TestMVCProject.Web.Common.Security;
using NHibernate;
namespace TestMVCProject.Web.Api.Controllers
{
[LoggingNHibernateSession]
public class CategoryController : ApiController
{
private readonly ISession _session;
private readonly ICategoryMapper _categoryMapper;
private readonly IHttpCategoryFetcher _categoryFetcher;
public CategoryController(
ISession session,
ICategoryMapper categoryMapper,
IHttpCategoryFetcher categoryFetcher)
{
_session = session;
_categoryMapper = categoryMapper;
_categoryFetcher = categoryFetcher;
}
public IEnumerable<Category> Get()
{
return _session
.QueryOver<Data.Model.Category>()
.List()
.Select(_categoryMapper.CreateCategory)
.ToList();
}
public Category Get(long id)
{
var category = _categoryFetcher.GetCategory(id);
return _categoryMapper.CreateCategory(category);
}
public HttpResponseMessage Post(HttpRequestMessage request, Category category)
{
var modelCategory = new Data.Model.Category
{
Description = category.Description,
CategoryName = category.CategoryName
};
_session.Save(modelCategory);
var newCategory = _categoryMapper.CreateCategory(modelCategory);
//var href = newCategory.Links.First(x => x.Rel == "self").Href;
var response = request.CreateResponse(HttpStatusCode.Created, newCategory);
//response.Headers.Add("Location", href);
return response;
}
public HttpResponseMessage Delete()
{
var categories = _session.QueryOver<Data.Model.Category>().List();
foreach (var category in categories)
{
_session.Delete(category);
}
return new HttpResponseMessage(HttpStatusCode.OK);
}
public HttpResponseMessage Delete(long id)
{
var category = _session.Get<Data.Model.Category>(id);
if (category != null)
{
_session.Delete(category);
}
return new HttpResponseMessage(HttpStatusCode.OK);
}
public Category Put(long id, Category category)
{
var modelCateogry = _categoryFetcher.GetCategory(id);
modelCateogry.CategoryName = category.CategoryName;
modelCateogry.Description = category.Description;
_session.SaveOrUpdate(modelCateogry);
return _categoryMapper.CreateCategory(modelCateogry);
}
}
}
But when I add The "Product" table which has a foreign key of the Category table, the product controller doesn't work and throws below exception:
No session bound to the current context
ProductController class is as follow:
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using TestMVCProject.Web.Api.HttpFetchers;
using TestMVCProject.Web.Api.Models;
using TestMVCProject.Web.Api.TypeMappers;
using TestMVCProject.Web.Common;
//using TestMVCProject.Web.Common.Security;
using NHibernate;
namespace TestMVCProject.Web.Api.Controllers
{
[LoggingNHibernateSession]
public class ProductController : ApiController
{
private readonly ISession _session;
private readonly IProductMapper _productMapper;
private readonly IHttpProductFetcher _productFetcher;
public ProductController(
ISession session,
IProductMapper productMapper,
IHttpProductFetcher productFetcher)
{
_session = session;
_productMapper = productMapper;
_productFetcher = productFetcher;
}
public IEnumerable<Product> Get()
{
return _session
.QueryOver<Data.Model.Product>()
.List()
.Select(_productMapper.CreateProduct)
.ToList();
}
public Product Get(long id)
{
var product = _productFetcher.GetProduct(id);
return _productMapper.CreateProduct(product);
}
public HttpResponseMessage Post(HttpRequestMessage request, Product product)
{
var modelProduct = new Data.Model.Product
{
Description = product.Description,
ProductName = product.ProductName
};
_session.Save(modelProduct);
var newProduct = _productMapper.CreateProduct(modelProduct);
//var href = newproduct.Links.First(x => x.Rel == "self").Href;
var response = request.CreateResponse(HttpStatusCode.Created, newProduct);
//response.Headers.Add("Location", href);
return response;
}
public HttpResponseMessage Delete()
{
var categories = _session.QueryOver<Data.Model.Product>().List();
foreach (var product in categories)
{
_session.Delete(product);
}
return new HttpResponseMessage(HttpStatusCode.OK);
}
public HttpResponseMessage Delete(long id)
{
var product = _session.Get<Data.Model.Product>(id);
if (product != null)
{
_session.Delete(product);
}
return new HttpResponseMessage(HttpStatusCode.OK);
}
public Product Put(long id, Product product)
{
var modelProduct = _productFetcher.GetProduct(id);
modelProduct.ProductName = product.ProductName;
modelProduct.Description = product.Description;
_session.SaveOrUpdate(modelProduct);
return _productMapper.CreateProduct(modelProduct);
}
}
}
and the mapping class for Product table:
using TestMVCProject.Data.Model;
using FluentNHibernate.Mapping;
namespace TestMVCProject.Data.SqlServer.Mapping
{
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Id(x => x.ProductId);
Map(x => x.ProductName).Not.Nullable();
Map(x => x.Description).Nullable();
Map(x => x.CreateDate).Not.Nullable();
Map(x => x.Price).Not.Nullable();
References<Category>(x => x.CategoryId).Not.Nullable();
}
}
}
What is wrong?
Your snippets are missing the way, how the ISessionFactory is created and how ISession is passed into your controllers... You should follow this really comprehensive story (by Piotr Walat):
NHibernate session management in ASP.NET Web API
Where you can see that we, can use 2.3. Contextual Sessions:
NHibernate.Context.WebSessionContext - stores the current session in HttpContext. You are responsible to bind and unbind an ISession instance with static methods of class CurrentSessionContext.
The configuration
<session-factory>
..
<property name="current_session_context_class">web</property>
</session-factory>
In the article you can check that we need at the app start initialize factory (just an extract):
public class WebApiApplication : System.Web.HttpApplication
{
private void InitializeSessionFactory() { ... }
protected void Application_Start()
{
InitializeSessionFactory();
...
Next we should create some AOP filter (just an extract):
public class NhSessionManagementAttribute : ActionFilterAttribute
{
...
public override void OnActionExecuting(HttpActionContext actionContext)
{
// init session
var session = SessionFactory.OpenSession();
...
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
// close session
...
session = CurrentSessionContext.Unbind(SessionFactory);
}
For more details check the source mentioned above
Your approach of passing the session to the constructor of the controller factory does not seems to be working, there are a few ways to do this
1. Using dependency injection
If you are using a dependency injection framework, you have to configure controller so that it's constructed per request, it should looks like this (I have used the code for Ninject)
Step 1 - setup the session for injection
public class DIModule : NinjectModule
{
public override void Load()
{
this.Bind<ISessionFactory>()... bind to the session factory
this.Bind<ISession>().ToMethod(ctx => ctx.Kernel.Get<ISessionFactory>().OpenSession())
.InRequestScope();
}
private ISession CreateSessionProxy(IContext ctx)
{
var session = (ISession)this.proxyGenerator.CreateInterfaceProxyWithoutTarget(typeof(ISession), new[] { typeof(ISessionImplementor) }, ctx.Kernel.Get<SessionInterceptor>());
return session;
}
}
Step 2 - Create the controller factory so that it will inject the session when resolving
public class NinjectControllerFactory : DefaultControllerFactory, IDependencyResolver
{
private IDependencyResolver _defaultResolver;
public NinjectControllerFactory(IDependencyResolver defaultResolver)
{
_defaultResolver = defaultResolver;
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
return controllerType == null
? null
: (IController)DependencyKernel.Kernel.Get(controllerType);
}
public IDependencyScope BeginScope()
{
return this;
}
public object GetService(Type serviceType)
{
try
{
return DependencyKernel.Kernel.Get(serviceType);
}
catch (Exception)
{
return GetService(serviceType);
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
try
{
object item = DependencyKernel.Kernel.Get(serviceType);
return new List<object>() {item};
}
catch (Exception)
{
return GetServices(serviceType);
}
}
public void Dispose()
{
}
}
Step 3 - Register the controller factory
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
var factory = new NinjectControllerFactory(GlobalConfiguration.Configuration.DependencyResolver);
ControllerBuilder.Current.SetControllerFactory(factory);
GlobalConfiguration.Configuration.DependencyResolver = factory;
}
}
Now what will happen is that when your controller is created it will inject the a new NH session per each request.
2. Using a filter
This is much simpler, but you may need to change your controllers a bit this to work,
Step 1 - Setup the correct session context for the factory
_sessionFactory = CreateConfiguration()
.ExposeConfiguration(c => c.SetProperty("current_session_context_class","web"))
.BuildSessionFactory();
Step 2 - Create the filter
public class SessionPerRequestAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var session = SessionFactory.OpenSession();
NHibernate.Context.CurrentSessionContext.Bind(session);
base.OnActionExecuting(actionContext);
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var session = SessionFactory.GetCurrentSession();
session.Flush();
session.Clear();
session.Close();
base.OnActionExecuted(actionExecutedContext);
}
}
Step 3 - Register the filter in global configuration
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
//Do other config here
config.Filters.Add(new SessionPerRequestAttribute());
}
}
Step 4 - Modify your controller a bit,
public class CategoryController : ApiController
{
private readonly ICategoryMapper _categoryMapper;
private readonly IHttpCategoryFetcher _categoryFetcher;
public CategoryController(
ICategoryMapper categoryMapper,
IHttpCategoryFetcher categoryFetcher)
{
_categoryMapper = categoryMapper;
_categoryFetcher = categoryFetcher;
}
public IEnumerable<Category> Get()
{
var session = SessionFactory.GetCurrentSession();
return session
.QueryOver<Data.Model.Category>()
.List()
.Select(_categoryMapper.CreateCategory)
.ToList();
}
}
Here what happens is, when a request comes it will create a new session and it is bound to the request context and same is used for the web API method.
I've see n a lot of discussions surrounding HttpSessionState and asp.net MVC.
I'm trying to write tests for an asp.net application and wondering if it's possible to mock the HttpSessionState and if so, how?
I'm currently using Rhino Mocks and Nunit
Gilbert,
Maybe I'm too late for you. I'm using MSpec, but I think the concepts are similar. I needed to mock several components of the HttpContext in the controllers under test.
I started with these following classes to mock up the necessary (for my purposes) components in the HttpContextBase. I overrode only the necessary pieces inside the classes. Your needs will vary as to the mocks you need in the controller. It's fairly easy to add mocks as needed once you understand the pattern.
public class MockHttpContext : HttpContextBase
{
private readonly HttpRequestBase _request = new MockHttpRequest();
private readonly HttpServerUtilityBase _server = new MockHttpServerUtilityBase();
private HttpSessionStateBase _session = new MockHttpSession();
public override HttpRequestBase Request
{
get { return _request; }
}
public override HttpServerUtilityBase Server
{
get { return _server; }
}
public override HttpSessionStateBase Session
{
get { return _session; }
}
}
public class MockHttpRequest : HttpRequestBase
{
private Uri _url = new Uri("http://www.mockrequest.moc/Controller/Action");
public override Uri Url
{
get { return _url; }
}
}
public class MockHttpServerUtilityBase : HttpServerUtilityBase
{
public override string UrlEncode(string s)
{
//return base.UrlEncode(s);
return s; // Not doing anything (this is just a Mock)
}
}
public class MockHttpSession : HttpSessionStateBase
{
// Started with sample http://stackoverflow.com/questions/524457/how-do-you-mock-the-session-object-collection-using-moq
// from http://stackoverflow.com/users/81730/ronnblack
System.Collections.Generic.Dictionary<string, object> _sessionStorage = new System.Collections.Generic.Dictionary<string,object>();
public override object this[string name]
{
get { return _sessionStorage[name]; }
set { _sessionStorage[name] = value; }
}
public override void Add(string name, object value)
{
_sessionStorage[name] = value;
}
}
Here is how I setup the Controller Context to use the mocks (MSpec). This is setup for the actual tests on the contoller (the tests derive from this class)
public abstract class BlahBlahControllerContext
{
protected static BlahBlahController controller;
Establish context = () =>
{
controller = new BlahBlahController();
controller.ControllerContext = new ControllerContext()
{
Controller = controller,
RequestContext = new RequestContext(new MockHttpContext(), new RouteData()),
};
};
}
To further illustrate here is a test (Specification in MSpec world) that uses the mock session:
[Subject("ACCOUNT: Retrieve Password")]
public class retrieve_password_displays_retrieve_password2_page_on_success : BlahBlahControllerContext
{
static ActionResult result;
static RetrievePasswordModel model;
Establish context = () =>
{
model = new RetrievePasswordModel()
{
UserName = "Mike"
};
};
Because of = () =>
{
result = controller.RetrievePassword(model);
};
It should_return_a_RedirectToRouteResult = () =>
{
result.is_a_redirect_to_route_and().action_name().ShouldEqual("RetrievePassword2");
};
It session_should_contain_UN_value = () =>
{
controller.HttpContext.Session["UN"].ShouldEqual("Mike");
};
It session_should_contain_PQ_value = () =>
{
controller.HttpContext.Session["PQ"].ShouldEqual("Question");
};
}
I realize this doesn't use Rhino Mocks. I hope it illustrates the principles and readers can adopt it to their specific tools and methods.
If you need to instantiate exactly HttpSessionState for legacy code tests, you can leverage FormatterServices mechanism to get uninitialized object. To get it working it is needed to set private _container field though, like in internal constructor
Example:
var state = (HttpSessionState) System.Runtime.Serialization
.FormatterServices.GetUninitializedObject(typeof(HttpSessionState));
var containerFld = typeof(HttpSessionState).GetField(
"_container", BindingFlags.Instance | BindingFlags.NonPublic);
var itemCollection = new SessionStateItemCollection();
itemCollection["element"] = 1;
containerFld.SetValue(
state,
new HttpSessionStateContainer(
"1",
itemCollection,
new HttpStaticObjectsCollection(),
900,
true,
HttpCookieMode.UseCookies,
SessionStateMode.InProc,
false
)
);
look at the HttpSessionStateBase and HttpSessionStateWrapper classes in System.Web.Abstractions. HttpSessionStateBase is the abstract class from which HttpSessionState inherits, and HttpSessionStateWrapper is used to wrap a sealed class in an abstract class, which you can then mock in your tests.
A lot of the System.Web classes are sealed (for example, HttpSessionState), so it's a real pain to test your code when you have methods and classes that interact with them. One pattern I like to use to get around this looks like the following:
public void DoSomething(HttpSessionState state)
{
// take this HttpSeassionState and create an abstract HttpSessionStateBase
// instance
DoSomething(new HttpSessionStateWrapper(state));
}
internal void DoSomething(HttpSessionStateBase state)
{
// my actual logic for working with the session state
}
The public method is difficult to test, because HttpSessionState is sealed, and you can't mock it. However, the internal method operates on an HttpSessionStateBase instance, which you can mock. Note that I've marked it as internal because I don't want the outside world to be able to access that method. However, I do want my tests to be able to access that, so I'll modify my AssemblyInfo.cs to include something like this:
[assembly: InternalsVisibleTo("Vendor.Utilities.Tests")]
Finally, my test for this would look something like this:
[Test]
public void Test_DoSomething()
{
HttpSessionStateBase state = MockRepository.PartialMock<HttpSessionStateBase>();
state.Expect(s => ...);
MyClass.DoSomething(state);
state.VerifyAllExpectations();
}
Hope that helps. Good luck!
This is what I made up based on others contribution...
public class MockWebContext
{
public Mock<RequestContext> RoutingRequestContext { get; private set; }
public Mock<HttpContextBase> Http { get; private set; }
public Mock<HttpServerUtilityBase> Server { get; private set; }
public Mock<HttpResponseBase> Response { get; private set; }
public Mock<HttpRequestBase> Request { get; private set; }
public Mock<HttpSessionStateBase> Session { get; private set; }
public Mock<ActionExecutingContext> ActionExecuting { get; private set; }
public HttpCookieCollection Cookies { get; private set; }
private IDictionary items;
public MockWebContext()
{
RoutingRequestContext = new Mock<RequestContext>(MockBehavior.Loose);
ActionExecuting = new Mock<ActionExecutingContext>(MockBehavior.Loose);
Http = new Mock<HttpContextBase>(MockBehavior.Loose);
Server = new Mock<HttpServerUtilityBase>(MockBehavior.Loose);
Response = new Mock<HttpResponseBase>(MockBehavior.Loose);
Request = new Mock<HttpRequestBase>(MockBehavior.Loose);
Session = new Mock<HttpSessionStateBase>(MockBehavior.Loose);
Cookies = new HttpCookieCollection();
items = new Dictionary<string, object>();
RoutingRequestContext.SetupGet(c => c.HttpContext).Returns(Http.Object);
ActionExecuting.SetupGet(c => c.HttpContext).Returns(Http.Object);
Http.SetupGet(c => c.Request).Returns(Request.Object);
Http.SetupGet(c => c.Response).Returns(Response.Object);
Http.SetupGet(c => c.Server).Returns(Server.Object);
Http.SetupGet(c => c.Session).Returns(Session.Object);
Http.SetupGet(c => c.Items).Returns(items);
Request.Setup(c => c.Cookies).Returns(Cookies);
Request.Setup(c => c.RequestContext).Returns(RoutingRequestContext.Object);
Response.Setup(c => c.Cookies).Returns(Cookies);
Session.Setup(c =>
c.Add(It.IsAny<string>(), It.IsAny<object>())
).Callback((string key, object value)=> items.Add(key, value));
Session.Setup(c =>
c.Remove(It.IsAny<string>())
).Callback((string key) => items.Remove(key));
Session.Setup(c =>
c.Clear()
).Callback(() => items.Clear());
Session.Setup(c =>
c[It.IsAny<string>()]
).Returns((string key)=> items[key]);
}
}
Check out the MvcContrib project.