생성자에서 추가 매개 변수와 함께 Ninject를 사용하여 인스턴스 만들기
나는 Ninject를 사용하기로 결정했고 문제에 직면했습니다. 다음 시나리오가 있다고 가정하십시오. IService
이 인터페이스를 구현 하는 인터페이스와 2 개의 클래스가 있습니다. 또한 IService 및 int를 얻는 생성자가있는 클래스가 있습니다. Ninject를 사용하여이 클래스의 인스턴스를 어떻게 만들 수 있습니까 (이 int를 고정하고 싶지 않고 인스턴스를 얻을 때마다 전달하고 싶습니다)?
다음은 상황을 보여주는 코드입니다.
interface IService
{
void Func();
}
class StandardService : IService
{
public void Func()
{
Console.WriteLine("Standard");
}
}
class AlternativeService : IService
{
public void Func()
{
Console.WriteLine("Alternative");
}
}
class MyClass
{
public MyClass(IService service, int i)
{
this.service = service;
}
public void Func()
{
service.Func();
}
IService service = null;
}
class Program
{
static void Main(string[] args)
{
IKernel kernel = new StandardKernel(new InlineModule(
x => x.Bind<IService>().To<AlternativeService>(),
x => x.Bind<MyClass>().ToSelf()));
IService service = kernel.Get<IService>();
MyClass m = kernel.Get<MyClass>();
m.Func();
}
}
는 With.ConstructorArgument
이 목적을 위해 1.0에 존재했다. 2.0에서는 구문이 약간 변경되었습니다.- With.Parameters.ConstructorArgument with ninject 2.0
See Inject value into injected dependency for more details and examples of how to use the context, providers and arguments to pass stuff like this around more correctly.
EDIT: As Steven has elected to pretend my comment is irrelevant, I'd best make clear what I'm saying with some examples (for 2.0):
MyClass m = kernel.Get<MyClass>( new ConstructorArgument( "i", 2) );
which to my eyes is very clear and states exactly what's happening.
If you're in a position where you can determine the parameter in a more global way you can register a provider and do it like this:
class MyClassProvider : SimpleProvider<MyClass>
{
protected override MyClass CreateInstance( IContext context )
{
return new MyClass( context.Kernel.Get<IService>(), CalculateINow() );
}
}
And register it like this:
x => x.Bind<MyClass>().ToProvider( new MyClassProvider() )
NB the CalculateINow()
bit is where you'd put in your logic as in the first answer.
Or make it more complex like this:
class MyClassProviderCustom : SimpleProvider<MyClass>
{
readonly Func<int> _calculateINow;
public MyClassProviderCustom( Func<int> calculateINow )
{
_calculateINow = calculateINow;
}
protected override MyClass CreateInstance( IContext context )
{
return new MyClass( context.Kernel.Get<IService>(), _calculateINow() );
}
}
Which you'd register like so:
x => x.Bind<MyClass>().ToProvider( new MyClassProviderCustom( ( ) => new Random( ).Next( 9 ) ) )
UPDATE: Newer mechanisms which exhibit much improved patterns with less boilerplate than the above are embodied in the Ninject.Extensions.Factory
extension, see: https://github.com/ninject/ninject.extensions.factory/wiki
As stated earlier, if you need to pass a different parameter each time and you have multiple levels in the dependency graph, you might need to do something like this.
A final consideration is that because you haven't specified a Using<Behavior>
, it's going to default to the default as specified/defaulted in the options for the kernel (TransientBehavior
in the sample) which might render fact that the factory calculates i
on the fly moot [e.g., if it the object was being cached]
Now, to clarify some other points in the comments that are being FUDed and glossed over. Some important things to consider about using DI, be it Ninject or whatever else is to:
Have as much as possible done by constructor injection so you dont need to use container specific attributes and tricks. There's a good blog post on that called Your IoC Container is Showing.
Minimise code going to the container and asking for stuff - otherwise your code is coupled to a) the specific container (which the CSL can minimise) b) the way in which your entire project is laid out. There are good blog posts on that showing that CSL isnt doing what you think it does. This general topic is referred to as Service Location vs Dependency Injection. UPDATE: See http://blog.ploeh.dk/2011/07/28/CompositionRoot.aspx for a detailed and complete rationale.
Minimise use of statics and singletons
Don't assume there is only one [global] container and that it's OK to just demand it whenever you need it like a nice global variable. The correct use of multiple modules and
Bind.ToProvider()
gives you a structure to manage this. That way each separate subsystem can work on its own and you wont have low-level components being tied to top-level components, etc.
If someone wants to fill in the links to the blogs I'm referring to, I'd appreciate that (they're all already linked from other posts on SO though, so all of this is just duplication UI've introduced with the aim of avoiding the confusion of a misleading answer.)
Now, if only Joel could come in and really set me straight on what's nice syntax and/or the right way to do this!
UPDATE: While this answer is clearly useful from the number of upvotes it's garnered, I'd like to make the following recommendations:
- The above feels as it's a bit dated and to be honest reflects a lot of incomplete thinking which almost feels embarassing since reading Dependency Injection in .net - Run and buy it now - it's not just about DI, the first half is a complete treatment of all the architecture concerns surrounding it from a man who has spent way too much time here hanging around the dependency injection tag.
- Go read Mark Seemann's top rated posts here on SO right now - you'll learn valuable techniques from every one
'IT TIP' 카테고리의 다른 글
C #의 변수에 액세스하는 것은 원자 적 작업입니까? (0) | 2020.11.24 |
---|---|
누군가 Erlang에서 Pid의 구조를 설명 할 수 있습니까? (0) | 2020.11.24 |
PHP에서 '허용 된 메모리 크기 소진'오류를 안전하게 포착 (0) | 2020.11.24 |
Amazon S3 스타일 API를 로컬로 제공하는 서버가 있습니까? (0) | 2020.11.24 |
기본 멤버 값 모범 사례 (0) | 2020.11.24 |