Giter Club home page Giter Club logo

stronginject's Issues

Include full dependency chain in error message

I have have an error as follows:

Error while resolving dependencies for 'Pttox.Core.ViewModels.GroupPttViewModel': We have no source for instance of type 'Xamarin.Forms.Page?'

GroupPttViewModel doesn't have a direct dependency on Page, and GroupPttViewModel's depedency tree is quite large so finding the things that needs Page is hard.

Searching the code for Page is hopeless because it's a Xamarin.Forms app so page is everywhere.

Would it be possible to make the error message include the full dependency chain, or at least include the type that has a direct dependency on Page.

Natively support injecting IReadOnlyCollection<T> and ImmutableArray<T>

It's nice if classes don't require a type that they can mutate (T[]). It's a small thing, but it could matter if the type is also used without StrongInject, for example in test code.

I use ImmutableArray<T> except in existing projects that can't or don't yet reference System.Collections.Immutable.

The reason I'm not asking about IReadOnlyList<T> is that I use that type instead of IReadOnlyCollection<T> when I want to imply that the index order is significant. I'm assuming that StrongInject doesn't have an ordering guarantee that is obvious to the reader since it could be collecting implementations from various separate files.

Add Optional Parameters support

If an optional parameter cannot be resolved it should be a warning.

If users do not want this warning it is simple enough for them to suppress it.

If this causes users issues we may revisit this later.

Add synchronous API

For users that don't require async initialization, having to making everything asynchronous is unneccessary complication.

We should have essentially two API, a synchronous one and an asychronous one:

IFactory and IAsyncFactory
IContainer and IAsyncContainer
IInstanceProvider and IAsyncInstanceProvider
IRequiresInitialization and IRequiresAsyncInitialization

It will be an error if any async dependency is required for resolving an IContainer resolution.

Source not being generated

I have a .net 5 WPF project that I tried to use StronInject on. But the injection code is not being generated.

My Container is as follows:

using JsonSrcGenInstantAnswer.ViewModels;
using StrongInject;

namespace JsonSrcGenInstantAnswer
{
   [Register(typeof(SearchViewModel))]
   [Register(typeof(MainWindow))]
   public partial class InstantAnswerContainer : IContainer<MainWindow>
   {
   }
}

However when I build the build fails I get the following errors in my build output (but strangely not in my Errors list)

1>C:\Work\JsonSrcGenInstantAnswer\JsonSrcGenInstantAnswer\InstantAnswerContainer.cs(8,50,8,72): error CS0535: 'InstantAnswerContainer' does not implement interface member 'IContainer<MainWindow>.Run<TResult, TParam>(Func<MainWindow, TParam, TResult>, TParam)'
1>C:\Work\JsonSrcGenInstantAnswer\JsonSrcGenInstantAnswer\InstantAnswerContainer.cs(8,50,8,72): error CS0535: 'InstantAnswerContainer' does not implement interface member 'IContainer<MainWindow>.Resolve()'
1>C:\Work\JsonSrcGenInstantAnswer\JsonSrcGenInstantAnswer\InstantAnswerContainer.cs(8,50,8,72): error CS0535: 'InstantAnswerContainer' does not implement interface member 'IDisposable.Dispose()'

[Idea] Variable/Method names readability

Hi Yair,

Version 1.0.8 generates for code like this:

private global::Project.Services.SymbolConverter GetSingleInstanceField2()

Would you consider to change it to something like this:

private global::Project.Services.SymbolConverter GetSymbolConverterField2()

?

Obviously, it's not perfect but it would be much easier to read the generated code. I realize that not that many people actually are supposed to read the generated code. However, I suspect that it is necessary to read the generated code when one hits a bug.

Anyway, it's an idea, maybe you find it worth it.

Thanks for any feedback!

Consider adding an `IReleasingFactory` implementing `IFactory`

This would allow Factories to have control of how to dispose of the objects they create.

Spec:

Add type:

namespace StrongInject
{
   public interface IReleasingFactory<T> : IFactory<T>
   {
       System.Threading.Tasks.ValueTask ReleaseAsync(T instance);
   }
}

For any type registered as an IFactory<T> which implements IReleasingFactory<T>, then for any instance of T created by an instance of the factory, we will call factory.ReleaseAsync(instance) rather than use the default disposal.

Registering a type as IReleasingFactory should produce a warning.

cc @jnm2

Allow Inheriting from Modules

