Line data Source code
1 : #region Copyright
2 : // // -----------------------------------------------------------------------
3 : // // <copyright company="Chinchilla Software Limited">
4 : // // Copyright Chinchilla Software Limited. All rights reserved.
5 : // // </copyright>
6 : // // -----------------------------------------------------------------------
7 : #endregion
8 :
9 : using System;
10 : using System.Collections.Concurrent;
11 : using System.Collections.Generic;
12 : using System.Linq;
13 : using Akka.Actor;
14 : using Cqrs.Akka.Configuration;
15 : using Cqrs.Akka.Domain;
16 : using Cqrs.Configuration;
17 : using Cqrs.Domain;
18 : using Cqrs.Domain.Factories;
19 : using Cqrs.Ninject.Configuration;
20 : using Ninject;
21 :
22 : namespace Cqrs.Ninject.Akka
23 : {
24 : /// <summary>
25 : /// Provides an ability to resolve instances of objects and Akka.NET objects using Ninject
26 : /// </summary>
27 : public class AkkaNinjectDependencyResolver
28 : : NinjectDependencyResolver
29 : , IAkkaAggregateResolver
30 : , IAkkaSagaResolver
31 : , IHandlerResolver
32 1 : {
33 : /// <summary>
34 : /// The inner resolver used by Akka.NET
35 : /// </summary>
36 : protected global::Akka.DI.Ninject.NinjectDependencyResolver RawAkkaNinjectDependencyResolver { get; set; }
37 :
38 : /// <summary>
39 : /// The <see cref="ActorSystem"/> as part of Akka.NET.
40 : /// </summary>
41 : protected ActorSystem AkkaSystem { get; private set; }
42 :
43 : /// <summary>
44 : /// A generic type, quick reference, lookup for fast resolving of Akka.NET objects since the patterns calls for them to be treated like statics
45 : /// </summary>
46 : protected IDictionary<Type, IActorRef> AkkaActors { get; private set; }
47 :
48 : /// <summary>
49 : /// The <see cref="IAggregateFactory"/> that will be used to create new instances of Akka.NET objects.
50 : /// </summary>
51 : protected IAggregateFactory AggregateFactory { get; private set; }
52 :
53 : /// <summary>
54 : /// Instantiates a new instance of <see cref="AkkaNinjectDependencyResolver"/>
55 : /// </summary>
56 1 : public AkkaNinjectDependencyResolver(IKernel kernel, ActorSystem system)
57 : : base(kernel)
58 : {
59 : RawAkkaNinjectDependencyResolver = new global::Akka.DI.Ninject.NinjectDependencyResolver(kernel, AkkaSystem = system);
60 : AkkaActors = new ConcurrentDictionary<Type, IActorRef>();
61 : // ReSharper disable DoNotCallOverridableMethodsInConstructor
62 : AggregateFactory = Resolve<IAggregateFactory>();
63 : // ReSharper restore DoNotCallOverridableMethodsInConstructor
64 : }
65 :
66 : /// <summary>
67 : /// Checks if an instance of <see cref="IDependencyResolver"/> is already registered, if one is registered, it in unregistered and this instance is registered as the <see cref="IDependencyResolver"/>.
68 : /// It then checks if an instance of <see cref="IAkkaAggregateResolver"/> is already registered, if one is registered, it in unregistered and this instance is registered as the <see cref="IAkkaAggregateResolver"/>
69 : /// </summary>
70 1 : protected override void BindDependencyResolver()
71 : {
72 : bool isDependencyResolverBound = Kernel.GetBindings(typeof(IDependencyResolver)).Any();
73 : if (isDependencyResolverBound)
74 : Kernel.Unbind<IDependencyResolver>();
75 : Kernel.Bind<IDependencyResolver>()
76 : .ToConstant(this)
77 : .InSingletonScope();
78 :
79 : isDependencyResolverBound = Kernel.GetBindings(typeof(IAkkaAggregateResolver)).Any();
80 : if (!isDependencyResolverBound)
81 : {
82 : Kernel.Bind<IAkkaAggregateResolver>()
83 : .ToConstant(this)
84 : .InSingletonScope();
85 : }
86 : }
87 :
88 : /// <summary>
89 : /// Starts the <see cref="AkkaNinjectDependencyResolver"/>
90 : /// </summary>
91 : /// <remarks>
92 : /// This exists so the static constructor can be triggered.
93 : /// </remarks>
94 1 : public new static void Start(IKernel kernel = null, bool prepareProvidedKernel = false)
95 : {
96 : // Create the ActorSystem and Dependency Resolver
97 : ActorSystem system = ActorSystem.Create("Cqrs");
98 :
99 : Func<IKernel, NinjectDependencyResolver> originalDependencyResolverCreator = DependencyResolverCreator;
100 : Func<IKernel, NinjectDependencyResolver> dependencyResolverCreator = container => new AkkaNinjectDependencyResolver(container, system);
101 : if (originalDependencyResolverCreator == null)
102 : DependencyResolverCreator = dependencyResolverCreator;
103 : else
104 : DependencyResolverCreator = container =>
105 : {
106 : originalDependencyResolverCreator(container);
107 : return dependencyResolverCreator(container);
108 : };
109 :
110 : NinjectDependencyResolver.Start(kernel, prepareProvidedKernel);
111 : }
112 :
113 : /// <summary>
114 : /// Calls <see cref="ActorSystem.Shutdown"/>
115 : /// </summary>
116 1 : public static void Stop()
117 : {
118 : var di = Current as AkkaNinjectDependencyResolver;
119 : if (di != null)
120 : di.AkkaSystem.Shutdown();
121 : }
122 :
123 : #region Overrides of NinjectDependencyResolver
124 :
125 : /// <summary>
126 : /// Resolves instances of <paramref name="serviceType"/> using <see cref="Resolve(System.Type, Object)"/>.
127 : /// </summary>
128 1 : public override object Resolve(Type serviceType)
129 : {
130 : return Resolve(serviceType, null);
131 : }
132 :
133 : #endregion
134 :
135 : #region Implementation of IAkkaAggregateResolver
136 :
137 : /// <summary>
138 : /// Resolves instances of <typeparamref name="TAggregate"/> using <see cref="AkkaResolve"/>.
139 : /// </summary>
140 1 : public virtual IActorRef ResolveActor<TAggregate, TAuthenticationToken>(Guid rsn)
141 : where TAggregate : IAggregateRoot<TAuthenticationToken>
142 : {
143 : return (IActorRef)AkkaResolve(typeof(TAggregate), rsn, true);
144 : }
145 :
146 : /// <summary>
147 : /// Resolves instances of <typeparamref name="T"/> using <see cref="AkkaResolve"/>.
148 : /// </summary>
149 1 : public IActorRef ResolveActor<T>()
150 : {
151 : return (IActorRef)AkkaResolve(typeof(T), null, true);
152 : }
153 :
154 : #endregion
155 :
156 : #region Implementation of IAkkaSagaResolver
157 :
158 : /// <summary>
159 : /// Resolves instances of <typeparamref name="TSaga"/> using <see cref="ResolveSagaActor{TSaga,TAuthenticationToken}"/>.
160 : /// </summary>
161 : IActorRef IAkkaSagaResolver.ResolveActor<TSaga, TAuthenticationToken>(Guid rsn)
162 : {
163 : return ResolveSagaActor<TSaga, TAuthenticationToken>(rsn);
164 : }
165 :
166 : /// <summary>
167 : /// Resolves instances of <typeparamref name="TSaga"/> using <see cref="AkkaResolve"/>.
168 : /// </summary>
169 1 : public virtual IActorRef ResolveSagaActor<TSaga, TAuthenticationToken>(Guid rsn)
170 : where TSaga : ISaga<TAuthenticationToken>
171 : {
172 : return (IActorRef)AkkaResolve(typeof(TSaga), rsn, true);
173 : }
174 :
175 : #endregion
176 :
177 : /// <summary>
178 : /// Resolves instances of <paramref name="serviceType"/> using <see cref="IDependencyResolver.Resolve{T}"/>.
179 : /// </summary>
180 1 : protected virtual object RootResolve(Type serviceType)
181 : {
182 : return base.Resolve(serviceType);
183 : }
184 :
185 : /// <summary>
186 : /// Resolves instances of <paramref name="serviceType"/> using <see cref="AkkaResolve"/>.
187 : /// </summary>
188 1 : public virtual object Resolve(Type serviceType, object rsn)
189 : {
190 : return AkkaResolve(serviceType, rsn);
191 : }
192 :
193 : /// <summary>
194 : /// Resolves instances of <paramref name="serviceType"/> looking up <see cref="AkkaActors"/>, then <see cref="IDependencyResolver.Resolve{T}"/> and finally <see cref="AggregateFactory"/>.
195 : /// </summary>
196 1 : public virtual object AkkaResolve(Type serviceType, object rsn, bool isAForcedActorSearch = false)
197 : {
198 : IActorRef actorReference;
199 : try
200 : {
201 : if (AkkaActors.TryGetValue(serviceType, out actorReference))
202 : return actorReference;
203 : if (!isAForcedActorSearch)
204 : return base.Resolve(serviceType);
205 : }
206 : catch (ActivationException) { throw; }
207 : catch ( /*ActorInitialization*/Exception) { /* */ }
208 :
209 : Props properties;
210 : Type typeToTest = serviceType;
211 : while (typeToTest != null)
212 : {
213 : Type[] types = typeToTest.GenericTypeArguments;
214 : if (types.Length == 1)
215 : {
216 : Type aggregateType = typeof (AkkaAggregateRoot<>).MakeGenericType(typeToTest.GenericTypeArguments.Single());
217 : if (typeToTest == aggregateType)
218 : {
219 : typeToTest = aggregateType;
220 : break;
221 : }
222 : Type sagaType = typeof (AkkaSaga<>).MakeGenericType(typeToTest.GenericTypeArguments.Single());
223 : if (typeToTest == sagaType)
224 : {
225 : typeToTest = sagaType;
226 : break;
227 : }
228 : }
229 : typeToTest = typeToTest.BaseType;
230 : }
231 :
232 : // This sorts out an out-of-order binder issue
233 : if (AggregateFactory == null)
234 : AggregateFactory = Resolve<IAggregateFactory>();
235 :
236 : if (typeToTest == null || !(typeToTest).IsAssignableFrom(serviceType))
237 : properties = Props.Create(() => (ActorBase)RootResolve(serviceType));
238 : else
239 : properties = Props.Create(() => (ActorBase) AggregateFactory.Create(serviceType, rsn as Guid?, false));
240 : string actorName = serviceType.FullName.Replace("`", string.Empty);
241 : int index = actorName.IndexOf("[[", StringComparison.Ordinal);
242 : if (index > -1)
243 : actorName = actorName.Substring(0, index);
244 : actorReference = AkkaSystem.ActorOf(properties, string.Format("{0}~{1}", actorName, rsn));
245 : AkkaActors.Add(serviceType, actorReference);
246 : return actorReference;
247 : }
248 : }
249 : }
|