Using Autofac for DI into WCF service hosted in ASP.NET application - asp.net

I'm having trouble injecting services dependencies into my WCF service using Autofac 1.4.5. I've read and followed the Autofac wiki page on WcfIntegration but my debugging shows me that my WCF service is created by the System.ServiceModel.Dispatcher.InstanceBehavior.GetInstance() method and not by the AutofacWebServiceHostFactory. What am I doing wrong?
I've set up my ajax.svc file to look like the one in the example for use with WebHttpBinding:
<%# ServiceHost Language="C#" Debug="true"
Service="Generic.Frontend.Web.Ajax, Generic.Frontend.Web"
Factory="Autofac.Integration.Wcf.AutofacWebServiceHostFactory,
Autofac.Integration.Wcf" %>
My WCF service class Ajax is defined like this:
namespace Generic.Frontend.Web
{
[ServiceContract]
[AspNetCompatibilityRequirements(
RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Ajax
{
public MapWebService MapWebService { get; set;}
public Ajax() {
// this constructor is being called
}
public Ajax(MapWebService mapWebService)
{
// this constructor should be called
MapWebService = mapWebService;
}
[WebGet(ResponseFormat = WebMessageFormat.Json)]
[OperationContract(Name = "mapchange")]
public MapChangeResult ProcessMapChange(string args)
{
// use the injected service here
var result = MapWebService.ProcessMapChange(args);
return result;
}
}
}
Now I've used the wiring up in the Global.asax.cs as shown in the wiki mentioned above:
var builder = new ContainerBuilder();
builder.RegisterModule(new AutofacModuleWebservice());
var container = builder.Build();
AutofacServiceHostFactory.Container = container;
with
class AutofacModuleWebservice : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.Register<Ajax>();
builder.Register<MapWebService>().ContainerScoped();
}
}
In my web.config I have
<services>
<service name="Generic.Frontend.Web.Ajax">
<endpoint address="http://mysite.com/ajax.svc/" binding="webHttpBinding"
contract="Generic.Frontend.Web.Ajax" />
</service>
</services>
.
The service already works fine but I can't get the Autofac bits (read: creation/injection) to work. Any ideas?
Edit:
Removing the default constructor unfortunately leads to the following exception:
System.InvalidOperationException:
The service type provided could not be loaded as a service because it does not
have a default (parameter-less) constructor. To fix the problem, add a default
constructor to the type, or pass an instance of the type to the host.
Cheers, Oliver

Is your service setup with InstanceContextMode.Single? If it is then wcf will create your service using the default constructor. To get around this change your instance context mode and let autofac manage the lifetime of your service.

Try deleting the default Ajax constructor and modifying your constructor to this. If it gets run with mapWebService == null that would indicate a resolution problem.
public Ajax(MapWebService mapWebService = null)
{
// this constructor should be called
MapWebService = mapWebService;
}

I just got the same System.InvalidOperationException and solved it by changing the ServiceBehavior InstanceContextMode of the implementation from InstanceContextMode.PerCall to InstanceContextMode.PerSession, perhaps your AutoFac lifetime scope is out of sync with your web service implementation?
For testing AutoFac service creation I recommend creating a unit test and directly resolving them as this will highlight any issues and give more meaningful exception messages. For services with a request lifetime scope create a test aspx page and again resolve them directly.

