Using Unity instead of ASP.Net Core DI IServiceCollection - c#

More and more .NET Core libraries is bound to IServiceCollection. In example, I want to use HttpClientFactory described here in my NET Framework 4.7.1. desktop application. My application is using Unity IoC. I referenced Microsoft.Extensions.Http as NuGet.
But there is a problem: new ASP.Net Core components are bound to Microsoft DI framework for .NetCore - IServiceCollection. In example, registration of HttpClientFactory is here:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient();
}
I was going deeper into MS code and wanted to manually register corresponding interfaces and classes to Unity. This is how services are registered by IServiceCollection:
services.TryAddTransient<HttpMessageHandlerBuilder, DefaultHttpMessageHandlerBuilder>();
services.TryAddSingleton<IHttpClientFactory, DefaultHttpClientFactory>();
This would be no problem to move this to Unity IoC, but I am stucked when I want to register DefaultHttpMessageHandlerBuilder and DefaultHttpClientFactory which have internal visibility. So they are not available for registration outside of MS code.
Do I have any chance how to resolve this situation?

Edit 7.12.2022: Unity is now deprecated, so do not use it for new projects. I replaced it with Autofac.
Based on #davidfowl answer, I have used his second solution and completed code:
These packages need to be referenced from my project (snippet from .csproj):
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Http">
<Version>2.1.1</Version>
</PackageReference>
<PackageReference Include="Unity.Microsoft.DependencyInjection">
<Version>2.0.10</Version>
</PackageReference>
</ItemGroup>
And here is the test that services from ServiceCollection can be resolved from Unity container:
using System;
using System.Linq;
using System.Net.Http;
using Microsoft.Extensions.DependencyInjection;
using Unity;
using Unity.Microsoft.DependencyInjection;
using Xunit;
namespace FunctionalTests
{
public class UnityWithHttpClientFactoryTest
{
/// <summary>
/// Integration of Unity container with MS ServiceCollection test
/// </summary>
[Fact]
public void HttpClientCanBeCreatedByUnity()
{
UnityContainer unityContainer = new UnityContainer();
ServiceCollection serviceCollection = new ServiceCollection();
serviceCollection.AddHttpClient("Google", (c) =>
{
c.BaseAddress = new Uri("https://google.com/");
});
serviceCollection.BuildServiceProvider(unityContainer);
Assert.True(unityContainer.IsRegistered<IHttpClientFactory>());
IHttpClientFactory clientFactory = unityContainer.Resolve<IHttpClientFactory>();
HttpClient httpClient = clientFactory.CreateClient("Google");
Assert.NotNull(httpClient);
Assert.Equal("https://google.com/", httpClient.BaseAddress.ToString());
}
}
}

You have 2 options:
Create a ServiceCollection, add the factory and then call BuildServiceProvider and resolve the IHttpClientFactory. There's an uber sample here https://github.com/aspnet/HttpClientFactory/blob/64ed5889635b07b61923ed5fd9c8b69c997deac0/samples/HttpClientFactorySample/Program.cs#L21.
Use the unity adapter for IServiceCollection https://www.nuget.org/packages/Unity.Microsoft.DependencyInjection/.

Related

How to write program.cs file for .NET 6 Library Project? - getting error "Program using top-level statements must be an executable"

I keep all the data related classes, interfaces, configurations, etc in a separate library project so it can easily be reused for any other project I need (API, WebAssembly, Mobile, Server Pages, etc..)
I have converted all the projects from the solution to .NET6. They all work and build, as the did before, except for the data library which is giving me the following error: Program using top-level statements must be an executable.
How do I write the new .NET 6 Program.cs file for a .NET 6 library?
The new Program.cs file that is not working:
using FlasherData.Context;
using FlasherData.Repositories;
using FlasherData.Repositories.Interfaces;
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<FlasherContext>(options => options.UseSqlite(builder.Configuration.GetConnectionString("FlasherDb")));
builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();
var app = builder.Build();
app.Run();
The old .NET 5 Startup.cs file that worked:
using FlasherData.Context;
using FlasherData.Repositories;
using FlasherData.Repositories.Interfaces;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace FlasherData
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
// SQLite connection and database context
services.AddDbContext<FlasherContext>(options => options.UseSqlite(Configuration.GetConnectionString("FlasherDb")));
// Dependency Injection
services.AddScoped<IUnitOfWork, UnitOfWork>();
}
}
}
The .NET 5 library did not have a Program.cs file as it was not needed in a .NET 5 library project.
This is a link to the entire solution: Flasher
The first example you show isn't library code, but the entry point of an application. Top-level-statements are internally transformed into the Main method of the assembly, it's really just a syntactical alternative. But a library must not have a Main method, and that's why you get the mentioned error.
Therefore, for a library, either just remove the startup code or (to get the equivalent behavior than before) change it back to what it was before. There's no need to use top-level-statements anywhere. When updating an application from .NET 5.0 to .NET 6.0, typically you don't have to change anything (except in the project files), because the existing code base is fully backwards compatible.

Dependency Inject Blazor Version

i wan to use Dependency Injection in blazor i am using visual studio preview the latest version i want to add this code in startup.cs
services.AddSingleton<SengaltonServices>();
but i can not find startup.cs in client to inject in Razor component
For Blazor WASM you setup the DI in Program using WebAssemblyHostBuilder.Services:
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.Services.AddSingleton<SengaltonServices>();
await builder.Build().RunAsync();
}
}

.NET Core MVC Startup equivalent

I am wondering is, since I would like to implement dependency injection container for my web application (MVC) controllers. In .NET Core framework, I used to have a Startup.cs file inside the project which was used for adding transients and dependency injections to the container, also for DbContext:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddDbContext<LibraryContext>();
services.AddTransient<IGetBooksCommand, EfGetBooksCommand>();
}
I would like to know how could I achieve this in the full .NET Framework.
Prior to .Net Core, there is no built-in support for dependency injection the only way to get it was through the use of third-party frameworks such as Autofac, Castle Windsor, Unity, Ninject ..etc
You can check any of these frameworks and use them in your project.
.Net core Mvc supports built in dependency injection and it is capable of injecting dependencies in the controllers. So dependency registered as below can be used in controller
services.AddTransient<IGetBooksCommand, EfGetBooksCommand>();
However built in dependency injection functionality can be replaced by more mature DI frameworks. And that is very simple as below is the example for using Autofac
public IServiceProvider ConfigureServices(IServiceCollection services) {
var builder = new ContainerBuilder();
builder.Populate(services);
builder.RegisterType<EfGetBooksCommand>().As<IGetBooksCommand>();
var container = builder.Build();
return new AutofacServiceProvider(container);
}
ConfigureServices method now returns IServiceProvider instead of void. And dependencies will now be resolved using Autofac.
ref :
Dependency injection into controllers in ASP.NET Core
.Net Core Dependency Injection

Structure Map method Populate() doesn't work for ASP.NET Core

According to StructureMap documentation and examples from StructureMap.Microsoft.DependencyInjection repository it has to work but it doesn't.
Here is my Startup class:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddTransient<IMovieRepository, MovieRepository>();
var container = new Container();
container.Configure(config =>
{
config.AddRegistry(new MyRegistry());
config.Populate(services);
});
return container.GetInstance<IServiceProvider>();
}
And Registry:
public class MyRegistry : Registry
{
public MyRegistry()
{
For<IMovieRepository>().Transient().Use<MovieRepository>();
}
}
And here is error screenshot:
What's wrong with my code?
You should also add the following nuget package to your project in order to use the Populate method of the Configuration option.
The package name: StructureMap.Microsoft.DependencyInjection
You do not have to import this library to the startup class though. "using StructureMap" there handles everything.
I decided to change IoC to Autofac. And the same problem appeared. I was following autofac documentation for asp.net core and skip a little detail. It took three days to figure out that I referenced to the wrong package. I referenced to the autofac package when what I was truly need was Autofac.Extensions.DependencyInjection package. It's ridiculous mistake that kick me off for a three days. I am truly convinced that the same kind of mistake I did with structure map, so just look for StructureMap.AspNetCore package instead of StructureMap package and everything will work.
!Read documentation extremely attentively!

Convention based binding in ASP.NET 5 / MVC 6

It is possible to register dependencies manually:
services.AddTransient<IEmailService, EmailService>();
services.AddTransient<ISmsService, SmsService>();
When there are too much dependencies, it becomes difficult to register all dependencies manually.
What is the best way to implement a convention based binding in MVC 6 (beta 7)?
P.S. In previous projects I used Ninject with ninject.extensions.conventions. But I can't find a Ninject adapter for MVC 6.
No, there is no support for batch registration in the ASP.NET 5 built-in DI library. As a matter of fact, there are many features that are needed to build large SOLID applications, but are not included in the built-in DI library.
The included ASP.NET DI library is primarily meant to extend the ASP.NET system itself. For your application, you are best off using one of the mature DI libraries out there, and keep your configuration separate from the configuration that used to configure the ASP.NET system itself.
This removes the need for an adapter.
An MVC 6 adapter exists, but seeing as ASP.net 5 is still in Release candidate, it isn't yet available on NuGet so you'll need to add the ASP.NET 5 "master" branch feed from MyGet to your Visual Studio NuGet package sources.
A walkthrough to do this is available here:
http://www.martinsteel.co.uk/blog/2015/using-ninject-with-mvc6/
If it is still interesting for someone.
This is my solution of the issue with Autofac. It is required Autofac and Autofac.Extensions.DependencyInjection NuGet packages.
// At Startup:
using Autofac;
using Autofac.Extensions.DependencyInjection;
// ...
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// Some middleware
services.AddMvc();
// Not-conventional "manual" bindings
services.AddSingleton<IMySpecificService, SuperService>();
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterModule(new MyConventionModule());
containerBuilder.Populate(services);
var autofacContainer = containerBuilder.Build();
return autofacContainer.Resolve<IServiceProvider>();
}
This is the convention module:
using Autofac;
using System.Reflection;
using Module = Autofac.Module;
// ...
public class MyConventionModule : Module
{
protected override void Load(ContainerBuilder builder)
{
var assemblies = new []
{
typeof(MyConventionModule).GetTypeInfo().Assembly,
typeof(ISomeAssemblyMarker).GetTypeInfo().Assembly,
typeof(ISomeOtherAssemblyMarker).GetTypeInfo().Assembly
};
builder.RegisterAssemblyTypes(assemblies)
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
}
}

Categories