Whilst modules are great for reuse of anything that can be registered statically they can't be used to register anything dynamically, because you can't inject dependencies into them.

If you could inherit from a module that would solve this issue.

This will involve the following changes:

Instances/factories in modules must be public static, or protected.

Inheriting from a module is equivalent to importing a module, except that you also import protected Instances/Factories.

"How it works?" part for README

Hi,

I think a "How it works?" part would be important for the README file, as it would help newcomers to understand the basic concepts of the container (because it is a lot more different than most popular IoC containers available).

I thought of a short chapter early on in the README file, which summarizes the internal mechanisms in a few line, and after the few lines it provides a link for a wiki page which describes it in more detail.

Add Instance attribute

Similar to factory methods, instances can be marked as an [Instance]. To be exported they must be public and static.

Create Wiki

The ReadMe is getting longer and longer. We should create a proper wiki/static website for documentation. This will be an appropriate place for other things like examples as well.

The documentation must be kept in source control in this repository. If we use github wiki we should use a github action to copy documentation to the wiki repository.

Allow Instances and Factory methods to register an IFactory and remove IInstanceProvider

IInstanceProvider<T> and IFactory<T> have identical APIs and it seems pointless keeping them both,

Instead if an [Instance] field/property or a [Factory] method returns something of, or inheriting from, IFactory<T> we should automatically resolve it as a factory.

Then we can remove InstanceProviders, and replace their usages with [Instance] IFactory<T> _instanceProvder;

[Idea] Generate ServiceProvider factory methods for convenience

Great job on stronginject @YairHalberstadt, I'm really impressed by the execution of this really clever idea.

A lot of frameworks and libraries today use Microsoft.Extensions.DependencyInjection, and offer extensions methods that hang off of ServiceCollection. I see the ASP.NET Core example here showing how to integrate the two worlds:

[Register(typeof(WeatherForecastController), Scope.InstancePerResolution)]
[Register(typeof(WeatherForecastProvider), Scope.InstancePerDependency, typeof(IWeatherForecastProvider))]
[Register(typeof(WeatherSummarizer), Scope.SingleInstance, typeof(IWeatherSummarizer))]
[Register(typeof(UsersController), Scope.InstancePerResolution)]
[Register(typeof(DatabaseUsersCache), Scope.SingleInstance, typeof(IUsersCache))]
[Register(typeof(MockDatabase), Scope.SingleInstance, typeof(IDatabase))]
[RegisterDecorator(typeof(DatabaseDecorator), typeof(IDatabase))]
public partial class Container : IContainer<WeatherForecastController>, IContainer<UsersController>
{
    private readonly IServiceProvider _serviceProvider;

    public Container(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    [Factory] private ILogger<T> GetLogger<T>() => _serviceProvider.GetRequiredService<ILogger<T>>();
}

I think it would be really nice if there was a simple attribute that could be used (such as ForwardToServiceProvider, or something to that effect), so that the generic code is auto-generated, and the factory is added automatically. So the sample would become something like:

[Register(typeof(WeatherForecastController), Scope.InstancePerResolution)]
[Register(typeof(WeatherForecastProvider), Scope.InstancePerDependency, typeof(IWeatherForecastProvider))]
[Register(typeof(WeatherSummarizer), Scope.SingleInstance, typeof(IWeatherSummarizer))]
[Register(typeof(UsersController), Scope.InstancePerResolution)]
[Register(typeof(DatabaseUsersCache), Scope.SingleInstance, typeof(IUsersCache))]
[Register(typeof(MockDatabase), Scope.SingleInstance, typeof(IDatabase))]
[RegisterDecorator(typeof(DatabaseDecorator), typeof(IDatabase))]
[ForwardToServiceProvider(typeof(ILogger<>))]
public partial class Container : IContainer<WeatherForecastController>, IContainer<UsersController>
{
}

AutoImplement delegate dependencies

If a type takes e.g. a Func<T> in it's constructor we should auto-implement that.
If it takes a Func<int, T> we should also auto-implement that provided int is used anywhere in the transient dependency chain for that type.

how to inject Func<T,C>

I have type that takes a Func<CallInfo, ISession> as a constructor argument.

I have injected it using a Factory in a module

[Factory]
public Func<CallInfo, ISession> CreateSessionFactory()
{
 ....
}

However I'm getting the following error:

Error while resolving dependencies for Pttox.Core.Services.ILinkHandler: we have no source for instance of type 'Pttox.Core.Services.ISession'

This is odd for several reasons,

