Closed MaxwellDAssistek closed 1 year ago
@MaxwellDAssistek Hi, thanks for notifying.
Could you please do as stated in the error message and wrap the code in try/catch and
get the error details via exception.TryGetDetails(container)
in the catch?
The only caveat, you need to ask for the actual container via app.Services.GetRequiredService<IContainer>();
@dadhi So there is no code to try/catch. Everything happens in a separate Blazor thread which I have no code running in at all.
What I was able to do is put a breakpoint in DryIoc.ContainerException.WithDetails and I was able to run:
new ContainerException(details, error, string.Format(GetMessage(ErrorCheck.Unspecified, error), Print(arg0), Print(arg1), Print(arg2), Print(arg3))).TryGetDetails(BlazorApp2.Globals.MainContainer)
(Globals is a static class I made just to store the container instance)
Unfortunately all I get is: "Unable to get the service registration for the problematic factory with FactoryID=1000"
I was able to go up the stack and look at the locals in DryIoc.Interpreter.InterpretGetScopedOrSingletonViaFactoryDelegate
s = {CurrentScopeReuse.GetScopedViaFactoryDelegateExpression} null
scope = {Scope} {Name=null}
map = {ImMapEntry<object>} {H:1000,V:System.Object}
$exception = {ContainerException} DryIoc.ContainerException: code: Error.WaitForScopedServiceIsCreatedTimeoutExpired;\r\nmessage: DryIoc has waited for the creation of the scoped or singleton service by the "other party" for the 3000 ticks without the completion. \r\nYou may call `exception.T…
result = {object} null
id = {int} 1000
r = {Container} container with scope {Name=null}\r\n with Rules with {TrackingDisposableTransients, SelectLastRegisteredFactory} and without {ThrowOnRegisteringDisposableTransient, VariantGenericTypesInResolvedCollection}\r\n with FactorySelector=SelectLastRegisteredFactory\r…
noItem = object
oldMap = {ImMapEntry<object>} {H:1000,V:System.Object}
parentArgs = {Interpreter.ParentLambdaArgs} null
e = {CurrentScopeReuse.GetScopedOrSingletonViaFactoryDelegateExpression} r.CurrentOrSingletonScope.GetOrAddViaFactoryDelegate(1000, r => new DefaultCircuitAccessor() {Circuit = Convert(r.CurrentOrSingletonScope.GetOrAddViaFactoryDelegate(999, value(DryIoc.FactoryDelegate), r), Circuit)}, r)
newMap = {ImMapEntry<object>} {H:1000,V:System.Object}
lambda = {Expression} null
paramValues = {Container} container with scope {Name=null}\r\n with Rules with {TrackingDisposableTransients, SelectLastRegisteredFactory} and without {ThrowOnRegisteringDisposableTransient, VariantGenericTypesInResolvedCollection}\r\n with FactorySelector=SelectLastRegisteredFactory\r…
paramExprs = OneParameterLambdaExpression
otherItemRef = {ImMapEntry<object>} {H:1000,V:System.Object}
itemRef = {ImMapEntry<object>} {H:1000,V:System.Object}
disp = {IDisposable} null
lambdaConstExpr = {ConstantExpression} null
@MaxwellDAssistek
Thank you for the info. Hope, I can extract useful bits from it.
This is where that service is added: https://github.com/dotnet/aspnetcore/blob/a7bf7ccdcff6475f144c300a0d21421b6efbb1b3/src/Components/Server/src/DependencyInjection/ComponentServiceCollectionExtensions.cs#L69-L70
Maybe the services that are added to builder.Services are not properly register with DryIoc.
I noticed that if I do builder.Services.AddSingleton<WeatherForecastService>();
, container.GetServiceRegistrations().ToList()
gives no results. But if I do container.Register<WeatherForecastService>();
I do get that service.
Here is a seemingly related Microsoft article: https://learn.microsoft.com/en-us/aspnet/core/migration/50-to-60-samples?view=aspnetcore-7.0#aspnet-core-6-7
Looks like DryIoc needs to expose an IServiceCollection in some way and that might be the only way to register MS things, since they all use extension methods of IServiceCollection.
var diFactory = new DryIocServiceProviderFactory(container);
builder.Host.UseServiceProviderFactory(diFactory);
builder.Host.ConfigureContainer<IServiceCollection>((_, collection) =>
{
collection.AddRazorPages();
collection.AddServerSideBlazor(options =>
{
options.DetailedErrors = true;
});
});
This gives: System.InvalidCastException: Unable to cast object of type 'DryIoc.Container' to type 'Microsoft.Extensions.DependencyInjection.IServiceCollection'.
Edit: This might all be a red-herring. I added container.Populate(builder.Services);
after builder.Build() and now the services show up in container.GetServiceRegistrations().ToList()
so I doubt any of the above is helpful. 😞
@MaxwellDAssistek
I noticed that if I do builder.Services.AddSingleton
();, container.GetServiceRegistrations().ToList() gives no results. But if I do container.Register (); I do get that service.
The problem here is that when you're integrating the existing container
with MS.DI via new DryIocServiceProviderFactory(container)
, by default it will clone the existing registry.
This means whatever is added later to builder.Services
or to container
won't be visible to each other.
To share the services, you need to pass the second parameter as following new DryIocServiceProviderFactory(container, RegistrySharing.Share)
;
I've already got feedback that it is unexpected behavior for many people,
so in the next DryIoc.MS.DI version it will be sharing by default.
Looks like DryIoc needs to expose an IServiceCollection in some way and that might be the only way to register MS things, since they all use extension methods of IServiceCollection
I am not sure about this, never heard of this requirement. But will check, thanks for the link.
@dadhi Still no luck unfortunately. I still get the same exception with the following Program.cs:
using BlazorApp2;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using BlazorApp2.Data;
using DryIoc;
using DryIoc.Microsoft.DependencyInjection;
using Serilog;
var builder = WebApplication.CreateBuilder(args);
var container = new Container(
Rules.MicrosoftDependencyInjectionRules.With(
FactoryMethod.ConstructorWithResolvableArguments,
propertiesAndFields: PropertiesAndFields.Auto)
);
Globals.MainContainer = container;
// Here it goes the integration with the existing DryIoc container
var diFactory = new DryIocServiceProviderFactory(container, RegistrySharing.Share);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor(options =>
{
options.DetailedErrors = true;
});
builder.Services.AddSingleton<WeatherForecastService>();
var logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
.ReadFrom.Configuration(builder.Configuration)
.Enrich.FromLogContext()
.WriteTo.File("Errors/Log_.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
builder.Host.UseSerilog(logger);
builder.Host.UseServiceProviderFactory(diFactory);
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();
This is where that service is added: https://github.com/dotnet/aspnetcore/blob/a7bf7ccdcff6475f144c300a0d21421b6efbb1b3/src/Components/Server/src/DependencyInjection/ComponentServiceCollectionExtensions.cs#L69-L70
I am still not sure about the root cause, but regarding this service Circuit
you may try to check something for me...
Try to override its registration with the following:
container.RegisterDelegate<ICircuitAccessor, ICircuit>(
circuitAccessor => circuitAccessor.Circuit,
Reuse.ScopedOrSingleton,
ifAlreadyRegistered: IfAlreadyRegistered.Replace);
@dadhi Unfortunately, the issue is that ICircuitAccessor is marked internal so its impossible for me to do anything with it without rebuilding aspnetcore.
@MaxwellDAssistek
Don't do this :) Yeah, the internal
is good for authors to avoid breaking changes, but hard for user hacking.
Ok, I will try to re-create the app and update you later.
@MaxwellDAssistek I have added the sample. The error is caused by propertiesAndFields: PropertiesAndFields.Auto
in the rules.
I may imagine that DryIoc tries to resolve the property and the constructor parameter, which form a recursive dependency.
So if you plan to inject some properties, and you are not in the full control of every service (like here with a lot of framework stuff), I would suggest avoiding .Auto
and specify only the particular properties in the individual registrations.
@MaxwellDAssistek ...or you may use container rules with
PropertiesAndFields.All(serviceInfo: (MemberInfo member, Request request) => MySpecificPropertiesOnly(memberInfo));
and filter the properties in MySpecificPropertiesOnly
based on its DeclaringType, Namespace, Name, etc.
@dadhi I can confirm that removing PropertiesAndFields fixed it! I should've guessed that it could be the cause, but it just never caused any issues in the other places we use DryIoc like MAUI.
Thank you for your help!
@MaxwellDAssistek Thanks for investing into this issue. Actually, it helped me to figure out how to quickly prototype required properties support (of C# 11), see https://github.com/dadhi/DryIoc/commit/7ee0ea1b09077da70ef6f5b75dac1136c0fe8565.
That's a fantastic. I think we will end up switching to that right away!
@MaxwellDAssistek Sorry for bothering, I was looking into the related issues (#544). Just wondering, did you see something like this in your project?
@dadhi We don't use file uploads in our projects currently so we did not have that issue, but I was able to reproduce it using the code provided and added what the actual exception is that is hiding. What we've found with Blazor is that they like to hide their internal exceptions under the "Debug" log level so we need to lower the log level to that in order to see the exceptions (as I did in the sample code here using Serilog).
@MaxwellDAssistek Thank you very much, I saw the details on #547.
@MaxwellDAssistek I have "quickly" added the net7.0 target to both DryIoc.dll and DryIoc.Microsoft.DependencyInjection and released the DryIoc.Microsoft.DependencyInjection.6.2.0-preview-01
on NuGet (maybe need some time to indexing).
If you have time, could you check it out?
Hello,
When I try to use DryIoc in my Server-size Blazor project, it causes an internal Blazor error.
To reproduce: