HttpRuntime.Cache Equivalent for asp.net 5, MVC 6 - c#

So I've just moved from ASP.Net 4 to ASP.Net 5. Im at the moment trying to change a project so that it works in the new ASP.Net but of course there is going to be a load of errors.
Does anyone know what the equivalent extension is for HttpRuntime as I cant seem to find it anywhere. I'm using to cache an object client side.
HttpRuntime.Cache[Findqs.QuestionSetName]
'Findqs' is just a general object

You can an IMemoryCache implementation for caching data. There are different implementations of this including an in-memory cache, redis,sql server caching etc..
Quick and simple implemenation goes like this
Update your project.json file and add the below 2 items under dependencies section.
"Microsoft.Extensions.Caching.Abstractions": "1.0.0-rc1-final",
"Microsoft.Extensions.Caching.Memory": "1.0.0-rc1-final"
Saving this file will do a dnu restore and the needed assemblies will be added to your project.
Go to Startup.cs class, enable caching by calling the services.AddCaching() extension method in ConfigureServices method.
public void ConfigureServices(IServiceCollection services)
{
services.AddCaching();
services.AddMvc();
}
Now you can inject IMemoryCache to your class via constructor injection. The framework will resolve a concrete implementation for you and inject it to your class constructor.
public class HomeController : Controller
{
IMemoryCache memoryCache;
public HomeController(IMemoryCache memoryCache)
{
this.memoryCache = memoryCache;
}
public IActionResult Index()
{
var existingBadUsers = new List<int>();
var cacheKey = "BadUsers";
List<int> badUserIds = new List<int> { 5, 7, 8, 34 };
if(memoryCache.TryGetValue(cacheKey, out existingBadUsers))
{
var cachedUserIds = existingBadUsers;
}
else
{
memoryCache.Set(cacheKey, badUserIds);
}
return View();
}
}
Ideally you do not want to mix your caching within your controller. You may move it to another class/layer to keep everything readable and maintainable. You can still do the constructor injection there.
The official asp.net mvc repo has more samples for your reference.

My answer is focused on the "Does anyone know what the equivalent extension is for HttpRuntime as I cant seem to find it anywhere"
You tagged two different frameworks (.net and .net core), and for them there are two completely different caching implementations/solutions. The first one below is the one you were looking for:
1 - System.Runtime.Caching/MemoryCache
2 - Microsoft.Extensions.Caching.Memory/IMemoryCache
System.Runtime.Caching/MemoryCache:
This is pretty much the same as the old day's ASP.Net MVC's HttpRuntime.Cache. You can use it on ASP.Net CORE without any dependency injection. This is how to use it:
// First install 'System.Runtime.Caching' (NuGet package)
// Add a using
using System.Runtime.Caching;
// To get a value
var myString = MemoryCache.Default["itemCacheKey"];
// To store a value
MemoryCache.Default["itemCacheKey"] = myString;
Microsoft.Extensions.Caching.Memory
This one is tightly coupled with Dependency Injection, and is the recommended way to do it on ASP.Net CORE. This is one way to implement it:
// In asp.net core's Startup add this:
public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
}
Using it on a controller:
// Add a using
using Microsoft.Extensions.Caching.Memory;
// In your controller's constructor, you add the dependency on the 'IMemoryCache'
public class HomeController : Controller
{
private IMemoryCache _cache;
public HomeController(IMemoryCache memoryCache)
{
_cache = memoryCache;
}
public void Test()
{
// To get a value
string myString = null;
if (_cache.TryGetValue("itemCacheKey", out myString))
{ /* key/value found - myString has the key cache's value*/ }
// To store a value
_cache.Set("itemCacheKey", myString);
}
}

Related

Can not resolve a generic service using manual interception with ASP.NET Core 2 and Castle.Core