  • firstly nothing has a direct ISession dependency (only via the Func)
  • secondly ILinkHandler does not have any dependency at all on ISession not even through the Func

Allow parallelism in async resolution

Currently resolution is linear - dependency 1 is resolved, then dependency 2, then dependency 3, etc.

However when resolution is async, we could kick it off, and then only await it when it's needed, allowing multiple things to be resolved in parallel.

There's 2 parts to this:

  1. Change resolution so it doesn't immediately await dependencies only once they're needed.

  2. Order resolution so that we kick off async tasks as early as possible, and await them as late as possible. I need to think a bit about what algorithm we could use for this.

Included in reference list

Hi,

I've included your repository in my personal C# Source Generators list to help gather these kind of projects in one place and hopefully under single umbrella, thus I propose to add the csharp-sourcegenerator topic to this repository to help with visibility.

I hope you're okay with this. If you happen to know other generator projects, I'll be happy to enlist those in my list as well.

Thanks!

Consider way to support convention based registration

Is it possible for users to use convention based registration. For example a user might want to auto-register all types, or all types with a specific suffix, etc.

In general this relates to plugin functionality. We need to work out how it's possible for users to extend out StrongInjects functionality. dotnet/roslyn#48358 would allow this to be done by running a source generator before StrongInject to generate StrongInject registrations. This does make customizing things painful, but I'm not sure I can think of better alternatives.

Support for [FromServices] attribute?

I have started using StrongInject and love the idea of compile-time DI, but have not been able to get things to work with the [FromServices] attribute.

Specifically I get the following error when trying to inject a service into a controller action using [FromServices], which I presume means that the service is not being resolved by StrongInject at all:

System.InvalidOperationException: No service for type 'StrongInjectTest.Services.IMyTestService' has been registered.
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ServicesModelBinder.BindModelAsync(ModelBindingContext bindingContext)
   at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionContext actionContext, IModelBinder modelBinder, IValueProvider valueProvider, ParameterDescriptor parameter, ModelMetadata metadata, Object value, Object container)
   at Microsoft.AspNetCore.Mvc.Controllers.ControllerBinderDelegateProvider.<>c__DisplayClass0_0.<<CreateBinderDelegate>g__Bind|0>d.MoveNext()

I find the [FromServices] pattern to be very useful to avoid creating too many services in the constructor when they are only used in a single controller action.

Is this currently possible and, if not, is it something which is planned for the future?

Allow internal types, factories, and modules, to be registered with internal modules and containers

Currently internal types cannot be registered at all with modules. This is because modules can be used by containers outside the current assembly, and code generation can not generate code to instantiate internal types outside their declaring assembly.

This is a significant limitation. At the very least we should allow these to be registered with internal modules and containers. Whilst it makes sense to want to allow this on public containers, containers are also modules so I'm not sure whether this will work well.

Use Imperative rather than declarative names for attributes

[Registration] -> [Register]
[FactoryRegistration] -> [RegisterFactory]
[ModuleRegistration] -> [RegisterModule]

etc.

Initially mark existing attributes as [Obsolete("Use RegisterAttribute instead", error: true)]

However after a month or two delete the old attributes.

Provide the consumer type in factories

First of all, love the project. I've been really excited for source generators and this project just uses then so well.

Now, I'm trying to inject a logging interface in my application.
The interface is:

public interface ILogger
{
    void Info(...);
    void Error(...);
}

And my implementation is:

public class Logger<T> : ILogger
{
    public Logger()
    {
        //uses T to get the actual logger from some library
    }

    public void Info(...);
    public void Error(...);
}

As you can see, the consumer type is used in the implementation but not in the interface.
I know that default (at least for microsoft) is to use something like ILogger<T>, but all my projects uses ILogger (and I personally thinks that is better).

I don't know if currently is possible to use StrongInject to resolve this.
One possible solution is to provide the consumer type (the type begin resolved) in the factory methods and in the IFactory interface.

I'm thinking something like this:

public interface ILogger { }

public class Logger<T> : ILogger { }

public class A { public A(ILogger logger) { } }

public class B { public B(C c) { } }

public class C { public C(ILogger logger) { } }

public partial class Container : IContainer<A>, IContainer<B>
{
    [Factory]
    private static ILogger Create<TConsumer>() => new Logger<TConsumer>();
}

var container = new Container();
container.Resolve<A>(); // Here TConsumer is 'A'
container.Resolve<B>() // Here TConsumer is 'C'

