【问题标题】:Azure service fabric actor dependency injectionAzure 服务结构参与者依赖注入
【发布时间】:2015-08-03 18:33:06
【问题描述】:

有没有办法将依赖项注入到 Azure Service Fabric Actor 的构造函数中?

【问题讨论】:

    标签: c# dependency-injection azure-service-fabric actor


    【解决方案1】:

    更新

    现在全部在 github 和 myget 上:https://github.com/s-innovations/S-Innovations.ServiceFabric.Unity

    并与 aspnet 核心依赖注入集成,没有太多麻烦,请查看 readme.md 的示例


    我是 Unity 的长期用户,并决定制作核心扩展方法,以便在使用 Actor 时获得良好的依赖注入体验。

    我的 program.cs 现在看起来像这样:

    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;
            }
        }
    }
    

    我在演员和服务中的位置可以将我的依赖项放入构造函数中。

    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;
        }
    

    如果您觉得这很有用并希望我将其放入 nuget 包中,请支持此答案。

    关于实现的注意事项,每个参与者都会有自己的范围。这意味着所有使用“HierarchicalLifetimeManager”注册的实现IDisposable 的依赖项将自动在actorOnDeactivationAsync 中处理。这是通过使用拦截对OnDeactivationAsync 的调用的动态类型动态代理actor 类来完成的。为此,Actor 必须是公共定义的。

    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;
            }
        }
    }
    

    【讨论】:

    • 我实际上已经为最新的 SDK 版本更新了它。我将在接下来的几周内尝试使其在 nuget 中可用。
    • 现在我需要看看你所有的东西。到目前为止,我看到的是高质量的。就像看英国达人秀和帕瓦罗蒂走上舞台
    • 这很优雅,我完全赞成你对这个解决方案的看法,但我认为我们可以同意,不得不求助于这个级别来实现适当的演员停用挂钩是“疯狂的”,对吧? ActorService 需要提供挂钩,以允许进行适当的生命周期跟踪等操作,以便我们可以围绕运行时构建更多服务,从而为我们的 Actor 提供更多功能。
    • @PoulK.Sørensen:好东西!您是否为新的 SDK 重构了这个?
    • @gumaflux 我做了,github.com/s-innovations/…(如果需要,也可以尽快安装 nuget)
    【解决方案2】:

    我知道这是旧的,但为了文档的缘故,DI 现在在 Reliable Actor 框架中得到支持,就像你所期望的那样。

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

    然后您将 Actor 及其依赖项注册到 Service Fabric,如下所示:

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

    【讨论】:

    • 完美! :) 除了RegisterActorFactory&lt;TActor&gt;(Func&lt;TActor&gt; actorFactory)
    • FabricRuntime 类已于 2016 年 3 月 31 日在 GA 中进行服务注册而被弃用。见release notes。现在是ActorRuntime.RegisterActorAsync&lt;ActorOne&gt;((context, actorType) =&gt; new ActorService(context, actorType, () =&gt; new ActorOne(new MyDependency()))) .GetAwaiter().GetResult();
    • 这里不幸的问题是管理生命周期范围。虽然您可以通过提供工厂来参与 Actor 创建,但您无法在 Actor 停用过程中清理其相关生命周期范围的资源。虽然工厂方法是一个好的开始,但我们确实需要更多足够的运行时钩子才能为演员提供服务恕我直言。
    【解决方案3】:

    在不久前使用 dotPeek 在该领域进行了一些研究(尝试从 Autofac 生命周期范围内每次调用解析演员),我认为诀窍是创建您自己的 StatelessActorServiceFactory 实现,以及您的自己的扩展方法来注册actor。虽然工厂类被标记为内部,但它的接口(IStatelessServiceFactory)和它创建的服务类型(StatelessActorServiceInstance)都是公共的。不幸的是,它看起来不像 StatelessActorServiceInstance 被设计为可扩展的(我希望这只是一个疏忽)。

    不幸的是,看起来 WcfActorCommunicationProvider 也被标记为内部,因此您几乎必须从头开始创建自己的管道:

    1. 实现自己的 IStatelessServiceFactory
    2. 实现自己的 IStatelessServiceInstance、IActorService
    3. 实现自己的 IActorCommunicationProvider
    4. 实现您自己的 IActorHost

    似乎不再值得付出努力,是吗? :-/

    这就是我现在放弃的地方。鉴于公共 API 的相对不成熟,我认为现在不值得尝试自己动手,因为如果这种功能完全出现,他们可能会以一种会破坏的方式这样做任何你自己实现的东西。

    【讨论】:

    • 我做了同样的事情,也放弃了。看起来我必须对一堆类进行逆向工程。最终,我创建了一个服务定位器。是的,我知道这是一个 anipattern,但如果你小心点,你可以离开它,毕竟我只是在玩这个 sdk。
    【解决方案4】:

    为什么不在 Actor 中使用一些根元素字段,并在 Actor 的构造函数中通过注入依赖项的容器解析它?如果这是一个错误的决定,请解释原因:

    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());
        }
    }
    

    【讨论】:

    • 因为这被称为控制反转(而不是依赖注入),现在被认为是一种反模式。
    【解决方案5】:

    如果您使用 Autofac,他们有专门的集成包:

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

    简而言之,注册是使用ActorRuntime.RegisterActorAsync / ServiceRuntime.RegisterServiceAsync 进行的,正如您所期望的那样。然而,更成问题的部分,即对象释放,在使用动态代理的 OnDeactivateAsync / OnCloseAsync / OnAbort 覆盖中自动处理。适当的生命周期范围也得到维护。

    在撰写本文时,它仍处于 Alpha 阶段(上个月刚刚发布)。

    【讨论】:

    • 有趣,谢谢。我希望看到的是微软开发的一些“官方”方法,它们基于/使用 MS.Extensions.DI 框架。
    【解决方案6】:

    @abatishchev 我认为您指的是服务定位器反模式。依赖注入和服务定位器都是控制反转的变体。

    https://www.martinfowler.com/articles/injection.html

    【讨论】:

      猜你喜欢
      • 2017-07-09
      • 2011-06-26
      • 1970-01-01
      • 1970-01-01
      • 2020-09-09
      • 2016-06-12
      • 2018-04-11
      • 2017-12-08
      • 2018-10-11
      相关资源
      最近更新 更多