I know there are a lot of question similar to this one but actually none of them solved my issue.
I created a new Asp.Net Core 2 application.
Now I am trying to use an intercepter for a specific service to fetch some data into this service(I am using Castle.Core nuget package).
I have a generic IConfigurationInterceptor<> and a real implementation ConfigurationInterceptor<>
Here is the interface:
public interface IConfigurationInterceptor<T> : IInterceptor where T : class { }
public class ConfigurationInterceptor<T> : IConfigurationInterceptor<T> where T : class
{
public ConfigurationInterceptor(ConfigurationInfo<T> configurationInfo,
some other services)
{
_configurationInfo = configurationInfo;
//.....
}
public void Intercept(IInvocation invocation)
{
invocation.ReturnValue = somefunc(someconfig, invocation.Arguments);
}
}
Then I have an extension method like below:
public static void AddSingletonConfiguration<TInterface, TImplementation>(
this IServiceCollection services, string featureName)
where TImplementation : class, TInterface where TInterface : class
{
var info = new ConfigurationInfo<TImplementation>(featureName, typeof(TInterface));
var generator = new ProxyGenerator();
services.AddSingleton(x =>
{
var ic = x.GetService<Func<ConfigurationInfo<TImplementation>,
IConfigurationInterceptor<TImplementation>>>();
var icTemp = ic.Invoke(info);
return (TInterface) generator.CreateInterfaceProxyWithoutTarget(
info.ServiceType, icTemp);
});
}
But when I get to this line of code:
var ic = x.GetService<Func<ConfigurationInfo<TImplementation>,
IConfigurationInterceptor<TImplementation>>>();
it returns me a null value for ic:
ConfigurationInfo class is just a simple class I create for storing some extra data.
public sealed class ConfigurationInfo<TImpl>
{
public Type ServiceType { get; }
public string FeatureName { get; }
public ConfigurationInfo(string featureName, Type serviceType)
{
FeatureName = featureName;
ServiceType = serviceType;
}
public override string ToString()
=> $"{FeatureName} ({ServiceType} -> {typeof(TImpl)})";
}
In my ConfigureServices I have these both lines:
services.AddSingleton(typeof(IConfigurationInterceptor<>),
typeof(ConfigurationInterceptor<>));
services.AddSingletonConfiguration<IStaticDataConfiguration, StaticDataConfiguration>(
"SomeFeatureKey");
I am not sure why ic variable is null because previously another project was using Autofac and was working perfectly but in the startup you would find something like this:
builder.RegisterGeneric(typeof(ConfigurationInterceptor<>))
.As(typeof(IConfigurationInterceptor<>)).SingleInstance();
builder.RegisterConfiguration<IStaticDataConfiguration, StaticDataConfiguration>(
"SomeFeatureKey");
and the extension method was like this one:
public static void RegisterConfiguration<TInterface, TImplementation>(
this ContainerBuilder builder, string featureName)
where TImplementation : class, TInterface
{
var info = new ConfigurationInfo<TImplementation>(featureName, typeof(TInterface));
var generator = new ProxyGenerator();
builder
.Register(c =>
{
var ic = c.Resolve<Func<ConfigurationInfo<TImplementation>,
IConfigurationInterceptor<TImplementation>>>()(info);
return generator.CreateInterfaceProxyWithoutTarget(info.ServiceType, ic);
})
.As<TInterface>()
.SingleInstance();
}
Any help would be appreaciated.
EDIT 1:
Now I changed from method GetService<> to method GetRequiredService<> and throws an exception like below:
No service for type 'System.Func'2[StaticDataProvider.DomainModel.ConfigurationInfo'1[StaticDataProvider.Services.StaticDataConfiguration],StaticDataProvider.Services.Interfaces.IConfigurationInterceptor'1[StaticDataProvider.Services.StaticDataConfiguration]]' has been registered.
EDIT 2:
To wrap it up here is the issue: In my current project in Asp.Net core I can not get a Func<X, B> while in the Asp.Net MVC 5 project(It is a whole different project) I can get a Func<X, B> using Autofac. I think this has to do with parametrized instantiation feature in Autofac provided by default: here
Now, I dont know if in Asp.Net Core default DI container has something like this 'parametrized instantiation' feature where it allows me resolving Func<X, B> instead of B.
I'm guessing the root of the problem is in the fairly complex manual wiring up of the interceptors.
If you're using interceptors with Autofac, it'd be better to use the Autofac.Extras.DynamicProxy2 package and wire up interceptors using the built-in Autofac functionality instead of trying to chain a bunch of resolutions together with functions and parameters. I see a lot of little gotchas in here like how you're setting up a singleton interface proxy without a target but I'm not entirely clear how the target gets added post-facto. There's a lot of complexity you can avoid by using the tools provided.
That said, I'm also looking at the exception message. Without a stack trace I can't 100% guarantee it, but a search on the Autofac source indicates that's not a message that came from Autofac - it's likely, then, a message from the default Microsoft.Extensions.DependencyInjection container. That indicates you may not actually have everything wired up the way you think you do.
I'd back up a bit and just get simple things working and ensure they're coming from Autofac. If you decide you don't want Autofac in play, make sure you've removed it entirely from the equation. Basically, just make sure it's clean and working in the general sense.
After that, add things back slowly, one at a time. I might recommend putting a reproduction in a unit test where you use these registration mechanisms and get things working without the complexity of the entire app weighing down. Unwind it from there. If it's too complex to unit test... maybe that's an indicator you should simplify it and refactor. Make it testable.
I'll leave my previous answer for posterity, but... The default Microsoft IoC provider is very simple and does not support all the features of Autofac. You won't get parameterized resolution or auto-generated factories from it.
Here is what I had to do:
Modified ConfigureService method like below:
public void ConfigureServices(IServiceCollection services)
{
IConfigurationInterceptor<T> GetConfigurationInterceptor<T>(ConfigurationInfo<T> info) where T : class
{
return new ConfigurationInterceptor<T>(info, services.GetService<IConfigurationProvider>(), Configuration);
}
services.AddSingletonConfiguration<IStaticDataConfiguration, StaticDataConfiguration>("someFeatureKey", GetConfigurationInterceptor);
}
Then modified extension methods like below:
public static void AddSingletonConfiguration<TInterface, TImplementation>(this IServiceCollection services,
string featureName, Func<ConfigurationInfo<TImplementation>, IConfigurationInterceptor<TImplementation>> ic) where TImplementation : class, TInterface where TInterface : class
{
var info = new ConfigurationInfo<TImplementation>(featureName, typeof(TInterface));
var generator = new ProxyGenerator();
services.AddSingleton(x =>
{
var icTemp = ic.Invoke(info);
return (TInterface) generator.CreateInterfaceProxyWithoutTarget(info.ServiceType, icTemp);
});
}
public static TInterface GetService<TInterface>(this IServiceCollection services) where TInterface : class
{
var serviceProvider = services.BuildServiceProvider();
return serviceProvider.GetRequiredService<TInterface>();
}
Now its working fine but the idea is that I had to create Func<X, B> myself and pass as a parameter to extension method.