Or maybe something like this:

public interface ILogger { }

public class Logger : ILogger { public Logger(Type type) { } }

public class A { public A(ILogger logger) { } }

public class B { public B(C c) { } }

public class C { public C(ILogger logger) { } }

public partial class Container : IContainer<A>, IContainer<B>
{
    [Factory]
    private static ILogger Create(Type constumerType) => new Logger(constumerType);
}

var container = new Container();
container.Resolve<A>(); // Here the parameter 'constumerType' is 'A'
container.Resolve<B>() // Here is 'C'

I understand that this is probably a breaking change and if it's not something that you're not willing to do.

Thanks for the wonderful project ๐Ÿ˜„

Add predefined registrations.

This should wait till support for generic registrations is merged. This will allow us to register Lazy<T>.

Predefined registrations should be overridden by an user defined registrations for the same type.

Support Decorators

A Decorator of SomeInterface must must have exactly one constructor parameter of type SomeInterface and must implement SomeInterface. Multiple decorators can be registered for a type, and all will be used to wrap the underlying instance.

The order decorators will be applied is an implementation detail. No guarantee as to the order is provided and it may change in the future.

Support Generic Factory Methods

It should be possible to mark a generic method as a factory, on condition the return type uses all the method type parameters.

When we can't find a registration for a type, we'll fall back to looking at generic factory methods, and seeing if any of them can provide the type.

Things to think about:
Should we allow the method factory to just return T?
How do we efficiently lookup the which generic methods match the type?
Should we disambiguate generic method candidates based on the constraints?

Feature 'CodeLens references' is currently unavailable due to an internal error

Since installing and starting to use StrongInject I get the following error showing up at the top of the IDE:

Feature 'CodeLens references' is currently unavailable due to an internal error

StreamJsonRpc.RemoteInvocationException: Object reference not set to an instance of an object.
   at StreamJsonRpc.JsonRpc.<InvokeCoreAsync>d__139`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Threading.Tasks.ValueTask`1.get_Result()
   at System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable`1.ConfiguredValueTaskAwaiter.GetResult()
   at Microsoft.CodeAnalysis.Remote.BrokeredServiceConnection`1.<TryInvokeAsync>d__17`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.VisualStudio.Telemetry.WindowsErrorReporting.WatsonReport.GetClrWatsonExceptionInfo(Exception exceptionObject)
RPC server exception:
System.NullReferenceException: Object reference not set to an instance of an object.
      at Microsoft.CodeAnalysis.CodeLens.CodeLensReferencesService.<TryGetMethodDescriptorAsync>d__8.MoveNext()
   --- End of stack trace from previous location where exception was thrown ---
      at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
      at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
      at Microsoft.CodeAnalysis.CodeLens.CodeLensReferencesService.<>c__DisplayClass9_0.<<FindReferenceMethodsAsync>b__0>d.MoveNext()
   --- End of stack trace from previous location where exception was thrown ---
      at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
      at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
      at Microsoft.CodeAnalysis.CodeLens.CodeLensReferencesService.<FindAsync>d__1`1.MoveNext()
   --- End of stack trace from previous location where exception was thrown ---
      at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
      at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
      at Microsoft.CodeAnalysis.Remote.RemoteCodeLensReferencesService.<>c__DisplayClass5_0.<<FindReferenceMethodsAsync>b__0>d.MoveNext()
   --- End of stack trace from previous location where exception was thrown ---
      at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
      at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
      at System.Threading.Tasks.ValueTask`1.get_Result()
      at Microsoft.CodeAnalysis.Remote.BrokeredServiceBase.<RunServiceImplAsync>d__12`1.MoveNext()
   --- End of stack trace from previous location where exception was thrown ---
      at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
      at Microsoft.VisualStudio.Telemetry.WindowsErrorReporting.WatsonReport.GetClrWatsonExceptionInfo(Exception exceptionObject)

Support Microsoft.Extensions.DependencyInjection.Abstractions

It would be nice to be able to use existing infrastructure and just drop in stronginject as a replacement. For example, an existing application might depend on:

  • IServiceCollection
  • IServiceProvider
  • IServiceScope
    Implementing these and other related interfaces might make adoption easier and faster

Get typeof parent in factory

We use serilog for logging and it needs to be initialised with the type of the object it will be logging for (the type it is injected into)

ILogger specificLogger = logger.ForContext(typeof(Parent))

