1- #if UNISTATE_REFLEX_SUPPORT
2-
1+ #if UNISTATE_REFLEX_SUPPORT
2+
33using System ;
4+ using System . Collections . Generic ;
45using Reflex . Core ;
5-
6- namespace UniState
7- {
6+ using Reflex . Enums ;
7+ using Reflex . Resolvers ;
8+
9+ namespace UniState
10+ {
811 public static class ReflexBuildExtensions
912 {
1013 public static void AddStateMachine (
1114 this ContainerBuilder builder ,
1215 Type stateMachineImplementation ,
1316 Type stateMachineContract )
1417 {
15- ValidateStateMachineBindingInput ( stateMachineImplementation , stateMachineContract ) ;
16-
17- builder . AddTransient ( stateMachineImplementation ) ;
18- builder . AddTransient ( container =>
19- {
20- var stateMachine = ( IStateMachine ) container . Resolve ( stateMachineImplementation ) ;
21- stateMachine . SetResolver ( container . ToTypeResolver ( ) ) ;
22-
23- return stateMachine ;
24- } , stateMachineContract ) ;
18+ AddStateMachineInternal ( builder , stateMachineImplementation , stateMachineContract , Lifetime . Transient ) ;
2519 }
26-
20+
2721 public static void AddSingletonStateMachine (
2822 this ContainerBuilder builder ,
2923 Type stateMachineImplementation ,
3024 Type stateMachineContract )
3125 {
32- ValidateStateMachineBindingInput ( stateMachineImplementation , stateMachineContract ) ;
33-
34- builder . AddSingleton ( stateMachineImplementation ) ;
35- builder . AddSingleton ( container =>
36- {
37- var stateMachine = ( IStateMachine ) container . Resolve ( stateMachineImplementation ) ;
38- stateMachine . SetResolver ( container . ToTypeResolver ( ) ) ;
39-
40- return stateMachine ;
41- } , stateMachineContract ) ;
26+ AddStateMachineInternal ( builder , stateMachineImplementation , stateMachineContract , Lifetime . Singleton ) ;
4227 }
43-
28+
4429 public static void AddState ( this ContainerBuilder builder , Type state )
4530 {
4631 ValidateStateBindingInput ( state ) ;
4732
48- builder . AddTransient ( state ) ;
33+ builder . AddTransient ( state , GetStateContracts ( state ) ) ;
4934 }
50-
51- public static void AddState ( this ContainerBuilder builder , Type stateImplementation , Type stateContract )
52- {
53- ValidateStateBindingInput ( stateImplementation , stateContract ) ;
54-
55- builder . AddTransient ( stateImplementation , stateContract ) ;
56- }
57-
35+
36+ public static void AddState ( this ContainerBuilder builder , Type stateImplementation , Type stateContract )
37+ {
38+ ValidateStateBindingInput ( stateImplementation , stateContract ) ;
39+
40+ builder . AddTransient ( stateImplementation , stateContract ) ;
41+ }
42+
5843 public static void AddSingletonState ( this ContainerBuilder builder , Type state )
5944 {
6045 ValidateStateBindingInput ( state ) ;
6146
62- builder . AddSingleton ( state ) ;
47+ builder . AddSingleton ( state , GetStateContracts ( state ) ) ;
6348 }
64-
65- public static void AddSingletonState ( this ContainerBuilder builder , Type stateImplementation ,
66- Type stateContract )
67- {
68- ValidateStateBindingInput ( stateImplementation , stateContract ) ;
69-
70- builder . AddSingleton ( stateImplementation , stateContract ) ;
71- }
72-
73- private static void ValidateStateBindingInput ( Type stateImplementation , Type stateContract )
49+
50+ public static void AddSingletonState ( this ContainerBuilder builder , Type stateImplementation ,
51+ Type stateContract )
52+ {
53+ ValidateStateBindingInput ( stateImplementation , stateContract ) ;
54+
55+ builder . AddSingleton ( stateImplementation , stateContract ) ;
56+ }
57+
58+ private static void ValidateStateBindingInput ( Type stateImplementation , Type stateContract )
59+ {
60+ ValidateStateBindingInput ( stateImplementation ) ;
61+
62+ if ( ! stateContract . IsAssignableFrom ( stateImplementation ) )
63+ {
64+ throw new ArgumentException (
65+ $ "AddState({ stateImplementation . Name } ): Type parameter state must implement { stateContract . Name } .") ;
66+ }
67+ }
68+
69+ private static void ValidateStateBindingInput ( Type state )
7470 {
75- ValidateStateBindingInput ( stateImplementation ) ;
76-
77- if ( ! stateContract . IsAssignableFrom ( stateImplementation ) )
71+ if ( ! typeof ( IExecutableState ) . IsAssignableFrom ( state ) )
7872 {
7973 throw new ArgumentException (
80- $ "AddState({ stateImplementation . Name } ): Type parameter state must implement { stateContract . Name } . ") ;
74+ $ "AddState({ state . Name } ): Type parameter state must implement IState<TPayload> ") ;
8175 }
8276 }
8377
84- private static void ValidateStateBindingInput ( Type state )
78+ private static Type [ ] GetStateContracts ( Type state )
8579 {
86- if ( ! typeof ( IExecutableState ) . IsAssignableFrom ( state ) )
80+ var interfaces = state . GetInterfaces ( ) ;
81+ var contracts = new Type [ interfaces . Length + 1 ] ;
82+ contracts [ 0 ] = state ;
83+
84+ for ( var i = 0 ; i < interfaces . Length ; i ++ )
8785 {
88- throw new ArgumentException (
89- $ "AddState({ state . Name } ): Type parameter state must implement IState<TPayload>") ;
86+ contracts [ i + 1 ] = interfaces [ i ] ;
9087 }
88+
89+ return contracts ;
9190 }
91+
92+ private static void ValidateStateMachineBindingInput ( Type stateMachineImplementation , Type stateMachineContract )
93+ {
94+ if ( stateMachineImplementation == stateMachineContract )
95+ {
96+ throw new ArgumentException (
97+ $ "AddStateMachine<{ stateMachineImplementation . Name } >: Type parameters must differ : " +
98+ "use AddStateMachine() where stateMachineImplementation implements stateMachineContract.\" );" ) ;
99+ }
100+
101+ if ( ! stateMachineContract . IsAssignableFrom ( stateMachineImplementation ) )
102+ {
103+ throw new ArgumentException (
104+ $ "AddStateMachine: Type { stateMachineImplementation . Name } " +
105+ $ "must implement { stateMachineContract . Name } .") ;
106+ }
107+
108+ if ( ! typeof ( IStateMachine ) . IsAssignableFrom ( stateMachineContract ) )
109+ {
110+ throw new ArgumentException (
111+ $ "AddStateMachine: Type { stateMachineContract . Name } " +
112+ $ "must implement IStateMachine.") ;
113+ }
114+ }
115+
116+ private static void AddStateMachineInternal (
117+ ContainerBuilder builder ,
118+ Type stateMachineImplementation ,
119+ Type stateMachineContract ,
120+ Lifetime lifetime )
121+ {
122+ ValidateStateMachineBindingInput ( stateMachineImplementation , stateMachineContract ) ;
92123
93- private static void ValidateStateMachineBindingInput ( Type stateMachineImplementation , Type stateMachineContract )
124+ builder . Bindings . Add ( Binding . Validated (
125+ new ReflexStateMachineResolver ( stateMachineImplementation , lifetime ) ,
126+ stateMachineImplementation ,
127+ stateMachineImplementation ,
128+ stateMachineContract ) ) ;
129+ }
130+
131+ private sealed class ReflexStateMachineResolver : IResolver
94132 {
95- if ( stateMachineImplementation == stateMachineContract )
133+ private readonly Type _stateMachineImplementation ;
134+ private readonly Lifetime _lifetime ;
135+ private readonly List < IDisposable > _disposables = new ( ) ;
136+
137+ private object _instance ;
138+
139+ public Lifetime Lifetime => _lifetime ;
140+
141+ public ReflexStateMachineResolver ( Type stateMachineImplementation , Lifetime lifetime )
96142 {
97- throw new ArgumentException (
98- $ "AddStateMachine<{ stateMachineImplementation . Name } >: Type parameters must differ : " +
99- "use AddStateMachine() where stateMachineImplementation implements stateMachineContract.\" );" ) ;
143+ _stateMachineImplementation = stateMachineImplementation ;
144+ _lifetime = lifetime ;
100145 }
101146
102- if ( ! stateMachineContract . IsAssignableFrom ( stateMachineImplementation ) )
147+ public object Resolve ( Container container )
103148 {
104- throw new ArgumentException (
105- $ "AddStateMachine: Type { stateMachineImplementation . Name } " +
106- $ "must implement { stateMachineContract . Name } .") ;
149+ if ( _lifetime == Lifetime . Singleton && _instance != null )
150+ {
151+ return _instance ;
152+ }
153+
154+ var stateMachine = ( IStateMachine ) container . Construct ( _stateMachineImplementation ) ;
155+ stateMachine . SetResolver ( container . ToTypeResolver ( ) ) ;
156+
157+ if ( _lifetime == Lifetime . Singleton )
158+ {
159+ _instance = stateMachine ;
160+ }
161+
162+ if ( stateMachine is IDisposable disposable )
163+ {
164+ _disposables . Add ( disposable ) ;
165+ }
166+
167+ return stateMachine ;
107168 }
108169
109- if ( ! typeof ( IStateMachine ) . IsAssignableFrom ( stateMachineContract ) )
170+ public void Dispose ( )
110171 {
111- throw new ArgumentException (
112- $ "AddStateMachine: Type { stateMachineContract . Name } " +
113- $ "must implement IStateMachine.") ;
172+ for ( var i = _disposables . Count - 1 ; i >= 0 ; i -- )
173+ {
174+ _disposables [ i ] . Dispose ( ) ;
175+ }
176+
177+ _disposables . Clear ( ) ;
178+ _instance = null ;
114179 }
115180 }
116181 }
117182}
118-
119- #endif
183+
184+ #endif
0 commit comments