How to Unit Test Startup.cs in .NET Core

How do people go about Unit Testing their Startup.cs classes in a .NET Core 2 application? All of the functionality seems to be provided by Static extensions methods which aren't mockable?
If you take this ConfigureServices method for example:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<BlogContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddMvc();
}
How can I write tests to ensure that AddDbContext(...) & AddMvc() are called - the choice of implementing all of this functionality via extension methods seems to have made it untestable?
Well yes, if you want to check the fact that extension method AddDbContext was called on services you are in trouble.
The good thing is that you shouldn't actually check exactly this fact.
Startup class is an application composition root. And when testing a composition root you want to check that it actually registers all dependencies required for instantiation of the root objects (controllers in the case of ASP.NET Core application).
Say you have following controller:
public class TestController : Controller
{
public TestController(ISomeDependency dependency)
{
}
}
You could try checking whether Startup has registered the type for ISomeDependency. But implementation of ISomeDependency could also require some other dependencies that you should check.
Eventually you end up with a test that has tons of checks for different dependencies but it does not actually guarantee that object resolution will not throw missing dependency exception. There is not too much value in such a test.
An approach that works well for me when testing a composition root is to use real dependency injection container. Then I call a composition root on it and assert that resolution of the root object does not throw.
It could not be considered as pure Unit Test because we use other non-stubbed class. But such tests, unlike other integration tests, are fast and stable. And most important they bring the value of valid check for correct dependencies registration. If such test passes you could be sure that object will also be correctly instantiated in the product.
Here is a sample of such test:
[TestMethod]
public void ConfigureServices_RegistersDependenciesCorrectly()
{
// Arrange
// Setting up the stuff required for Configuration.GetConnectionString("DefaultConnection")
Mock<IConfigurationSection> configurationSectionStub = new Mock<IConfigurationSection>();
configurationSectionStub.Setup(x => x["DefaultConnection"]).Returns("TestConnectionString");
Mock<Microsoft.Extensions.Configuration.IConfiguration> configurationStub = new Mock<Microsoft.Extensions.Configuration.IConfiguration>();
configurationStub.Setup(x => x.GetSection("ConnectionStrings")).Returns(configurationSectionStub.Object);
IServiceCollection services = new ServiceCollection();
var target = new Startup(configurationStub.Object);
// Act
target.ConfigureServices(services);
// Mimic internal asp.net core logic.
services.AddTransient<TestController>();
// Assert
var serviceProvider = services.BuildServiceProvider();
var controller = serviceProvider.GetService<TestController>();
Assert.IsNotNull(controller);
}
I also had a similar problem, but managed to get around that by using the WebHost in AspNetCore and essentially re-creating what program.cs does, and then Asserting that all of my services exist and are not null. You could go a step further and execute specific extensions for IServices with .ConfigureServices or actually perform operations with the services you created to make sure they were constructed properly.
One key, is I created a unit test startup class that inherits from the startup class I'm testing so that I don't have to worry about separate assemblies. You could use composition if you prefer to not use inheritance.
[TestClass]
public class StartupTests
{
[TestMethod]
public void StartupTest()
{
var webHost = Microsoft.AspNetCore.WebHost.CreateDefaultBuilder().UseStartup<Startup>().Build();
Assert.IsNotNull(webHost);
Assert.IsNotNull(webHost.Services.GetRequiredService<IService1>());
Assert.IsNotNull(webHost.Services.GetRequiredService<IService2>());
}
}
public class Startup : MyStartup
{
public Startup(IConfiguration config) : base(config) { }
}
This approach works, and uses the real MVC pipeline, as things should only be mocked if you need to change how they work.
public void AddTransactionLoggingCreatesConnection()
{
var servCollection = new ServiceCollection();
//Add any injection stuff you need here
//servCollection.AddSingleton(logger.Object);
//Setup the MVC builder thats needed
IMvcBuilder mvcBuilder = new MvcBuilder(servCollection, new Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartManager());
IEnumerable<KeyValuePair<string, string>> confValues = new List<KeyValuePair<string, string>>()
{
new KeyValuePair<string, string>("TransactionLogging:Enabled", "True"),
new KeyValuePair<string, string>("TransactionLogging:Uri", "https://api.something.com/"),
new KeyValuePair<string, string>("TransactionLogging:Version", "1"),
new KeyValuePair<string, string>("TransactionLogging:Queue:Enabled", "True")
};
ConfigurationBuilder builder = new ConfigurationBuilder();
builder.AddInMemoryCollection(confValues);
var confRoot = builder.Build();
StartupExtensions.YourExtensionMethod(mvcBuilder); // Any other params
}
As an alternative approach to #datchung's answer with ASP.net Core 6 (or 7) Minimal start-up, it's possible to leverage WebApplicationFactory<T> to run startup. Note that this requires defining InternalsVisibleTo from API to test project for the Program reference to be accessible.
Sample test, using xUnit:
[Fact]
public void StartupTest()
{
var waf = new WebApplicationFactory<Program>();
var server = waf.Server;
// Optional: check for individual services
var myService = server.Services.GetService<IMyService>();
Assert.NotNull(myService);
}
The .Server call there triggers the test server and ServiceCollection build. That, in turn, triggers validation unless "ValidateOnBuild" option has been turned off.
More about WAF internals in here: https://andrewlock.net/exploring-dotnet-6-part-6-supporting-integration-tests-with-webapplicationfactory-in-dotnet-6/
All of this does require that your Startup code works in test scenario (it shouldn't connect to online services etc.) but that is also useful for integration testing too (e.g. Alba).
In my case, I'm using .NET 6 with the minimal API (no Startup class).
My Program.cs originally looked like this:
// using statements
...
var builder = WebApplication.CreateBuilder(args);
...
builder.services.AddSingleton<IMyInterface, MyImplementation>();
...
I added StartupHelper.cs:
public class StartupHelper
{
private readonly IServiceCollection _services;
public StartupHelper(IServiceCollection services)
{
_services = services;
}
public void SetUpServices()
{
_services.AddSingleton<IMyInterface, MyImplementation>();
}
}
I used StartupHelper in Program.cs:
// using statements
...
var builder = WebApplication.CreateBuilder(args);
...
var startupHelper = new StartupHelper(builder.Services);
startupHelper.SetUpServices();
...
And my test (NUnit) looks like this:
[Test]
public void SetUpServices()
{
var builder = WebApplication.CreateBuilder(new string[0]);
var startupHelper = new StartupHelper(builder.Services);
startupHelper.SetUpServices();
var app = builder.Build();
var myImplementation = app.Services.GetService<IMyInterface>();
Assert.NotNull(myImplementation);
Assert.IsTrue(myImplementation is MyImplementation);
}
You should be install to Xunit project then add startup.cs file in base directory .

Asp.Net Core: Use memory cache outside controller

In ASP.NET Core its very easy to access your memory cache from a controller
In your startup you add:
public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
}
and then from your controller
[Route("api/[controller]")]
public class MyExampleController : Controller
{
private IMemoryCache _cache;
public MyExampleController(IMemoryCache memoryCache)
{
_cache = memoryCache;
}
[HttpGet("{id}", Name = "DoStuff")]
public string Get(string id)
{
var cacheEntryOptions = new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromHours(1));
_cache.Set("key", "value", cacheEntryOptions);
}
}
But, how can I access that same memory cache outside of the controller. eg. I have a scheduled task that gets initiated by HangFire, How do I access the memorycache from within my code that starts via the HangFire scheduled task?
public class ScheduledStuff
{
public void RunScheduledTasks()
{
//want to access the same memorycache here ...
}
}
Memory cache instance may be injected to the any component that is controlled by DI container; this means that you need configure ScheduledStuff instance in the ConfigureServices method:
public void ConfigureServices(IServiceCollection services) {
services.AddMemoryCache();
services.AddSingleton<ScheduledStuff>();
}
and declare IMemoryCache as dependency in ScheduledStuff constructor:
public class ScheduledStuff {
IMemoryCache MemCache;
public ScheduledStuff(IMemoryCache memCache) {
MemCache = memCache;
}
}
I am bit late here, but just wanted to add a point to save someone's time. You can access IMemoryCache through HttpContext anywhere in application
var cache = HttpContext.RequestServices.GetService<IMemoryCache>();
Please make sure to add MemeoryCache in Startup
services.AddMemoryCache();
There is another very flexible and easy way to do it is using System.Runtime.Caching/MemoryCache
System.Runtime.Caching/MemoryCache:
This is pretty much the same as the old day's ASP.Net MVC's HttpRuntime.Cache. You can use it on ASP.Net CORE without any dependency injection, in any class you want to. This is how to use it:
// First install 'System.Runtime.Caching' (NuGet package)
// Add a using
using System.Runtime.Caching;
// To get a value
var myString = MemoryCache.Default["itemCacheKey"];
// To store a value
MemoryCache.Default["itemCacheKey"] = myString;