I had the same problem and came across this question while searching for an answer.
In my case, using property injection worked, and the code in the question already has a property that can be used:
namespace Generic.Frontend.Web
{
[ServiceContract]
[AspNetCompatibilityRequirements(
RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Ajax
{
// inject the dependency here
public MapWebService MapWebService { get; set;}
[WebGet(ResponseFormat = WebMessageFormat.Json)]
[OperationContract(Name = "mapchange")]
public MapChangeResult ProcessMapChange(string args)
{
// use the injected service here
var result = MapWebService.ProcessMapChange(args);
return result;
}
}
}
and register to use property injection (sample code from the wiki and syntax has changed as this is now using version 2.5.2.830):
builder.RegisterType<Ajax>().PropertiesAutowired();

Following the instructions solved it for me:
code.google.com/p/autofac/wiki/… I simply do : builder.RegisterType();
and I've followed their instuructions for changing the .svc file.
When you look at your .svc file you do not get any hints about something being wrong there btw?
You host it throu the iis and do not utilize WAS, I do not see your code for overriding global.asax.cs
Add the global file to your solution and there you implement:
protected void Application_Start(object sender, EventArgs e)
{
// build and set container in application start
IContainer container = AutofacContainerBuilder.BuildContainer();
AutofacHostFactory.Container = container;
}
AutofacContainerBuilder is my container builder.

Related

DI in Azure Functions

I have some class libraries that I use in my ASP.NET Web API app that handle all my backend stuff e.g. CRUD operations to multiple databases like Azure SQL Database, Cosmos DB, etc.
I don't want to re-invent the wheel and able to use them in a new Azure Functions that I'm creating in Visual Studio 2017. All my repository methods use an interface. So, how will I implement dependency injection in my new Azure function?
I'm not seeing any support for DI but I'm a bit confused. It appears Azure Functions are based on the same SDK as WebJobs and I think last year Microsoft had started supporting DI in WebJobs - I know for sure because I implemented it using Ninject.
Is there way around this so that I can use my existing libraries in my new Azure Functions project?
I see these two techniques in addition to the service locator (anti)pattern. I asked the Azure Functions team for their comments as well.
https://blog.wille-zone.de/post/azure-functions-dependency-injection/
https://blog.wille-zone.de/post/azure-functions-proper-dependency-injection/
There is an open feature request on the GitHub pages for Azure Functions concerning this matter.
However, the way I'm approaching this is using some kind of 'wrapper' entry point, resolve this using the service locator and and start the function from there.
This looks a bit like this (simplified)
var builder = new ContainerBuilder();
//register my types
var container = builder.Build();
using(var scope = container.BeginLifetimeScope())
{
var functionLogic = scope.Resolve<IMyFunctionLogic>();
functionLogic.Execute();
}
This is a bit hacky of course, but it's the best there is until there is at the moment (to my knowledge).
I've seen the willie-zone blog mentioned a lot when it comes to this topic, but you don't need to go that route to use DI with Azure functions.
If you are using Version2 you can make your Azure functions non-static. Then you can add a public constructor for injecting your dependencies. The next step is to add an IWebJobsStartup class. In your startup class you will be able to register your services like you would for any other .Net Core project.
I have a public repo that is using this approach here: https://github.com/jedi91/MovieSearch/tree/master/MovieSearch
Here is a direct link to the startup class: https://github.com/jedi91/MovieSearch/blob/master/MovieSearch/Startup.cs
And here is the function: https://github.com/jedi91/MovieSearch/blob/master/MovieSearch/Functions/Search.cs
Hope this approach helps. If you are wanting to keep your Azure Functions static then the willie-zone approach should work, but I really like this approach and it doesn't require any third party libraries.
One thing to note is the Directory.Build.target file. This file will copy your extensions over in the host file so that DI will work once the function is deployed to Azure. Running the function locally does not require this file.
Azure Functions Depdendency Injection was announced at MSBuild 2019. Here's an example on how to do it:
[assembly: FunctionsStartup(typeof(MyNamespace.Startup))]
namespace MyNamespace
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddHttpClient();
builder.Services.AddSingleton((s) => {
return new CosmosClient(Environment.GetEnvironmentVariable("COSMOSDB_CONNECTIONSTRING"));
});
builder.Services.AddSingleton<ILoggerProvider, MyLoggerProvider>();
}
}
}
GitHub Example
Documentation
As stated above, it was just announced at Build 2019. It can now be setup almost exactly like you would in an ASP .Net Core app.
Microsoft Documentation
Short Blog I Wrote
Actually there is a much nicer and simpler way provided out of the box by Microsoft. It is a bit hard to find though. You simply create a start up class and add all required services here, and then you can use constructor injection like in regular web apps and web apis.
This is all you need to do.
First I create my start up class, I call mine Startup.cs to be consistent with Razor web apps, although this is for Azure Functions, but still it's the Microsoft way.
using System;
using com.paypal;
using dk.commentor.bl.command;
using dk.commentor.logger;
using dk.commentor.sl;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using org.openerp;
[assembly:Microsoft.Azure.WebJobs.Hosting.WebJobsStartup(typeof(dk.commentor.starterproject.api.Startup))]
namespace dk.commentor.starterproject.api
{
public class Startup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
builder.Services.AddSingleton<ILogger, CommentorLogger>();
builder.Services.AddSingleton<IPaymentService, PayPalService>();
builder.Services.AddSingleton<IOrderService, OpenERPService>();
builder.Services.AddSingleton<ProcessOrderCommand>();
Console.WriteLine("Host started!");
}
}
}
Next I change the method call in the function from static to non-static, and I add a constructor to the class (which is now also non-static). In this constructor I simply add the services I require as constructor parameters.
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using dk.commentor.bl.command;
namespace dk.commentor.starterproject.api
{
public class ProcessOrder
{
private ProcessOrderCommand processOrderCommand;
public ProcessOrder(ProcessOrderCommand processOrderCommand) {
this.processOrderCommand = processOrderCommand;
}
[FunctionName("ProcessOrder")]
public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log)
{
log.LogInformation("C# HTTP trigger ProcessOrder called!");
log.LogInformation(System.Environment.StackTrace);
string jsonRequestData = await new StreamReader(req.Body).ReadToEndAsync();
dynamic requestData = JsonConvert.DeserializeObject(jsonRequestData);
if(requestData?.orderId != null)
return (ActionResult)new OkObjectResult($"Processing order with id {requestData.orderId}");
else
return new BadRequestObjectResult("Please pass an orderId in the request body");
}
}
}
Hopes this helps.
I would like to add my 2 cents to it. I used the technique that it's used by Host injecting ILogger. If you look at the Startup project I created GenericBindingProvider that implements IBindingProvider. Then for each type I want to be injected I register it as follow:
builder.Services.AddTransient<IWelcomeService, WelcomeService>();
builder.Services.AddSingleton<IBindingProvider, GenericBindingProvider<IWelcomeService>>();
The downside is that you need to register the type you want to be injected into the function twice.
Sample code:
Azure Functions V2 Dependency Injection sample
I have been using SimpleInjector perfectly fine in Azure Functions. Just create a class (let's call it IoCConfig) that has the registrations and make a static instance of that class in function class so that each instance will use the existing instance.
public interface IIoCConfig
{
T GetInstance<T>() where T : class;
}
public class IoCConfig : IIoCConfig
{
internal Container Container;
public IoCConfig(ExecutionContext executionContext, ILogger logger)
{
var configurationRoot = new ConfigurationBuilder()
.SetBasePath(executionContext.FunctionAppDirectory)
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
Container = new Container();
Configure(configurationRoot, logger);
}
public IoCConfig(IConfigurationRoot configurationRoot, ILogger logger)
{
Container = new Container();
Configure(configurationRoot, logger);
}
private void Configure(IConfigurationRoot configurationRoot, ILogger logger)
{
Container.RegisterInstance(typeof(IConfigurationRoot), configurationRoot);
Container.Register<ISomeType, SomeType>();
}
public T GetInstance<T>() where T : class
{
return Container.GetInstance<T>();
}
}
Then in root:
public static class SomeFunction
{
public static IIoCConfig IoCConfig;
[FunctionName("SomeFunction")]
public static async Task Run(
[ServiceBusTrigger("some-topic", "%SUBSCRIPTION_NAME%", Connection = "AZURE_SERVICEBUS_CONNECTIONSTRING")]
SomeEvent msg,
ILogger log,
ExecutionContext executionContext)
{
Ensure.That(msg).IsNotNull();
if (IoCConfig == null)
{
IoCConfig = new IoCConfig(executionContext, log);
}
var someType = IoCConfig.GetInstance<ISomeType>();
await someType.Handle(msg);
}
}
AzureFunctions.Autofac is very easy to use.
Just add a config file:
public class DIConfig
{
public DIConfig(string functionName)
{
DependencyInjection.Initialize(builder =>
{
builder.RegisterType<Sample>().As<ISample>();
...
}, functionName);
}
}
Add the DependencyInjectionConfig attribute then inject:
[DependencyInjectionConfig(typeof(DIConfig))]
public class MyFunction
{
[FunctionName("MyFunction")]
public static HttpResponseMessage Run([HttpTrigger(AuthorizationLevel.Function, "get", Route = null)]HttpRequestMessage request,
TraceWriter log,
[Inject]ISample sample)
{
https://github.com/introtocomputerscience/azure-function-autofac-dependency-injection
I think this is a better solution:
https://github.com/junalmeida/autofac-azurefunctions
https://www.nuget.org/packages/Autofac.Extensions.DependencyInjection.AzureFunctions
Install the NuGet in your project and then make a Startup.cs and put this in it:
[assembly: FunctionsStartup(typeof(Startup))]
public class Startup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder
.UseAppSettings() // this is optional, this will bind IConfiguration in the container.
.UseAutofacServiceProviderFactory(ConfigureContainer);
}
private void ConfigureContainer(ContainerBuilder builder)
{
// do DI registration against Autofac like normal! (builder is just the normal ContainerBuilder from Autofac)
}
...
Then in your function code you can do normal constructor injection via DI:
public class Function1 : Disposable
{
public Function1(IService1 service1, ILogger logger)
{
// logger and service1 injected via autofac like normal
// ...
}
[FunctionName(nameof(Function1))]
public async Task Run([QueueTrigger("myqueue-items", Connection = "AzureWebJobsStorage")]string myQueueItem)
{
//...
Support for Dependency injection begins with Azure Functions 2.x which means Dependency Injection in Azure function can now leverage .NET Core Dependency Injection features.
Before you can use dependency injection, you must install the following NuGet packages:
Microsoft.Azure.Functions.Extensions
Microsoft.NET.Sdk.Functions
Having Dependency Injection eases things like DBContext, Http client usage (Httpclienfactory), Iloggerfactory, cache support etc.
Firstly, update the Startup class as shown below
namespace DemoApp
{
public class Startup: FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddScoped<IHelloWorld, HelloWorld>();
// Registering Serilog provider
var logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateLogger();
builder.Services.AddLogging(lb => lb.AddSerilog(logger));
//Reading configuration section can be added here etc.
}
}
}
Secondly, Removal of Static keyword in Function class and method level
public class DemoFunction
{
private readonly IHelloWorld _helloWorld;
public DemoFunction(IHelloWorld helloWorld)
{
_helloWorld = helloWorld;
}
[FunctionName("HttpDemoFunction")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
}
If we look into above e.g. IHelloWorld is injected using .NET Core DI
**Note:**In-spite of having latest version of Azure function v3 for Dependency Injection to enable few steps are manual as shown above
Sample code on github can be found here

How to pass IOptions to an ASP.NET 5 middle-ware service?

I have started playing around with ASP.NET 5 vNext and I am struggling passing the options from config.json into a middle-ware service that is used by my WebApi controller.
Here is a snippet with my middle-ware service:
public class MyService : IMyService
{
public MyService(IOptions<MyOptions> settings)
{
var o = settings.Options;
}
}
Here is my WebApi controller that is using the middle-ware service:
public class MyController : Controller
{
private IMyService _myService;
public TestController(IMyService service)
{
_myService = service;
}
}
In Startup.cs I am reading the options:
services.AddOptions();
services.Configure<MyOptions>(Configuration);
What I am struggling with is how to register an instance to IMyService so that it would be passed to the constructor of the controller (how can I get a hold of the IOptions)?
services.AddInstance<IMyService>(new MyService(XXXXX));
As suggested below I did try to use both
services.AddTransient<MyService>();
and
services.AddSingleton<MyService>();
But in both cases I am seeing the following error:
An unhandled exception occurred while processing the request.
InvalidOperationException: Unable to resolve service for type
'MyApp.Services.IMyService' while attempting to activate
'MyApp.Controllers.TestController'.
Microsoft.Framework.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider
sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
Thanks for your help!
Don't register it as an Instance. Instead just add it as Scoped/Transient/Singleton depending on your requirements and let Dependency Injection do its magic;
services.Configure<MyOptions>(Configuration.GetSection("MyOptions"));
services.AddScoped<IMyService, MyService>();
For example, you can to add to Startup.cs that code:
public void ConfigureServices(IServiceCollection services)
{
var builder = new ConfigurationBuilder("[path to file with configuration]");
builder.AddJsonFile("config.json");
var config = builder.Build();
services.AddOptions();
services.Configure<MyOptions>(config);
//services.AddSingleton<IMyService, MyService>();
services.AddTransient<IMyService, MyService>();
}
I can assure you that you can use Singleton or Transient.
If you're interested, you can find more info here https://github.com/aspnet/Docs/issues/24.
And additionally, currently Autofac creates DI for ASP.NET 5 on
http://alexmg.com/autofac-4-0-alpha-1-for-asp-net-5-0-beta-3/

Web API controller parameterized constructor called only once, parameterless constructor on subsequent requests

I'm attempting to use Unity to inject a dependency per this article:
http://www.asp.net/web-api/overview/extensibility/using-the-web-api-dependency-resolver
Here is what I have in my global.asax
void ConfigureApi(HttpConfiguration config)
{
var unity = new UnityContainer();
unity.RegisterType<CustomerController>();
unity.RegisterType<TPS.Data.Can.IUnitOfWork, TPS.Data.Can.EFRepository.UnitOfWork>(new HierarchicalLifetimeManager());
config.DependencyResolver = new IoCContainer(unity);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
ConfigureApi(GlobalConfiguration.Configuration);
}
Here is my API controller:
public class CustomerController : ApiController
{
private TPS.Data.Can.IRepository<tblCustomer> _repo;
private TPS.Data.Can.IUnitOfWork _uow;
public CustomerController() { }
public CustomerController(TPS.Data.Can.IUnitOfWork uow) {
_uow = uow;
_repo = uow.CustomerRepository;
}
// GET api/customer/5
public IEnumerable<Customer> Get()
{
string identity = HttpContext.Current.User.Identity.Name;
//REFACTOR THIS
if (String.IsNullOrWhiteSpace(identity))
identity = "chardie";
var customers = from c in _repo.Get()
where c.SalesRep == identity
select new Customer
{
IDCUST = null,
CustCode = c.CustCode,
CustName = c.CustName
};
return customers.ToList();
}
This works when I first start debugging my application. If I set a breakpoint in the parameterized constructor, the breakpoint will be hit when I hit the Web API for the first time. When I hit refresh in my browser, the constructor does not get called, the dependency doesn't get injected, and the Get() action throws an exception because the expected repository is null.
Can anyone tell me why my constructor isn't being called after the first request?
Thanks!
Chris
EDIT
FWIW, I removed the parameterless constructor entirely from the Web API controller, and on my second request to it, I get the exception:
Type 'TPS.Website.Api.CustomerController' does not have a default constructor
So it appears I'm getting my repo dependency injected on the first request, but after that every instantiation of the Web API controller is done through the parameterless constructor.
You're not specifying a lifecycle for the controller. MSDN states
If you do not specify a value for the lifetime, the instance will have
the default container-controlled lifetime. It will return a reference
to the original object on each call to Resolve.
If the IUnitOfWork dependency is transient, then the controller should be transient too. So try
unity.RegisterType<CustomerController>(new TransientLifetimeManager());
This might not solve the whole problem but it sounds like part of it. You certainly shouldn't need the parameterless constructor.
I had this as I was returning my resolver for my dependency scope using this and then disposing the container in the dispose. So after the first request the container was disposed.
Looks like it's because you're not using singleton pattern for the Unity Container.
Have a private static variable instead of the var container = new UnityContainer();
internal static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() => new UnityContainer());
Then access within code using the .Value property.

Can I use my Ninject .NET project within Orchard CMS?

I am creating a website using Orchard CMS and I have an external .NET project written with Ninject for dependency injection which I would like to use together with a module within Orchard CMS. I know that Orchard uses Autofac for dependency injection and this is causing me problems since I never worked with DI before.
I have created an Autofac module, UserModule, which registers the a source, UserRegistrationSource, like this:
UserModule.cs
public class UserModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterSource(new UserRegistrationSource());
}
}
UserRegistrationSource.cs
public class UserRegistrationSource : IRegistrationSource
{
public bool IsAdapterForIndividualComponents
{
get { return false; }
}
public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
var serviceWithType = service as IServiceWithType;
if (serviceWithType == null)
yield break;
var serviceType = serviceWithType.ServiceType;
if (!serviceType.IsInterface || !typeof(IUserServices).IsAssignableFrom(serviceType) || serviceType != typeof(IUserServices))
yield break;
var registrationBuilder = // something...
yield return registrationBuilder.CreateRegistration();
}
}
UserServices.cs
public interface IUserServices : IDependency
{
void Add(string email, string password);
}
public class UserServices : IUserServices
{
private readonly EFMembershipManager _manager;
public UserServices(EFMembershipManager manager)
{
_manager = manager;
}
public void Add(string email, string password)
{
_manager.createUser(email, password);
}
}
EFMembershipManager.cs constructor
public EFMembershipManager(ServerRepository db,
ServerRepositoryMembershipProvider membershipProvider,
string testUsername,
string serverUsername)
{
...
}
EFMembershipManager is a class from the external project which uses Ninject for DI's and uses ServerRepository and ServerRepositoryMembershipProvider whom also are injected using Ninject.
And now I'm stuck...
Should UserRegistrationSource take the Ninject container (kernel) as a constructor argument and try to find the IUserServices service and then mediate the resolves to the Ninject kernel and return an empty Enumerable so that Autofac doesn't try to resolve anything related to IUserServices or is this the wrong approach?
Autofac supports registration sources (and more on registration sources here). A registration source is a service that the container will consult when trying to resolve a type. The source can respond, either with a means to build the type, or an empty list which indicates that the source is not able to provide the requested type.
In your case, a registration source could be implemented that will try to resolve the requested type from your Ninject container.
I'm not too familiar with Orchard but I'm guessing that it uses configuration files to configure Autofac. My suggestion is that you create a simple Autofac module that registers your registration source implementation, and that you configure Orchard to load the module from config.