Our types have the ILogger as constructor arguments and out current injection framework creates them for us with the correct context.

I can't see anyway to achieve this with stronginject Factories

Consider adding convention based factories

The current IFactory<T> interface is essentially a Func<ConstructerParams, T>

This requires a lot of boilerplate, since you need to define a constructor, fields and the CreateAsync method.

We could use a marker interface and look for a convention based CreateAsync method which can take parameters.

The current workaround would be to use records/primary constructors to simplify the declaration of a constructor/fields to the bare minimum.

The two options are

Current:

public record Factory(A a, B b) : IFactory<(A, B)>
{
     public ValueTask<(A, B)> CreateAsync() => new ValueTask<(A, B)>((a, b));
}

Suggested:

public class Factory : IFactory<(A, B)>
{
     public ValueTask<(A, B)> CreateAsync(A a, B b) => new ValueTask<(A, B)>((a, b));
}

Resolve method is explicitly implemented, but the readme shows `new Container().Resolve()`

Also, what do you think about only making it explicit if there is more than one IContainer<> implementation on the container class?

Here's the readme source:

stronginject/README.md

Lines 181 to 195 in 4a18700

For these cases you can call the `Resolve` method. This reurns an `Owned<T>` which is essentially a disposable wrapper over `T`. Make sure you call `Owned<T>.Dispose` once you're done using `Owned<T>.Value`.
```csharp
using StrongInject;
public class Program
{
public static void Main()
{
using var ownedOfA = new Container().Resolve();
var a = ownedOfA.Value;
System.Console.WriteLine(a.ToString());
}
}
```

Add Resolve method to IContainer

At the moment StrongInject cannot be used by other containers because it cannot resolve anything for use outside the container.
It can only run a func inside a Run method.

We should add a method Resolve which returns an Owned<T>.

namespace StrongInject
{
    public interface IContainer<T> : IDisposable
    {
        TResult Run<TResult, TParam>(Func<T, TParam, TResult> func, TParam param);
        Owned<T> Resolve();
    }

    public sealed class Owned<T> : IDisposable
    {
        private readonly Action _dispose;
        private int _disposed = 0;

        public Owned(T value, Action dispose)
        {
            Value = value;
            _dispose = dispose;
        }

        public T Value { get; }

        public void Dispose()
        {
            var disposed = Interlocked.Exchange(ref _disposed, 1);
            if (disposed == 1)
            {
                _dispose();
            }
        }
    }
}

Support resolving all registrations for a type.

This would allow resolving IEnumerable<T> for example.

Currently StrongInject doesn't allow you to provide multiple registrations for a type.
We'll have to change the logic so you can register multiple registrations for a type. At resolution we disambiguate the best one as follows:

Any registrations provided by a module are better than any registrations it defines.

If this doesn't give any best registration we produce an error. The user can solve this by adding a registration for the type to the container, which will be better than any imported registrations. If the container already has multiple registrations for a type, the user will have to move all but one registrations to a module, and import the module.

Support init-accessor of properties

Hi, I think it would be awesome if the C# 9.0 init-accessors for properties would be supported. Consider following example:

    public interface IRunnable
    {
        void Run();
    }

    internal class Runnable : IRunnable
    {
        internal IHelloWorldPrinter HelloWorldPrinter { private get; init; }

        public void Run() => HelloWorldPrinter.DoIt();
    }
    
    public interface IHelloWorldPrinter
    {
        void DoIt() => Console.WriteLine("Hello World!");
    }

    internal class HelloWorldPrinter : IHelloWorldPrinter
    {
    }

Without the init-accessor feature I would prefer construtor injection over property injection, because for the latter the property would need to become mutable. In my opinion the init-accessor is changing the game. In the example above, if constructor-injected, then the constructor would initialize a readonly field or get-only property anyway. So by supporting init-accessor the dependency could be injected into the "get-only" (of course there is also the init) property right away.

Additionally, that way the dependencies which are not relevant for the constructor but later on could be separated from the constructor.

One little caveat is of course that the init-injected properties cannot be used in the constructor, because they'll be initialized by the runtime after the constructor run through.

Consider way to control injection for one parameter of a constructor

If a constructor has multiple parameters, one of which needs special treatment, you need to create a factory method which takes all the other parameters as dependencies and manually calls the constructor.

This means if a new parameter is added to the constructor, we have to update the factory as well.

If there was some way to modify a single parameter of a constructor, that would avoid this issue.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.