ASP.NET Core Configuration Section in Startup

I am migrating a ASP.NET 5 RC1 project to ASP.NET Core, and have come across an interesting issue I've not yet seen, or found a solution for.
In order to use configuration settings within Startup I have previously retrived the configuration the following way
// Works fine for DI both in ASP.NET 5 RC1 and ASP.NET Core
services.Configure<SomeConfigurationClass>(Configuration.GetSection("SomeConfigurationSection"));
// How I previous retrieved the configuration for use in startup.
// No longer available in ASP.NET Core
var someConfigurationToUseLater = Configuration.Get<SomeConfigurationClass>("SomeConfigurationSection");
After updating to ASP.NET Core 1.0 it seems Configuration.Get<T>() is no longer available.
I have tried updating the code to use Configuration.GetValue<T>() however this does not seem to work with objects and will only work when providing a path to a value. This has left me with a workaround for most of my configuration classes like so
var someConfigurationName = "someConfiguration";
var someConfigurationClass = new SomeConfigurationClass()
{
Value1 = Configuration.GetValue<string>($"{someConfigurationName}:value1"),
Foo = Configuration.GetValue<string>($"{someConfigurationName}:foo"),
Bar = Configuration.GetValue<string>($"{someConfigurationName}:bar")
};
However this is an issue when the configuration class contains an array of objects. In my case an array of Client objects
public class ClientConfiguration
{
public Client[] Clients { get; set; }
}
With the following configuration
"configuredClients": {
"clients": [
{
"clientName": "Client1",
"clientId": "Client1"
},
{
"clientName": "Client2",
"clientId": "Client2"
}
]
}
Where this would previously bind to the Clients property of my configuration class no problem, I can no longer find a way of doing so in ASP.NET Core 1.0
Updated Answer
For ASP Core 1.1.0 generic model binding is now done using Get:
var config = Configuration.GetSection("configuredClients").Get<ClientConfiguration>();
Original Answer
How about this:
var config = Configuration.GetSection("configuredClients").Bind<ClientConfiguration>();
With ASP.NET Core 2.0 (basically Core 1.1+), the IConfiguration is injected to Startup, and that can be used within ConfigureServices() and Configure() methods.
As shown in the accepted answer, the configuration can be bound to an object. But if just one value is required, the key based approach works well.
The IConfiguration still works with colon : separated string keys. And for array, use 0-based index. Or use the the generic getValue<T>() method with same keys. See example below:
var clientId2 = Configuration["configuredClients:clients:1:clientId"]?.ToString();
var clientName1 = Configuration.GetValue<string>("configuredClients:clients:0:clientName");
To use the same configuration values in other classes (e.g. Controllers)
Either inject the IConfiguration and use the same key-based approach like above. Or
Register an instance of the strongly-typed configuration object with the DI container, and inject that object directly into client classes.
Sample code below:
//In Startup.ConfigureServices()
var clientConfig = Configuration.GetSection("configuredClients")
.Get<ClientConfiguration>();
services.AddSingleton(clientConfig);
//Controller
public class TestController : Controller
{
IConfiguration _configStore;
ClientConfiguration _clientConfiguration;
public TestController(IConfiguration configuration,
ClientConfiguration clientConfiguration)
{
_configStore = configuration;
_clientConfiguration = clientConfiguration;
}
public IActionResult Get()
{
//with IConfiguration
var clientId1 = _configStore
.GetValue<string>("configuredClients:clients:0:clientId");
//with strongly typed ClientConfiguration
var clientName1 = _clientConfiguration.Clients[0]?.ClientName;
return new OkObjectResult("Configuration test");
}
}
More examples here.
You don't read the configuration manually generally in ASP.NET Core yourself, instead you create an object that matches your definition. You can read more on that in the official documentation here.
E.g.
public class MyOptions
{
public string Option1 { get; set; }
public int Option2 { get; set; }
}
public void ConfigureServices(IServiceCollection services)
{
// Setup options with DI
services.AddOptions();
services.Configure<MyOptions>(Configuration);
}
Then you just inject the options IOptions<MyOptions> where you need them.
If you want to get first "clientName"(expected "Client1"), just write:
Configuration.GetSection("configuredClients")["clients:0:clientName"];
Update for comment
Install .NET Core 1.0.1 and go with #TomMakin's way.

