简体   繁体   English

Azure 服务结构参与者依赖注入

[英]Azure service fabric actor dependency injection

有什么方法可以将依赖项注入 Azure Service Fabric Actor 的构造函数?

Updated更新

Its all on github and myget now: https://github.com/s-innovations/S-Innovations.ServiceFabric.Unity现在都在 github 和 myget 上: https : //github.com/s-innovations/S-Innovations.ServiceFabric.Unity

and integrates with aspnet core dependency injection without to much hassle, check the examples of the readme.md并与 aspnet 核心依赖注入集成,没有太多麻烦,请查看 readme.md 的示例


I am a long time user of Unity and decided to make the core extension methods needed to have a nice dependency injection experience when working with actors.我是 Unity 的长期用户,并决定制作核心扩展方法,以便在与 Actor 一起工作时获得良好的依赖注入体验。

My program.cs now looks like this:我的 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;
        }
    }
}

where I in actors and services can just put in my dependencies in the constructors.我在演员和服务中的位置可以在构造函数中放入我的依赖项。

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

If you feel this is useful and would like me to put it into a nuget package, upvote this answer.如果您觉得这很有用,并希望我将其放入 nuget 包中,请对此答案点赞。

One note about the implementation, each actor will get its own scope.关于实现的一个说明,每个参与者都将获得自己的范围。 This means that all dependencies registered with 'HierarchicalLifetimeManager' that implements IDisposable will automaticly get disposed in the actor OnDeactivationAsync .这意味着所有向实现IDisposable “HierarchicalLifetimeManager”注册的依赖项将自动在 actor OnDeactivationAsync This was done by dynamicly proxying the actor class with a dynamic type that intercepts the call to OnDeactivationAsync .这是通过使用拦截对OnDeactivationAsync调用的动态类型动态代理actor 类来完成的。 For this to work the Actor must be public defined.为此,Actor 必须是公共定义的。

IActorDeactivationInterception.cs 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 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 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 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;
        }
    }
}

I know this is old but for documentations' sake DI is now supported in the Reliable Actor framework just like you would expect.我知道这已经过时了,但出于文档的考虑,现在 Reliable Actor 框架中支持 DI,就像您期望的那样。

public class ActorOne : Actor<MyActorState>, IMyActor{

private readonly IDependency _dependency;

public ActorOne(IDependency dependency)
{
    _dependency = dependency;
}}

And then you register the Actor with its dependency with the Service Fabric like this:然后将 Actor 及其依赖项注册到 Service Fabric 中,如下所示:

using (FabricRuntime fRuntime = FabricRuntime.Create()){

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

Having had a bit of a dig-around in this area with dotPeek a while back (trying to resolve actors from an Autofac lifetime scope per-invocation), I think the trick is to create your own implementation of StatelessActorServiceFactory, and your own extension method to register the actor with it.一段时间前,在使用 dotPeek 对这个领域进行了一些挖掘(尝试从每次调用的 Autofac 生命周期范围解析参与者),我认为诀窍是创建自己的 StatelessActorServiceFactory 实现和自己的扩展方法用它注册演员。 Although the factory class is marked as internal, its interface (IStatelessServiceFactory) and the service type it creates (StatelessActorServiceInstance) are both public.尽管工厂类被标记为内部,但它的接口(IStatelessServiceFactory)和它创建的服务类型(StatelessActorServiceInstance)都是公开的。 Unfortunately, it doesn't look like StatelessActorServiceInstance was designed to be extensible (I'm hoping this is just an oversight).不幸的是,它看起来不像 StatelessActorServiceInstance 被设计为可扩展的(我希望这只是一个疏忽)。

Unfortunately, it looks like WcfActorCommunicationProvider is also marked as internal so you'll pretty much have to create your own pipeline from scratch:不幸的是,看起来 WcfActorCommunicationProvider 也被标记为内部,因此您几乎必须从头开始创建自己的管道:

  1. Implement your own IStatelessServiceFactory实现你自己的 IStatelessServiceFactory
  2. Implement your own IStatelessServiceInstance, IActorService实现自己的 IStatelessServiceInstance、IActorService
  3. Implement your own IActorCommunicationProvider实现你自己的 IActorCommunicationProvider
  4. Implement your own IActorHost实现你自己的 IActorHost

Doesn't really seem worth the effort anymore, does it?看起来真的不再值得付出努力了,是吗? :-/ :-/

That's where I gave up for now.这就是我暂时放弃的地方。 I don't think it's worth trying to roll-your-own for now given the relative immaturity of the public API, since if this sort of functionality will show up at all, they'll probably do so in a way that'll break anything your implement yourself.鉴于公共 API 的相对不成熟,我认为现在不值得尝试自己动手,因为如果这种功能会出现,他们可能会以一种会破坏的方式这样做任何你自己实现的东西。

Why not just use some root element field in actor, and resolve it from container with injected dependencies in Actor's constructor?为什么不直接在 actor 中使用一些根元素字段,并在 Actor 的构造函数中使用注入依赖项的容器来解析它? If this is a bad decision, please explain why:如果这是一个错误的决定,请解释原因:

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

If you're using Autofac, they have a specific integration package for that:如果您使用 Autofac,他们有一个特定的集成包:

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

In short, registration is performed using ActorRuntime.RegisterActorAsync / ServiceRuntime.RegisterServiceAsync as you would expect.简而言之,如您所料,注册是使用ActorRuntime.RegisterActorAsync / ServiceRuntime.RegisterServiceAsync执行的。 However the more problematic part, namely object release, is automatically handled in the OnDeactivateAsync / OnCloseAsync / OnAbort overrides using a dynamic proxy.然而,更成问题的部分,即对象释放,在OnDeactivateAsync / OnCloseAsync / OnAbort覆盖中使用动态代理自动处理。 Proper lifetime scoped is maintained as well.适当的生命周期范围也得到维护。

At the time of writing it's still in Alpha though (just released last month).在撰写本文时,它仍处于 Alpha 阶段(上个月刚刚发布)。

@abatishchev I think you are referring to the Service-Locator antipattern. @abatishchev 我认为您指的是 Service-Locator 反模式。 Both Dependency Injection and Service-Locator are variations of Inversion of Control.依赖注入和服务定位器都是控制反转的变体。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM