I'm using .NET Framework 4.7.2 and am trying to use IHttpClientFactory to create HttpClient instances.
I downloaded the Microsoft.Extensions.Http v7 NuGet Package and now have access to System.Net.Http.HttpClientFactory.
Am I meant to use HttpClientFactory.Create() to create HttpClients?
Or do I have to use DI and IHttpClientFactory?
What is HttpClientFactory used for and why doesn't it implement IHttpClientFactory?
I've checked the source code of the DefaultHttpClientFactory and even though it is part of the Microsoft.Extensions.Http namespace it is marked as internal.
Gladly the AddHttpClient extension method can do the DI registration of the above class on our behalf.
services.TryAddSingleton<DefaultHttpClientFactory>();
services.TryAddSingleton<IHttpClientFactory>(serviceProvider => serviceProvider.GetRequiredService<DefaultHttpClientFactory>());
So, all you need to do is:
create a ServiceCollection
call the AddHttpClient
build it to have a ServiceProvider
call the GetRequiredService
In order to do that in .NET Framework 4.7.2 you need to use the Microsoft.Extensions.DependencyInjection package.
using System;
using System.Net.Http;
using Microsoft.Extensions.DependencyInjection;
class Program
{
private static readonly ServiceProvider serviceProvider;
static Program()
{
serviceProvider = new ServiceCollection().AddHttpClient().BuildServiceProvider();
}
public static void Main(string[] args)
{
var factory = serviceProvider.GetRequiredService<IHttpClientFactory>();
Console.WriteLine(factory == null); //False
}
}
Here I have detailed how to do the same with SimpleInjector
I'm still trying to get my head around what's what with ASP.NET 5 / EF 7. I'm using DNX projects (.xproj).
Startup is used by OWIN/ASP.NET for configuring, loading services, etc. But it's also used for EF 7 migrations (to set your DbContextOptions for example).
My main goal is to know how EF7 (and ASP.NET 5) bootstrap with Startup and who's creating the startup class, initializing the DI container, etc.
An example of what I need to do, for context, is that in my xUnit unit tests (which are in their own assembly and reference my data assembly which doesn't have a Startup class), I need to AddDbContext to set my connection.
I have the sample startup class:
namespace Radar.Data
{
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.Data.Entity;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.PlatformAbstractions;
public class Startup
{
public IConfigurationRoot Configuration { get; set; }
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
{
var builder = new ConfigurationBuilder()
.SetBasePath(appEnv.ApplicationBasePath)
.AddJsonFile("appsettings.json");
Configuration = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<RadarDbContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
}
public void Configure(IApplicationBuilder app)
{
}
}
}
This is currently in my data assembly and not my unit test assembly. I tried adding the app setting (I know it's OWIN but I thought I'd give it a shot):
<appSettings>
<add key="owin:appStartup" value="Radar.Data.Startup, Radar.Data" />
</appSettings>
The startup class is not getting executed.
I'd really like an understanding of the overall mechanism with Startup, who calls it, etc., but for now, I just need an understanding of how EF 7 initializes dependencies/services so that I can properly initialize my unit tests.
UPDATE
Here's what I've got in my unit test so far and I thought I had it working at one point:
ServiceCollection serviceCollection = new ServiceCollection();
IServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
DbContextActivator.ServiceProvider = serviceProvider;
serviceCollection.AddEntityFramework()
.AddSqlServer()
.AddDbContext<RadarDbContext>(
options => options.UseSqlServer("Server=.;Database=SonOfRadar;Trusted_Connection=True;MultipleActiveResultSets=True"));
but now I'm getting No service for type 'Microsoft.Data.Entity.Internal.IDbSetInitializer' has been registered when my DbContext is instantiated. So obviously not getting all the EF services loaded.
If I comment out:
DbContextActivator.ServiceProvider = serviceProvider;
it errors earlier with: No database providers are configured. Configure a database provider by overriding OnConfiguring in your DbContext class or in the AddDbContext method when setting up services.
Setting DbContextActivator.ServiceProvider is the only place in EF7 where I can find a hook to set your own provider. I'd be just as happy getting an instance of EF7's internal service collection and working with that. I think I'm going to scour the EF7 unit test code again and see if I'm missing a critical piece.
Startup class is created by Microsoft.AspNet.Hosting package when you run you web application (see StartupLoader.cs).
You can also look onto WebApplication.Run method (WebApplication.Run) its an entry point to ASP.NET 5 web applications.
DI is initialized in WebHostBuilder class (WebHostBuilder.cs) and inside dnx in Bootstrapper class (Bootstrapper.cs)
I am trying to implement DI in a webforms project, so I installed the Unity.WebForms dlls in my UI layer. As soon as I did an App_Start folder was created for me with a UnityWebFormsStart class file. Inside this file there is a method RegisterDependencies which asks to be edited.
What is the next step after registering the dependencies? Is there something I need to add in the Global.asax class file? And how and where do I resolve a type inside a webform? Do I decorate that with any attributes?
The Unity.WebForms dll and NuGet package does a few things for you in the background. It will ensure that a child container is started at the begin of each new web request and disposed at the end of each request. This allows you to register components with a 'per web request' lifestyle (using the HierarchicalLifetimeManager in Unity), which is useful for components such as O/RM unit of works such as Entity Framework's DbContext.
The other thing that the package ensures is that the given HttpHandler (usually your Page) and all its child controls are Built up. The BuildUp method is the way to initialize components that are not created by the container itself.
So the idea is to use property injection in your page classes and controls, but solely use constructor injection in ALL other components in your application. Constructor injection is the preferred mechanism for doing dependency injection, but constructor injection is unfortunately not possible in ASP.NET Page and Control classes.
So your page could look like this:
public class CancelOrderPage : Page
{
[Dependency]
public ICommandHandler<CancelOrder> CancelOrderHandler { get; set; }
void CancelButton_Click(object sender, EventArgs e) {
this.CancelOrderHandler.Handle(new CancelOrder {
OrderId = Guid.Parse(this.OrderIdHiddenField.Value)
});
}
}
For the rest of your application, use constructor injection:
public class CancelOrderHandler : ICommandHandler<CancelOrder>
{
private readonly IAuthorizedRepository<Order> orderRepository;
private readonly IEventPublisher eventPublisher;
public CancelOrderHandler(IAuthorizedRepository<Order> orderRepository,
IEventPublisher eventPublisher) {
this.orderRepository = orderRepository;
this.eventPublisher = eventPublisher;
}
public void Handle(CancelOrder command) {
// some implementation
}
}
In the RegisterDependencies you will have to register your dependencies. You can do this manually:
container.RegisterType<ICommandHandler<CancelOrder>, CancelOrderHandler>();
container.RegisterType<IEventPublisher, InProcessPublisher>();
container.RegisterType(
typeof(AuthorizedRepository<>),
typeof(DbContextRepo<>));
Or you can use batch-registration.
We're trying to use MEF 2 with ASP.NET MVC 4 to support an extensible application. There are really 2 parts to this question (hope that's okay SO gods):
How do we use Microsoft.Composition and the MVC container code (MEF/MVC demo source) to replace Ninject as our DI for ICoreService, ICoreRepository, IUnitOfWork, and IDbContext?
It looks like we can't use both Ninject and the MVC container at the same time (I'm sure many are saying "duh"), so we'd like to go with MEF, if possible. I tried removing Ninject and setting [Export] attributes on each of the relevant implementations, spanning two assemblies in addition to the web project, but Save() failed to persist with no errors. I interpreted that as a singleton issue, but could not figure out how to sort it out (incl. [Shared]).
How do we load multiple assemblies dynamically at runtime?
I understand how to use CompositionContainer.AddAssemblies() to load specific DLLs, but for our application to be properly extensible, we require something more akin to how I (vaguely) understand catalogs in "full" MEF, which have been stripped out from the Microsoft.Composition package (I think?); to allow us to load all IPluggable (or whatever) assemblies, which will include their own UI, service, and repository layers and tie in to the Core service/repo too.
EDIT 1
A little more reading solved the first problem which was, indeed, a singleton issue. Attaching [Shared(Boundaries.HttpRequest)] to the CoreDbContext solved the persistence problem. When I tried simply [Shared], it expanded the 'singletonization' to the Application level (cross-request) and threw an exception saying that the edited object was already in the EF cache.
EDIT 2
I used the iterative assembly loading "meat" from Nick Blumhardt's answer below to update my Global.asax.cs code. The standard MEF 2 container from his code did not work in mine, probably because I'm using the MEF 2(?) MVC container. Summary: the code listed below now works as desired.
CoreDbContext.cs (Data.csproj)
[Export(typeof(IDbContext))]
[Shared(Boundaries.HttpRequest)]
public class CoreDbContext : IDbContext { ... }
CoreRepository.cs (Data.csproj)
[Export(typeof(IUnitOfWork))]
[Export(typeof(ICoreRepository))]
public class CoreRepository : ICoreRepository, IUnitOfWork
{
[ImportingConstructor]
public CoreRepository(IInsightDbContext context)
{
_context = context;
}
...
}
CoreService.cs (Services.csproj)
[Export(typeof(ICoreService))]
public class CoreService : ICoreService
{
[ImportingConstructor]
public CoreService(ICoreRepository repository, IUnitOfWork unitOfWork)
{
_repository = repository;
_unitOfWork = unitOfWork;
}
...
}
UserController.cs (Web.csproj)
public class UsersController : Controller
{
[ImportingConstructor]
public UsersController(ICoreService service)
{
_service = service;
}
...
}
Global.asax.cs (Web.csproj)
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
CompositionProvider.AddAssemblies(
typeof(ICoreRepository).Assembly,
typeof(ICoreService).Assembly,
);
// EDIT 2 --
// updated code to answer my 2nd question based on Nick Blumhardt's answer
foreach (var file in System.IO.Directory.GetFiles(Server.MapPath("Plugins"), "*.dll"))
{
try
{
var name = System.Reflection.AssemblyName.GetAssemblyName(file);
var assembly = System.Reflection.Assembly.Load(name);
CompositionProvider.AddAssembly(assembly);
}
catch
{
// You'll need to craft exception handling to
// your specific scenario.
}
}
}
}
If I understand you correctly, you're looking for code that will load all assemblies from a directory and load them into the container; here's a skeleton for doing that:
var config = new ContainerConfiguration();
foreach (var file in Directory.GetFiles(#".\Plugins", "*.dll"))
{
try
{
var name = AssemblyName.GetAssemblyName(file);
var assembly = Assembly.Load(name);
config.WithAssembly(assembly);
}
catch
{
// You'll need to craft exception handling to
// your specific scenario.
}
}
var container = config.CreateContainer();
// ...
Hammett discusses this scenario and shows a more complete version in F# here: http://hammett.castleproject.org/index.php/2011/12/a-decent-directorycatalog-implementation/
Note, this won't detect assemblies added to the directory after the application launches - Microsoft.Composition isn't intended for that kind of use, so if the set of plug-ins changes your best bet is to detect that with a directory watcher and prompt the user to restart the app. HTH!
MEF is not intended to be used as DI framework. Which means that you should separate your "plugins" (whatever they are) composition from your infrastructure dependencies, and implement the former via MEF and the latter via whatever DI framework you prefer.
I think there are a little misunderstandings on what MEF can and can't do.
Originally MEF was conceived as purely an extensibility architecture, but as the framework evolved up to its first release, it can be fully supported as a DI container also. MEF will handle dependency injection for you, and does so through it's ExportProvider architecture. It is also entirely possible to use other DI frameworks with MEF. So in reality there are a number of ways things could be achieved:
Build a NinjectExportProvider that you can plug into MEF, so when MEF is searching for available exports, it will be able to interrogate your Ninject container.
Use an implementation of the Common Services Locator pattern to bridge between MEF and Ninject or vice versa.
Because you are using MEF for the extensibility, you'll probably want to use the former, as this exposes your Ninject components to MEF, which in turn exposes them to your plugins.
The other thing to consider, which is a bit disappointing, is in reality there isn't a lot of room for automagically plugging in of features ala Wordpress on ASP.NET. ASP.NET is a compiled and managed environment, and because of that you either resort to late-binding by loading assemblies manually at runtime, or you restart the application to pick up the new plugins, which sort of defeats the object of being able to plug new extensions in through the application.
My advice, is plan your architecture to pick up any extensibility points as startup and assume that any core changes will require a deployment and application restart.
In terms of the direct questions asked:
The CompositionProvider accepts in instance of ContainerConfiguration which is used internally to create the CompositionContainer used by the provider. So you could use this as the point by which you customise how you want your container to be instantiated. The ContainerConfiguration supports a WithProvider method:
var configuration = new ContainerConfiguration().WithProvider(new NinjectExportDescriptorProvider(kernel));
CompositionProvider.SetConfiguration(configuration);
Where NinjectExportDescriptorProvider might be:
public class NinjectExportDescriptorProvider: ExportDescriptorProvider
{
private readonly IKernel _kernel;
public NinjectExportDescriptorProvider(IKernel kernel)
{
if (kernel == null) throw new ArgumentNullException("kernel");
_kernel = kernel;
}
public override IEnumerable<ExportDescriptorPromise> GetExportDescriptors(
CompositionContract contract, DependencyAccessor dependencyAccessor)
{
var type = contract.ContractType;
if (!_kernel.GetBindings(type).Any())
return NoExportDescriptors;
return new[] {
new ExportDescriptorPromise(
contract,
"Ninject Kernel",
true, // Hmmm... need to consider this, setting it to true will create it as a shared part, false as new instance each time,
NoDependencies,
_ => ExportDescriptor.Create((c, o) => _kernel.Get(type), NoMetadata)) };
}
}
}
Note: I have not tested this, this is all theory, and is based on the example AppSettingsExportDescriptorProvider at: http://mef.codeplex.com/wikipage?title=ProgrammingModelExtensions
It's different from using the standard ExportProvider, because using the CompostionProvider is built around lightweight composition. But essentially you're wrapping up access to your Ninject kernel and making it available to your CompositionContainer.
As with adding a specific new provider (see above), you can use the ContainerConfiguration to read the available assemblies, probably something like:
var configuration = new ContainerConfiguration().WithAssemblies(AppDomain.GetAssemblies())
Again, I haven't tested all of this, but I hope it at least points you in the right direction.
Heres my problem.
My app has several projects.
WEB (Controllers and views)
Services
Data (edmx and Repositories)
Entities (POCO)
Tests
So in my Web project I have the ninject configuration
[assembly: WebActivator.PreApplicationStartMethod(typeof(PublicPanama.AppStart_NinjectMVC3), "Start")]
namespace Web{
public static class AppStart_NinjectMVC3 {
public static void RegisterServices(IKernel kernel) {
//kernel.Bind<IThingRepository>().To<SqlThingRepository>();
kernel.Bind<IContributorService>().To<ContributorService>();
}
public static void Start() {
// Create Ninject DI Kernel
IKernel kernel = new StandardKernel();
// Register services with our Ninject DI Container
RegisterServices(kernel);
// Tell ASP.NET MVC 3 to use our Ninject DI Container
DependencyResolver.SetResolver(new NinjectServiceLocator(kernel));
}
}
}
The problem is, I also want to add
kernel.Bind<IRepository>().To<Repository>();
But my Web project does not have a reference to the Data project.. and just adding the reference for this doesnt seem right..
what am I missing?
please help!
http://www.planetgeek.ch/2010/11/13/official-ninject-mvc-extension-gets-support-for-mvc3/
And dont forget to read #Brad Wilson's blog series (ref'd in article)