Dependency Injection / Constructor Injection Help

I have the following classes / interfaces:
public interface IProjectRepository
{
IQueryably<Project> GetProjects();
}
// Depends on my EF Context
public ProjectRepository : IProjectRepository
{
private MyDbEntities context;
public ProjectRepository(MyDbEntities context)
{
this.context = context;
}
public IQueryable<Project> GetProjects()
{
return context.Projects;
}
}
My controller:
// Depends on IProjectRepository
public class ProjectsController : Controller
{
private IProjectRepository projectRepository;
public ProjectsController(IProjectRepository projectRepository)
{
this.projectRepository = projectRepository;
}
public ActionResult Index()
{
return View(projectRepository.GetProjects());
}
}
I need to set up my dependency injection so that it passes in ProjectRepository into my Controller AND it needs to pass in my Entity Framework context into the Project Repository. I need to Entity Context to be HTTP Request scoped.
I'm not sure where I am supposed to put all the mapping code to make the dependency injection work. I also don't understand how MVC will work without the default constructor.
Can someone help me put all the pieces together? I am using StructureMap but I could easily switch to something else because I have no idea what I'm doing.
If you are using MVC 3, to do things properly, you should make use of the built in dependency resolution bits. I would highly recommend you read through the series of blog posts from Brad Wilson (member of the ASP.NET MVC team).
As far as a StructureMap specific implementation, I found the following blog posts helpful.
StructureMap and ASP.NET MVC 3 – Getting Started
StructureMap, Model Binders and Dependency Injection in ASP.NET MVC 3
StructureMap, Action Filters and Dependency Injection in ASP.NET MVC 3
StructureMap, Global Action Filters and Dependency Injection in ASP.NET MVC 3
Anyway, here's some code. To start with, I would suggest that you install the StructureMap-MVC3 NuGet package.
I can't remember what exactly it creates in the way of files, but here's what's basically involved.
/App_Start/StructuremapMvc.cs - This hooks into the Application_Start and sets up your container (SmIoC.Initialize()) and then sets the MVC 3 DependencyResolver to a your SmDependencyResolver
using System.Web.Mvc;
using YourAppNamespace.Website.IoC;
using StructureMap;
[assembly: WebActivator.PreApplicationStartMethod(typeof(YourAppNamespace.App_Start.StructuremapMvc), "Start")]
namespace YourAppNamespace.Website.App_Start {
public static class StructuremapMvc {
public static void Start() {
var container = SmIoC.Initialize();
DependencyResolver.SetResolver(new SmDependencyResolver(container));
}
}
}
/IoC/SmDependencyResolver.cs - this is your MVC 3 IDependencyResolver implementation. It's used in the App_Start code above.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using StructureMap;
namespace YourAppNamespace.Website.IoC
{
public class SmDependencyResolver : IDependencyResolver
{
private readonly IContainer _container;
public SmDependencyResolver(IContainer container)
{
_container = container;
}
public object GetService(Type serviceType)
{
if (serviceType == null)
{
return null;
}
try
{
return _container.GetInstance(serviceType);
}
catch
{
return null;
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
return _container.GetAllInstances(serviceType).Cast<object>(); ;
}
}
}
/IoC/SmIoC.cs - this is where you setup your container... also used in the App_Start code.
namespace YourAppNamespace.Website.IoC
{
public static class SmIoC
{
public static IContainer Initialize()
{
ObjectFactory.Initialize(x =>
{
x.For<IProjectRepository>().Use<ProjectRepository>();
//etc...
});
return ObjectFactory.Container;
}
}
}
Now everything is hooked up... (I think ;-) but you still have one last thing to do. Inside your Global.asax, we need to make sure you dispose of everything that is HttpContext scoped.
protected void Application_EndRequest()
{
ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
}
So you should be able to achieve dependency injection through constructor injection, which is the correct way to go about doing things.
If you are set on using StructureMap, here is a tutorial on the setup that you will probably need.
Some other dependency injection frameworks come with custom controller factories which will do that for you. Ninject (open source dependency injection), for example has an extension that you can use that contains this behaviour. See here for example. And here to the extension.
You can use also Unity IOC which is another popular dependency injection framework with which, to my knowledge, you will have to create a custom controller factory (like with structuremap) to achieve this behaviour. See here for an example.
You can also research all other dependency injection frameworks to see what support you can get with each.
EDIT:
I hope I am explaining this correctly but here is some background info.
MVC uses a controller factory that has the responsibilities of instantiating the respective controllers needed when a request is made. By default, it will initialize a controller by calling its parameterless constructor.
To create the infrastructure for the constructor parameter injection you need to create a custom factory that can resolve constructor parameters. That is where the dependency injection containers come in: essentially the DI container (if configured properly) knows how to resolve those dependency and your custom factory will leverage it to request the registered dependencies and pass the to the controller constructor.
All work pretty much the same. Historically, all have had setter injectors (set up a property that is then filled), but most have constructor injection now. In structure map, the easiest way to accomplish this is use the attribute: [StructureMap.DefaultConstructor].
Once you add the attribute, the objects you have placed in your "map" should inject without any extra work. If you can't use attributes, consider using the setter.
There is a file on the structure map site:
http://structuremap.net/structuremap/ConstructorAndSetterInjection.htm
When using StructureMap I would generally have something like this in my controller:
private static IProjectRepository GetProjectRepository()
{
var retVal = ObjectFactory.TryGetInstance<IProjectRepository>()
?? new ProjectRepository();
return retVal;
}
If the TryGetInstance returns null (because nothing was set for that type) it will default to the concrete type you specify.
Now you have a bootstrapper somewhere like this:
public static class StructureMapBootStrapper
{
public static void InitializeStructureMap()
{
ObjectFactory.Initialize(x =>
{
x.For<IProjectRepository>().Use<ProjectRepository>();
}
}
}
Now you call this bootstrapper in your Global.asax Application_Start event:
protected void Application_Start()
{
StructureMapBootStrapper.InitializeStructureMap();
}
Now in a test project, when you want to inject a mock repository you can just do this:
[TestMethod]
public void SomeControllerTest()
{
StructureMap.ObjectFactory.Inject(
typeof(IProjectRepository),
new MockProjectRepository());
// ... do some test of your controller with the mock
}

Categories

Resources