porta microsoft management c# dependency-injection azure-service-fabric actor

c# - microsoft - porta azure



Inyección de dependencia de actor de la tela de servicio de Azure (5)

¿Hay alguna forma de inyectar dependencias en el constructor del Actor de tejido de servicios de Azure?


Actualizado

Todo está en github y myget ahora: https://github.com/s-innovations/S-Innovations.ServiceFabric.Unity

y se integra con la inyección de dependencias del núcleo aspnet sin mucha molestia, verifique los ejemplos de readme.md

Soy un usuario de Unity desde hace mucho tiempo y decidí crear los métodos de extensión principales necesarios para tener una buena experiencia de inyección de dependencia al trabajar con actores.

Mi programa.cs ahora se ve así:

internal static class Program { /// <summary> /// This is the entry point of the service host process. /// </summary> private static void Main() { try { using (var container = new UnityContainer()) { container.RegisterType<IMessageProcessorClientFactory, DummyFactory>(new HierarchicalLifetimeManager()); container.RegisterType<IMessageClusterConfigurationStore, test>(new HierarchicalLifetimeManager()); container.WithFabricContainer(); container.WithActor<MessageClusterActor>(); container.WithActor<QueueListenerActor>(); container.WithStatelessFactory<ManagementApiServiceFactory>("ManagementApiServiceType"); container.WithActor<VmssManagerActor>(); ServiceFabricEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(ManagementApiService).Name); Thread.Sleep(Timeout.Infinite); // Prevents this host process from terminating to keep the service host process running. } } catch (Exception e) { ServiceFabricEventSource.Current.ActorHostInitializationFailed(e.ToString()); throw; } } }

Donde yo en actores y servicios solo puedo poner mis dependencias en los constructores.

public class VmssManagerActor : StatefulActor<VmssManagerActor.ActorState>, IVmssManagerActor, IRemindable { public const string CheckProvision = "CheckProvision"; /// <summary> /// Cluster Configuration Store /// </summary> protected IMessageClusterConfigurationStore ClusterConfigStore { get; private set; } public VmssManagerActor(IMessageClusterConfigurationStore clusterProvider) { ClusterConfigStore = clusterProvider; }

Si crees que esto es útil y quieres que lo coloque en un paquete nuget, avala esta respuesta.

Una nota sobre la implementación, cada actor tendrá su propio alcance. Esto significa que todas las dependencias registradas con ''HierarchicalLifetimeManager'' que implementa IDisposable se eliminarán automáticamente en el actor OnDeactivationAsync . Esto se hizo mediante un proxy dinámico de la clase de actor con un tipo dinámico que intercepta la llamada a OnDeactivationAsync . Para que esto funcione el actor debe ser público definido.

IActorDeactivationInterception.cs

namespace SInnovations.Azure.ServiceFabric.Unity.Abstraction { /// <summary> /// The <see cref="IActorDeactivationInterception"/> interface for defining an OnDeactivateInterception /// </summary> public interface IActorDeactivationInterception { void Intercept(); } }

ActorProxyTypeFactory.cs

namespace SInnovations.Azure.ServiceFabric.Unity.Actors { using System; using System.Linq; using System.Reflection; using System.Reflection.Emit; using SInnovations.Azure.ServiceFabric.Unity.Abstraction; public class ActorProxyTypeFactory { /// <summary> /// Creates a new instance of the <see cref="ActorProxyTypeFactory"/> class. /// </summary> /// <param name="target"></param> public ActorProxyTypeFactory(Type target) { this.target = target; } /// <summary> /// Creates the proxy registered with specific interceptor. /// </summary> /// <returns></returns> public static T Create<T>(IActorDeactivationInterception deactivation, params object[] args) { return (T)new ActorProxyTypeFactory(typeof(T)).Create(new object[] { deactivation }.Concat(args).ToArray()); } public static Type CreateType<T>() { return new ActorProxyTypeFactory(typeof(T)).CreateType(); } /// <summary> /// Creates the proxy registered with specific interceptor. /// </summary> /// <returns></returns> public object Create(object[] args) { BuidAssembly(); BuildType(); InterceptAllMethods(); Type proxy = this.typeBuilder.CreateType(); return Activator.CreateInstance(proxy, args); } public Type CreateType() { BuidAssembly(); BuildType(); InterceptAllMethods(); Type proxy = this.typeBuilder.CreateType(); return proxy; // return Activator.CreateInstance(proxy, args); } /// <summary> /// Builds a dynamic assembly with <see cref="AssemblyBuilderAccess.RunAndSave"/> mode. /// </summary> /// <returns></returns> public void BuidAssembly() { AssemblyName assemblyName = new AssemblyName("BasicProxy"); AssemblyBuilder createdAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); // define module this.moduleBuilder = createdAssembly.DefineDynamicModule(assemblyName.Name); } public void BuildType() { if (!target.IsPublic) { throw new ArgumentException("Actors have to be public defined to proxy them"); } this.typeBuilder = this.moduleBuilder.DefineType(target.FullName + "Proxy", TypeAttributes.Class | TypeAttributes.Public, target); this.fldInterceptor = this.typeBuilder.DefineField("interceptor", typeof(IActorDeactivationInterception), FieldAttributes.Private); foreach (var constructor in target.GetConstructors()) { // Type[] parameters = new Type[1]; ParameterInfo[] parameterInfos = constructor.GetParameters(); Type[] parameters = new Type[parameterInfos.Length + 1]; parameters[0] = typeof(IActorDeactivationInterception); for (int index = 1; index <= parameterInfos.Length; index++) { parameters[index] = parameterInfos[index - 1].ParameterType; } ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, parameters); for (int argumentIndex = 0; argumentIndex < parameters.Length; argumentIndex++) constructorBuilder.DefineParameter( argumentIndex + 1, ParameterAttributes.None, $"arg{argumentIndex}"); ILGenerator generator = constructorBuilder.GetILGenerator(); generator.Emit(OpCodes.Ldarg_0); for (int index = 1; index < parameters.Length; index++) { generator.Emit(OpCodes.Ldarg, index + 1); } generator.Emit(OpCodes.Call, constructor); generator.Emit(OpCodes.Ldarg_0); generator.Emit(OpCodes.Ldarg_1); generator.Emit(OpCodes.Stfld, fldInterceptor); generator.Emit(OpCodes.Ret); } } /// <summary> /// Builds a type in the dynamic assembly, if already the type is not created. /// </summary> /// <returns></returns> public void InterceptAllMethods() { const MethodAttributes targetMethodAttributes = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig; var methodInfo = target.GetMethod("OnDeactivateAsync", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy); { if (methodInfo.IsVirtual) { Type[] paramTypes = GetParameterTypes(methodInfo.GetParameters()); MethodBuilder methodBuilder = typeBuilder.DefineMethod(methodInfo.Name, targetMethodAttributes, methodInfo.ReturnType, paramTypes); ILGenerator ilGenerator = methodBuilder.GetILGenerator(); ilGenerator.Emit(OpCodes.Ldarg_0); ilGenerator.Emit(OpCodes.Ldfld, fldInterceptor); ilGenerator.Emit(OpCodes.Call, typeof(IActorDeactivationInterception).GetMethod("Intercept")); ilGenerator.Emit(OpCodes.Ldarg_0); ilGenerator.Emit(OpCodes.Call, methodInfo); ilGenerator.Emit(OpCodes.Ret); return; } } } private Type[] GetParameterTypes(ParameterInfo[] parameterInfos) { Type[] parameters = new Type[parameterInfos.Length]; int index = 0; foreach (var parameterInfo in parameterInfos) { parameters[index++] = parameterInfo.ParameterType; } return parameters; } private TypeBuilder typeBuilder; private ModuleBuilder moduleBuilder; private readonly Type target; private FieldInfo fldInterceptor; } }

OnActorDeactivateInterceptor.cs

namespace SInnovations.Azure.ServiceFabric.Unity.Actors { using Microsoft.Practices.Unity; using SInnovations.Azure.ServiceFabric.Unity.Abstraction; public class OnActorDeactivateInterceptor : IActorDeactivationInterception { private readonly IUnityContainer container; public OnActorDeactivateInterceptor(IUnityContainer container) { this.container = container; } public void Intercept() { this.container.Dispose(); } } }

UnityFabricExtensions.cs

namespace SInnovations.Azure.ServiceFabric.Unity { using System; using System.Fabric; using Microsoft.Practices.Unity; using Microsoft.ServiceFabric.Actors; using SInnovations.Azure.ServiceFabric.Unity.Abstraction; using SInnovations.Azure.ServiceFabric.Unity.Actors; public static class UnityFabricExtensions { public static IUnityContainer WithFabricContainer(this IUnityContainer container) { return container.WithFabricContainer(c => FabricRuntime.Create()); } public static IUnityContainer WithFabricContainer(this IUnityContainer container, Func<IUnityContainer,FabricRuntime> factory) { container.RegisterType<FabricRuntime>(new ContainerControlledLifetimeManager(), new InjectionFactory(factory)); return container; } public static IUnityContainer WithActor<TActor>(this IUnityContainer container) where TActor : ActorBase { if (!container.IsRegistered<IActorDeactivationInterception>()) { container.RegisterType<IActorDeactivationInterception, OnActorDeactivateInterceptor>(new HierarchicalLifetimeManager()); } container.RegisterType(typeof(TActor), ActorProxyTypeFactory.CreateType<TActor>(),new HierarchicalLifetimeManager()); container.Resolve<FabricRuntime>().RegisterActorFactory(() => { try { var actor = container.CreateChildContainer().Resolve<TActor>(); return actor; } catch (Exception ex) { throw; } }); return container; } public static IUnityContainer WithStatelessFactory<TFactory>(this IUnityContainer container, string serviceTypeName) where TFactory : IStatelessServiceFactory { if (!container.IsRegistered<TFactory>()) { container.RegisterType<TFactory>(new ContainerControlledLifetimeManager()); } container.Resolve<FabricRuntime>().RegisterStatelessServiceFactory(serviceTypeName, container.Resolve<TFactory>()); return container; } public static IUnityContainer WithStatefulFactory<TFactory>(this IUnityContainer container, string serviceTypeName) where TFactory : IStatefulServiceFactory { if (!container.IsRegistered<TFactory>()) { container.RegisterType<TFactory>(new ContainerControlledLifetimeManager()); } container.Resolve<FabricRuntime>().RegisterStatefulServiceFactory(serviceTypeName, container.Resolve<TFactory>()); return container; } public static IUnityContainer WithService<TService>(this IUnityContainer container, string serviceTypeName) { container.Resolve<FabricRuntime>().RegisterServiceType(serviceTypeName, typeof(TService)); return container; } } }


¿Por qué no solo usar un campo de elemento raíz en actor y resolverlo desde un contenedor con dependencias inyectadas en el constructor de Actor? Si esta es una mala decisión, explique por qué:

public class StatelessActor2 : Actor, IStatelessActor2 { private ConfiguredContainer _container; private IRootElement _rootElement; public StatelessActor2() { _container = new ConfiguredContainer(); //... container is configured in it''s constructor _rootElement = _container.Resolve<IRootElement>(); } public async Task<string> DoWorkAsync() { // Working with a RootElement with all dependencies are injected.. return await Task.FromResult(_rootElement.WorkingWithInjectedStuff()); } }


Después de haber investigado un poco en esta área con dotPeek hace un tiempo (tratando de resolver actores desde un alcance de por vida de Autofac por invocación), creo que el truco es crear su propia implementación de StatelessActorServiceFactory y su propio método de extensión Para registrar el actor con él. Aunque la clase de fábrica está marcada como interna, su interfaz (IStatelessServiceFactory) y el tipo de servicio que crea (StatelessActorServiceInstance) son públicos. Desafortunadamente, no parece que StatelessActorServiceInstance haya sido diseñado para ser extensible (espero que esto sea solo un descuido).

Desafortunadamente, parece que WcfActorCommunicationProvider también está marcado como interno, por lo que prácticamente tendrás que crear tu propia canalización desde cero:

  1. Implementa tu propio IStatelessServiceFactory
  2. Implementar su propia IStatelessServiceInstance, IActorService
  3. Implementa tu propio IActorCommunicationProvider
  4. Implementa tu propio IActorHost

Realmente ya no parece valer la pena el esfuerzo, ¿verdad? : - /

Ahí es donde me rendí por ahora. No creo que valga la pena intentar su propio rollo por ahora, dada la relativa inmadurez de la API pública, ya que si este tipo de funcionalidad se muestra, probablemente lo harán de una manera que se rompa cualquier cosa tu implementas tu mismo.


Sé que esto es antiguo, pero por el bien de la documentación, la DI ahora se admite en el marco del actor confiable, como es de esperar.

public class ActorOne : Actor<MyActorState>, IMyActor{ private readonly IDependency _dependency; public ActorOne(IDependency dependency) { _dependency = dependency; }}

Y luego registra el Actor con su dependencia con el Service Fabric de la siguiente manera:

using (FabricRuntime fRuntime = FabricRuntime.Create()){ fRuntime.RegisterActor(() => new ActorOne(new MyDependency()); Thread.Sleep(Timeout.Infinite);}


Si está utilizando Autofac, tienen un paquete de integración específico para eso:

https://alexmg.com/introducing-the-autofac-integration-for-service-fabric/ https://www.nuget.org/packages/Autofac.ServiceFabric/

En resumen, el registro se realiza utilizando ActorRuntime.RegisterActorAsync / ServiceRuntime.RegisterServiceAsync como se espera. Sin embargo, la parte más problemática, a saber, la liberación de objetos, se maneja automáticamente en las OnDeactivateAsync / OnCloseAsync / OnAbort utilizando un proxy dinámico. También se mantiene el alcance adecuado de por vida.

En el momento de escribir esto todavía está en Alpha (recién lanzado el mes pasado).