From 0b9c877563950ad1beb83e0aefbd628d4bd3c4f0 Mon Sep 17 00:00:00 2001 From: Luca Leonardo Scorcia Date: Tue, 16 Mar 2021 14:20:07 +0100 Subject: [PATCH] Add the DiObjectDataSource control and its documentation --- .../DiObjectDataSource.cs | 64 +++++++++++++++++++ GETTING_STARTED.md | 22 +++++++ 2 files changed, 86 insertions(+) create mode 100644 AspNetDependencyInjection/AspNetDependencyInjection.WebForms/DiObjectDataSource.cs diff --git a/AspNetDependencyInjection/AspNetDependencyInjection.WebForms/DiObjectDataSource.cs b/AspNetDependencyInjection/AspNetDependencyInjection.WebForms/DiObjectDataSource.cs new file mode 100644 index 0000000..e1fd98d --- /dev/null +++ b/AspNetDependencyInjection/AspNetDependencyInjection.WebForms/DiObjectDataSource.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using System.Web.Compilation; +using System.Web.UI.WebControls; + +namespace AspNetDependencyInjection.WebForms +{ + /// + /// A drop-in replacement for controls that enables dependency injection + /// instantiation for data sources. + /// + /// To use it, register the control namespace by adding the following element to your web.config file: + /// + /// + /// <configuration> + /// <system.web> + /// <pages> + /// <controls> + /// ... + /// <add tagPrefix="di" namespace="AspNetDependencyInjection.WebForms" assembly="AspNetDependencyInjection" /> + /// ... + /// </controls> + /// </pages> + /// </system.web> + /// </configuration> + /// + /// + /// Then replace the <asp:ObjectDataSource> tags in your code with <di:DiObjectDataSource> tags. + /// + /// + /// Please make sure your methods are not marked . + /// + public class DiObjectDataSource : ObjectDataSource + { + /// + /// Background: controls invoke a `Select` method defined + /// in the `Page` object in order to retrieve the data to bind to other controls. This select method is + /// usually a method on the containing page, which prevents using DI, + /// but can also be an instance method, in which case it instantiates a new `Page` object and calls the method + /// on the newly created instance. + /// That's the scenario when DI should be possible. + /// + /// Unfortunately it seems that Microsoft never updated the control + /// to use , + /// so it's not able to instantiate DI-enabled `Page` controls. + /// However, it is possible to override its + /// event to make it work the way we want. + /// + public DiObjectDataSource() + { + // We need to use `BuildManager.GetType` instead of `Type.GetType` because this method is actually + // able to look for the type in all of the application assemblies - otherwise we'd be restricted + // to the current assembly types. + + this.ObjectCreating += ( sender, args ) => + args.ObjectInstance = HttpRuntime.WebObjectActivator.GetService( BuildManager.GetType( + ( (ObjectDataSourceView)sender ).TypeName, throwOnError: false, ignoreCase: true ) ); + } + } +} diff --git a/GETTING_STARTED.md b/GETTING_STARTED.md index 976c31b..b354754 100644 --- a/GETTING_STARTED.md +++ b/GETTING_STARTED.md @@ -186,6 +186,28 @@ Another advantage of this approach is that if you need to use a `DbContext` insi * `WebActivatorEx`'s `PreApplicationStartMethod` (the `PreStart` method in our sample above) runs **before** OWIN's Startup method. * See this StackOverflow post: https://stackoverflow.com/questions/21462777/webactivatorex-vs-owinstartup +### How to deal with `ObjectDataSource` controls +Background: `ObjectDataSource` controls invoke a `Select` method defined in the `Page` object in order to retrieve the data to bind to other controls. This select method is usually a `static` method on the containing page, which prevents using DI, but can also be an instance method, in which case it instantiates a new `Page` object and calls the method on the newly created instance. That's the scenario when DI should be possible. + +Unfortunately it seems that Microsoft never updated the `ObjectDataSource` control to use `HttpRuntime.WebObjectActivator`, so it's not able to instantiate DI-enabled `Page` controls. However, it is possible to override its `ObjectCreating` event to make it work the way we want. + +For your convenience, this component includes a custom `ObjectDataSource`-derived control called `DiObjectDataSource` that can be used as a drop-in replacement for basic `ObjectDataSource` controls. To use it, register the control namespace by adding the following element to your `web.config` file: +``` + + + + + ... + + ... + + + + +``` +Then replace the `` tags in your code with `` tags. +*Note:* Please make sure your `ObjectDataSource` methods are not marked `static`. + ## Included services All included services are exposed as interfaces so you can replace them with your own implementation for testing purposes or for different production scenarios. They are listed below: