In ASP.NET core, inject a service with constructor parameters [duplicate] - asp.net-core

Having the following service constructor
public class Service : IService
{
public Service(IOtherService service1, IAnotherOne service2, string arg)
{
}
}
What are the choices of passing the parameters using .NET Core IOC mechanism
services.AddSingleton<IOtherService , OtherService>();
services.AddSingleton<IAnotherOne , AnotherOne>();
services.AddSingleton<IService>(x =>
new Service(
services.BuildServiceProvider().GetService<IOtherService>(),
services.BuildServiceProvider().GetService<IAnotherOne >(),
""));
Is there any other way ?

The expression parameter (x in this case) of the factory delegate is an IServiceProvider.
Use that to resolve the dependencies:
_serviceCollection.AddSingleton<IService>(x =>
new Service(x.GetRequiredService<IOtherService>(),
x.GetRequiredService<IAnotherOne>(),
""));
The factory delegate is a delayed invocation. Whenever the type is to be resolved, it will pass the completed provider as the delegate parameter.

The recommended way to achieve this is to use the Options pattern - note that this applies to any .NET Core/5 application, not just ASP.NET Core. But there are use cases where it's impractical (e.g. when parameters are only known at runtime, not at startup/compile-time) or you need to dynamically replace a dependency.
It's very useful when you need to replace a single dependency (be it a string, integer or another type of dependency) or when using a 3rd-party library which accepts only string/integer parameters and you require runtime parameters.
You could try ActivatorUtilities.CreateInstance<T>(IServiceProvider, Object[]) as a shortcut rather than resolving every single dependency manually:
_serviceCollection.AddSingleton<IService>(x =>
ActivatorUtilities.CreateInstance<Service>(x, "");
);
The parameters to pass to your service's constructor (the object[] parameter to CreateInstance<T>/CreateInstance) allows you to specify parameters that should be injected directly, as opposed to resolved from the service provider. They are applied from left to right as they appear (i.e. first string will be replaced with the first string-typed parameter of the type to be instantiated).
ActivatorUtilities.CreateInstance<Service> is used in many places to resolve service and replace one of the default registrations for this single activation.
For example, if you have a class named MyService, and it has IOtherService, ILogger<MyService> as dependencies and you want to resolve the service but replace the default service of IOtherService (say it's OtherServiceA) with OtherServiceB, you could do something like:
myService = ActivatorUtilities.CreateInstance<Service>(serviceProvider,
new OtherServiceB());
Then the first parameter of IOtherService will get OtherServiceB injected, rather than OtherServiceA - but the remaining parameters will come from the service provider.
This is helpful when you have many dependencies and want just to treat a single one specially (i.e. replace a database-specific provider with a value configured during the request or for a specific user, something you only know at runtime and/or during a request - not when the application is built/started).
If performance is a concern, you can use ActivatorUtilities.CreateFactory(Type, Type[]) to create a factory method instead. GitHub reference and benchmark.
This is useful when the type is resolved very frequently (such as in SignalR and other high request scenarios). Basically, you'd create an ObjectFactory via
var myServiceFactory = ActivatorUtilities.CreateFactory(typeof(MyService), new Type[] { typeof(IOtherService), });
then cache it (as a variable etc.) and invoke it where needed:
MyService myService = myServiceFactory(serviceProvider, myServiceOrParameterTypeToReplace);
This all works perfectly with primitive types too - here's an example I tested with:
class Program
{
static void Main(string[] args)
{
var services = new ServiceCollection();
services.AddTransient<HelloWorldService>();
services.AddTransient(p => p.ResolveWith<DemoService>("Tseng", "Stackoverflow"));
var provider = services.BuildServiceProvider();
var demoService = provider.GetRequiredService<DemoService>();
Console.WriteLine($"Output: {demoService.HelloWorld()}");
Console.ReadKey();
}
}
public class DemoService
{
private readonly HelloWorldService helloWorldService;
private readonly string firstname;
private readonly string lastname;
public DemoService(HelloWorldService helloWorldService, string firstname, string lastname)
{
this.helloWorldService = helloWorldService ?? throw new ArgumentNullException(nameof(helloWorldService));
this.firstname = firstname ?? throw new ArgumentNullException(nameof(firstname));
this.lastname = lastname ?? throw new ArgumentNullException(nameof(lastname));
}
public string HelloWorld()
{
return this.helloWorldService.Hello(firstname, lastname);
}
}
public class HelloWorldService
{
public string Hello(string name) => $"Hello {name}";
public string Hello(string firstname, string lastname) => $"Hello {firstname} {lastname}";
}
// Just a helper method to shorten code registration code
static class ServiceProviderExtensions
{
public static T ResolveWith<T>(this IServiceProvider provider, params object[] parameters) where T : class =>
ActivatorUtilities.CreateInstance<T>(provider, parameters);
}
Prints
Output: Hello Tseng Stackoverflow

If you feel uncomfortable with newing the service, you could use the Parameter Object pattern.
So extract the string parameter into its own type
public class ServiceArgs
{
public string Arg1 {get; set;}
}
And the constructor will now look like
public Service(IOtherService service1,
IAnotherOne service2,
ServiceArgs args)
{
}
And the setup
_serviceCollection.AddSingleton<ServiceArgs>(_ => new ServiceArgs { Arg1 = ""; });
_serviceCollection.AddSingleton<IOtherService , OtherService>();
_serviceCollection.AddSingleton<IAnotherOne , AnotherOne>();
_serviceCollection.AddSingleton<IService, Service>();
The first benefit is if you need to change the Service constructor and add new services to it, then you don't have to change the new Service(... calls. Another benefit is the setup is a bit cleaner.
For a constructor with a single parameter or two, this might be too much though.

You can inject dependencies with this process also
_serviceCollection.AddSingleton<IOtherService , OtherService>();
_serviceCollection.AddSingleton<IAnotherOne , AnotherOne>();
_serviceCollection.AddSingleton<IService>(x=>new Service( x.GetService<IOtherService>(), x.GetService<IAnotherOne >(), "" ));

Related

AutoFixture with AutoNSubstituteCustomization: Set object count on ReadOnly IEnumerable<t> property

My test requires that I have different counts of objects in an IEnumerable property of the main entity collection. I have been searching for documentation about this but can't find anything. Here is a sample of what I mean (note that the base entity is created using AutoNSubstituteCustomization)
IFixture fixture = new Fixture().Customize(new AutoNSubstituteCustomization() { ConfigureMembers = true });
var t = fixture.CreateMany<ITransaction>(5)
var service1 = Substitute.For<ITransactionsSvc>();
service1.GetTransactions().ReturnsForAnyArgs(t);
var service2 = Substitute.For<IRequestsSvc>();
service2.GetRequest(default).ReturnsForAnyArgs(
new Result(){
TransactionId = t.First().Files.First().RequestId
}
);
Where ITransaction would look like
public interface ITransaction
{
long RequestId { get; }
IEnumerable<FileDef> Files { get; }
IEnumerable<Comment> Comments { get; }
}
I know I could set fixture.RepeatCount to specify the global count but how can I have a different value for Files and Comments?
I already tried using t.With(x => x.Files, () => fixture.CreateMany<FileDef>(rnd.Next(1,5)) but it throws saying this is a readonly property.
I also tried using NSubstitute .Returns on the t.Files property but for some reason, the type of RequestId got changed from Int64 to Task`1 when trying to read the value for service2 ReturnForAnyArgs response.
I know I had to remove some of the complexity from the real case so that is stays concise so I hope I didn't remove too much and kept it understandable. If you need any precisions, feel welcome to ask.
Sub-question: is there any complete documentation on AutoFixture? On AutoFixture website I was only able to find very introductory documentation.
It seems that the issue you're having is not related to AutoFixture but rather with NSubstitute.
Since ITransaction is an interface AutoFixture will delegate the task of creating and instance to the mocking library. In your case that's NSubstitute.
Since your interface only declares getters but no setters, NSubstitute will generate a dynamic proxy, for your interface, that as will as well not have any public setters. This is why AutoFixture is unable to set the values of your properties.
So if you want to continue using the mock, you'll have to either specify a public setter in your interface or tell AutoFixture how to set the values using the NSubstitute API. Unfortunately you'll be able to implement the second option only by implementing an ISpecimenBuilder factory for your interface and then play with reflection.
Another way, which is what I recommend, is to relay the setup of your interface to a fake implementation, which you'll create by hand and which will have the public setters. Then you'll instruct AutoFixture to relay all requests to the interface to your fake class.
[Fact]
public void MyTest()
{
var fixture = new Fixture();
fixture.Customize<FakeTransaction>(c => c
.With(x => x.Files, fixture.CreateMany<FileDef>(2).ToList())
.With(x => x.Comments, fixture.CreateMany<Comment>(5).ToList()));
fixture.Customizations.Add(new TypeRelay(typeof(ITransaction), typeof(FakeTransaction)));
ITransaction mock2 = fixture.Create<ITransaction>();
Assert.Equal(2, mock2.Files.Count());
Assert.Equal(5, mock2.Comments.Count());
}
public class FakeTransaction : ITransaction
{
public long RequestId { get; set; }
public IEnumerable<FileDef> Files { get; set; }
public IEnumerable<Comment> Comments { get; set; }
}
Protip: In order to not repeat the relay everywhere, you could create a customization that will add the relay to the fixture, and then combine it with your current NSubstitute customization using CompositeCustomization. Read more here.
About your second question. Unfortunately that is the only "official" documentation. The current effort is going to releasing the next version.
For more info you can refer to the maintainer blogs and this community documentation site. Also there is a cool Pluralsight course available here.
In fact, as #AndreiIvascu mentionned, the problem AND the cleanest solution I found were linked to NSubstitute. Since NSubstitute is creating the instances, theses instances can be configured using standard NSubstitute calls.
The solution is simply to use Returns and ReturnsForAnyArgs as I mentionned but it is essential that this newly created substitute is not used directly in the definition of a second substitute as it was the case when accessing the RequestId.
Note the line var requestId = t.First().Files.First().RequestId; that is now outside of the new Result() definition.
public void MyTest()
{
IFixture fixture = new Fixture().Customize(new AutoNSubstituteCustomization() { ConfigureMembers = true });
var t = fixture.Create<ITransaction>();
t.Files.Returns(fixture.CreateMany<FileDef>(2).ToList());
t.Comments.Returns(fixture.CreateMany<Comment>(5).ToList());
var service1 = Substitute.For<ITransactionsSvc>();
service1.GetTransactions().ReturnsForAnyArgs(t);
var requestId = t.First().Files.First().RequestId;
var service2 = Substitute.For<IRequestsSvc>();
service2.GetRequest(default).ReturnsForAnyArgs(
new Result(){
TransactionId = requestId
}
);
}

Azure Function Uses Wrong DbContext Constructor

I have an existing EF Core 2.2 DbContext that works fine in an ASPNET Core application as well as LinqPad. Now I am trying to add it to an Azure function. In both ASPNET and the Azure function I am using dependency injection.
The DbContext class has three constructors - an empty one, one that takes a connection string and another that takes a DbOptionsBuilder instance. The ASPNET Core app seems to invoke the one that takes the DbOptionsBuilder instance while LinqPad uses the one that takes the connection string. As I said, both of these work fine.
The Azure function app tries to use the one that takes a string, but it passes null instead of a value. This causes an error later saying that a provider hasn't been configured.
I can force the function app to use the DbOptionsBuilder constructor by removing the one that takes a string. When I do this the function app works fine. However, I can no longer use the context in LinqPad if I do.
My question is, first, how can I make the Azure function call the appropriate constructor without removing the others? Second, and less importantly, why the different behavior between the ASPNET runtime and the Azure function runtime?
EDIT
I am only running the AZ function locally at this point so it is reading the connection string from 'local.settings.json' file. This part is working.
Here is the Startup.Configure method of the function project.
public class Startup : FunctionsStartup
{
/// <summary>
/// This method gets called by the runtime. Use this method to add services to the DI container.
/// </summary>
/// <param name="builder">The function host builder</param>
public override void Configure(IFunctionsHostBuilder builder)
{
// Add database context
string env = Environment.GetEnvironmentVariable("AZURE_FUNCTIONS_ENVIRONMENT");
string connectionString = Environment.GetEnvironmentVariable($"ConnectionStrings:{env}");
builder.Services.AddDbContext<FullContext>(x => x.UseSqlServer(connectionString), ServiceLifetime.Transient);
}
}
As I said, it is reading the connection string and appears to pass it to the AddDbContext method. But something is going wrong somewhere.
EDIT 2
Here are the three constructors from my DbContext subclass. Nothing special. Also including the OnConfiguring method.
public FullContext() { }
public FullContext(string connectionString)
{
ConnectionString = connectionString;
}
public FullContext(DbContextOptions<FullContext> options) : base(options) { }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (ConnectionString != null)
optionsBuilder.UseSqlServer(ConnectionString);
base.OnConfiguring(optionsBuilder);
}
EDIT 3
After reviewing the link #Jack Jia suggested I tried the following.
First, I create my own instance of the DbContextOptionsBuilder and specify the provider and connection string.
var options = new DbContextOptionsBuilder<FullContext>();
options.UseSqlServer(connectionString);
I then try to force the DI service to use these options. However, this fails when using the AddDbContext method - it still tries to call the wrong constructor using a null string as the parameter.
In other words, this fails:
builder.Services.AddDbContext<FullContext>(x => new FullContext(options.Options), ServiceLifetime.Transient);
but this seems to work:
builder.Services.AddTransient<FullContext>(x => new FullContext(options.Options));
Assuming I am understanding the docs correctly both calls should be forcing the DI service to use the constructor taking an DbContextOptions parameter. But this doesn't seem to be the case.
You may refer to: Service registration methods
If there are multiple constructors, you can specify one as following:
Add{LIFETIME}<{SERVICE}>(sp => new {IMPLEMENTATION})
For example:
// Constructor1
builder.Services.AddScoped<IMyDep>(sp => new MyDep());
// Constructor2
builder.Services.AddScoped<IMyDep>(sp => new MyDep("A string!"));
// Constructor3
builder.Services.AddScoped<IClass1, Class1>();
builder.Services.AddScoped<IMyDep>(sp =>
{
IClass1 class1 = sp.GetRequiredService<IClass1>();
//class1.doSomething(...);
return new MyDep(class1);
});
So, you do not need to change the DbContext class, just specifically use different constructors in different apps.
Where are storing the connections string value?
I would check the source. Out of the box asp.net core has the a application.settings.json file configured for injection. AZ Function does not do this.
If you are using an application.settings.json then you have to configure it to load settings from that file.
Here a sample how to load a config file in DI that allows you to have similar access to the content as in asp.net core:
var config = new ConfigurationBuilder().SetBasePath(Environment.CurrentDirectory)
.AddJsonFile("application.settings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
builder.Services.AddSingleton<IConfiguration>(config);
And getting a value in the Configure method:
string SqlConnectionString = config.GetConnectionString("SqlConnectionString");
This is done in the public override void Configure(IFunctionsHostBuilder builder).
Here is how to use DI in Azure Functions.
The other possibility I can think of is Azure Key Vault or environment variables.

How to replace a dependency in ASP.NET core?

In the startup.cs we have this:
public void ConfigureServices(IServiceCollection services)
{
services.AddLocalization(options => options.ResourcesPath = "Resources");
}
And so our Index Razor page can have this:
public IndexModel(IStringLocalizer<Strings> localizer) {
I want to wrap the localizer in another class and replace it's presence in the IoC container with a singleton of the wrapper.
The problem is that it does not appear possible to retrieve or remove entries from the container while in the COnfigureServices method.
Essentially, I want to replace the registered instance provided by the AddLocalization call so that I don't need to replace every instance of the wrapper class's injection in the solution.
Is this possible?
You can solve this problem with Decorator pattern.
First of all configure the services so that you can access the StringLocalizer<> directly. This is for the MyLocalizer<> class, because it needs a direct instance of StringLocalizer<> type, not the interface (IStringLocalizer<>). If you don't register it MyLocalizer<> would not get resolved.
services.AddTransient(typeof(StringLocalizer<>));
Then register the decorator (the dependency that you want to be replaced). Note that I assumed AddLocalization() is called before this line of code. This is the rule of the DI container; it always resolves the last registered type. So, after this line of code all dependents of IStringLocalizer<> will get MyLocalizer<> instead of the standard StringLocalizer<>.
// be careful about using Singleton scope
services.AddSingleton(typeof(IStringLocalizer<>), typeof(MyLocalizer<>));
Decorator Implementation:
Decorator pattern allows you to add extra features to an existing object. Suppose the IStringLocalizer<T> object returns a simple string that I need to just make it upper-case.
public class MyLocalizer<T> : IStringLocalizer<T>
{
public MyLocalizer(StringLocalizer<T> original)
{
_original = original;
}
private readonly StringLocalizer<T> _original;
// the decorator behavior is the same for all other methods.
// But for this particular method it adds a little feature to the original one! Beautiful :)
public LocalizedString this[string name] =>
new LocalizedString(name, _original[name].Value.ToUpper());
public LocalizedString this[string name, params object[] arguments] =>
_original[name, arguments];
public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures) =>
_original.GetAllStrings(includeParentCultures);
public IStringLocalizer WithCulture(CultureInfo culture) =>
_original.WithCulture(culture);
}
Now, nothing in your dependent classes will change. They just use MyLocalizer<T> instead of MVC's StingLocalizer<T>.
Wish that helps!

VB.Net Merge property class from more than one web references

I have project that need to reference to some web service, just say my reference is
service1Facade and service2Facade
both of them contain class name objectA
i must load objectA from service1Facade and use it as parameter in service2Facade.
but i got error
"value of type service1Facade.objectA cannot be converted to service2Facade.objectA"
how can i convert these object ?
what i have try but still not work:
group all reference into same folder, but .NET change its name into
objectA and objectA1
I copy every property of the property inside objectA, but still not working.
The functionality that is responsible for generating proxy classes based on your WSDL specification doesn't know (and it shouldn't know) that both your services use the same underlying type for objectA, and as I mentioned, no assumptions can be made regarding this since web services are meant to be decoupled from each other (from the consumer point of view).
I'd say your best option is to have your own proxy class (let's say ServiceProxyDTO) that can be used in both service #1 and #2. Something along the lines of:
public class ServiceProxyDTO
{
// Define properties from "objectA"
public ServiceProxyDTO() { }
public ServiceProxyDTO(service1Facade.ObjectA copyFrom)
{
// Copy state from "copyFrom"
}
public ServiceProxyDTO(service2Facade.ObjectA copyFrom)
{
// Copy state from "copyFrom"
}
public static implicit operator service1Facade.ObjectA(ServiceProxyDTO dto)
{
return new service1Facade.ObjectA() { /* Copy state back */ };
}
public static implicit operator service2Facade.ObjectA(ServiceProxyDTO dto)
{
return new service2Facade.ObjectA() { /* Copy state back */ };
}
public static implicit operator ServiceProxyDTO(service1Facade.ObjectA obj)
{
return new ServiceProxyDTO(obj);
}
public static implicit operator ServiceProxyDTO(service2Facade.ObjectA obj)
{
return new ServiceProxyDTO(obj);
}
}
With this code you can instantiate ServiceProxyDTO and pass it as parameter to both service #1 and #2 (as well as get the return values from both of these services).
Hope this helps.

Getting 'Context is not constructible. Add a default constructor or provide an implementation of IDbContextFactory."

I am getting this error when I try to use code first migrations.
My context has a constructor with the connection name.
public class VeraContext : DbContext, IDbContext
{
public VeraContext(string NameOrConnectionStringName = "VeraDB")
: base(NameOrConnectionStringName)
{
}
public IDbSet<User> Users { get; set; }
public IDbSet<Product> Products { get; set; }
public IDbSet<IntCat> IntCats { get; set; }
}
This connection name is injected with ninject when the project runs, I have also specified it as a default as in the above code but this did not help.
kernel.Bind<IDbContext>()
.To<VeraContext>()
.WithConstructorArgument("NameOrConnectionStringName", "VeraDB");
When I try to add migrations with "Enable-Migrations" is throws up the error:
The target context 'VeraData.EF.Infrastructure.VeraContext' is not
constructible. Add a default constructor or provide an implementation
of IDbContextFactory.
If I remove the constructor from VeraContext it will work but creates another database with VeraData.EF.Infrastructure.VeraContext as its name.
I presume that ninject only passes the connection string when the project runs and not when I use code first migrations. Anyway I can inject/provide a default for the connection name when using code first migrations ?
Essentially you need a default ctor (that's the error) - but just implementing it would lead to problems.
You'd have to implement the IDbContextFactory for the results to be consistent (or your migration from code won't work etc.).
Migrations actually call your default constructor to make a
connection. So you're other ctor won't matter much.
Here is the basic factory...
public class MyContextFactory : IDbContextFactory<MyContext>
{
public MyContext Create()
{
return new MyDBContext("YourConnectionName");
}
}
You should combine that with injection, to inject and construct your DbContext as you wish.
If you don't want to spend time looking into the IDbContextFactory option, and to get things working create a default constructor and hard-code the name of the connection string when calling the base DbContext:
public class CustomContext : DbContext
{
public CustomContext() :base("name=Entities") {}
}
SRC: http://www.appetere.com/Blogs/SteveM/April-2012/Entity-Framework-Code-First-Migrations
To complement #nccsbim071 answer, I have to add one more thing... this option doesn't like constructor with default parameters... for instance:
public MyContext(bool paramABC = false) : base("name=Entities") {...}
instead you have to create a non-parameter (default) constructor and the parameter-constructor like old fashion way.
public MyContext() :base("name=Entities") {...}
public MyContext(bool paramABC) : this() {...}
NOTE:
Entities in this case means the connection string name... By convention, the name of the context is the same as the connection string name and since MyContext is not the same as Entities, it's necessary specify it manually.
In my situation I wanted to use the default connection factory, instead of explicitly providing one. Somewhere inside EF6 it'll try to lookup the factory, but it fails with this exception message. Stepping through the EF6 code, I found that Glimpse.Ado was wrapping the connection factory, which made the lookup fail to find a match.