How to use Dependency Injection with ASP.NET Web Forms

I am trying to work out a way to use dependency injection with ASP.NET Web Forms controls.
I have got lots of controls that create repositories directly, and use those to access and bind to data etc.
I am looking for a pattern where I can pass repositories to the controls externally (IoC), so my controls remain unaware of how repositories are constructed and where they come from etc.
I would prefer not to have a dependency on the IoC container from my controls, therefore I just want to be able to construct the controls with constructor or property injection.
(And just to complicate things, these controls are being constructed and placed on the page by a CMS at runtime!)
Any thoughts?
UPDATE 2019:
With the introduction of Web Forms 4.7.2, there is now better support for DI. This invalidates the below. See: Wiring up Simple Injector in WebForms in .NET 4.7.2
You can use automatic constructor injection by replacing the default PageHandlerFactory with a custom one. This way you can use an overloaded constructor to load the dependencies. Your page might look like this:
public partial class HomePage : System.Web.UI.Page
{
private readonly IDependency dependency;
public HomePage(IDependency dependency)
{
this.dependency = dependency;
}
// Do note this protected ctor. You need it for this to work.
protected HomePage () { }
}
Configuring that custom PageHandlerFactory can be done in the web.config as follows:
<?xml version="1.0"?>
<configuration>
<system.web>
<httpHandlers>
<add verb="*" path="*.aspx"
type="YourApp.CustomPageHandlerFactory, YourApp"/>
</httpHandlers>
</system.web>
</configuration>
Your CustomPageHandlerFactory can look like this:
public class CustomPageHandlerFactory : PageHandlerFactory
{
private static object GetInstance(Type type)
{
// TODO: Get instance using your favorite DI library.
// for instance using the Common Service Locator:
return Microsoft.Practices.ServiceLocation
.ServiceLocator.Current.GetInstance(type);
}
public override IHttpHandler GetHandler(HttpContext cxt,
string type, string vPath, string path)
{
var page = base.GetHandler(cxt, type, vPath, path);
if (page != null)
{
// Magic happens here ;-)
InjectDependencies(page);
}
return page;
}
private static void InjectDependencies(object page)
{
Type pageType = page.GetType().BaseType;
var ctor = GetInjectableCtor(pageType);
if (ctor != null)
{
object[] arguments = (
from parameter in ctor.GetParameters()
select GetInstance(parameter.ParameterType)
.ToArray();
ctor.Invoke(page, arguments);
}
}
private static ConstructorInfo GetInjectableCtor(
Type type)
{
var overloadedPublicConstructors = (
from constructor in type.GetConstructors()
where constructor.GetParameters().Length > 0
select constructor).ToArray();
if (overloadedPublicConstructors.Length == 0)
{
return null;
}
if (overloadedPublicConstructors.Length == 1)
{
return overloadedPublicConstructors[0];
}
throw new Exception(string.Format(
"The type {0} has multiple public " +
"ctors and can't be initialized.", type));
}
}
Downside is that this only works when running your side in Full Trust. You can read more about it here. But do note that developing ASP.NET applications in partial trust seems a lost cause.
Starting from .NET 4.7.2 (what's new), it is now easy for developers to use Dependency Injection in WebForms applications. With the UnityAdapter, you can add it to your existing WebForms application in 4 simple steps. See this blog.
Autofac supports fairly unobtrusive dependency injection in ASP.NET WebForms. My understanding is it just hooks into the ASP.NET page lifecycle using an http module and does property injection. The only catch is that for controls I don't think this happens until after the Init event.
The best way is to have a base class for the controls like:
public class PartialView : UserControl
{
protected override void OnInit(System.EventArgs e)
{
ObjectFactory.BuildUp(this);
base.OnInit(e);
}
}
That will inject any control that inherits from that base class (uses structuremap). Combining that with a property based config, you will be able to have controls like:
public partial class AdminHeader : PartialView
{
IMyRepository Repository{get;set;}
}
Update 1: If you can't have the controls inherit, perhaps the CMS has a hook right after creating the controls, in there you can call the BuildUp. Also if the CMS allows you to hook something to fetch the instance you could use constructor based injection, but I prefer BuildUp on this specific scenario as asp.net doesn't have a hook for this.
You could also create some singleton instances in the Application_Start global.asax event and have them available as public static readonly properties.
This is a solution I recently used to avoid hooking into the pipeline (I find that confuses everyone that looks at my code in the future, but yes, I see its benefits as well):
public static class TemplateControlExtensions
{
static readonly PerRequestObjectManager perRequestObjectManager = new PerRequestObjectManager();
private static WIIIPDataContext GetDataContext(this TemplateControl templateControl)
{
var dataContext = (WIIIPDataContext) perRequestObjectManager.GetValue("DataContext");
if (dataContext == null)
{
dataContext = new WIIIPDataContext();
perRequestObjectManager.SetValue("DataContext", dataContext);
}
return dataContext;
}
public static IMailer GetMailer(this TemplateControl templateControl)
{
return (IMailer)IoC.Container.Resolve(typeof(IMailer));
}
public static T Query<T>(this TemplateControl templateControl, Query<T> query)
{
query.DataContext = GetDataContext(templateControl);
return query.GetQuery();
}
public static void ExecuteCommand(this TemplateControl templateControl, Command command)
{
command.DataContext = GetDataContext(templateControl);
command.Execute();
}
private class PerRequestObjectManager
{
public object GetValue(string key)
{
if (HttpContext.Current != null && HttpContext.Current.Items.Contains(key))
return HttpContext.Current.Items[key];
else
return null;
}
public void SetValue(string key, object newValue)
{
if (HttpContext.Current != null)
HttpContext.Current.Items[key] = newValue;
}
}
}
This shows how you can create your own life time manager pretty easily as well as hook into an IoC container if you so desire. Oh, and I am also using a query/command structure which is sort of unrelated, but more on the reasoning behind that can be found here:
Limit your abstractions: Refactoring toward reduced abstractions

Resources