Service Fabric with Generic Services

安稳与你 提交于 2019-11-30 21:30:58

Services in Service Fabric can implement generic interfaces:

interface IMyService<T>
{
    T Foo();
}

class Stateful1 : StatefulService, IMyService<string>
{
    public Stateful1(StatefulServiceContext context)
        : base(context)
    { }

    public string Foo()
    {
        // do foo
    }
}

This is fine.

What isn't supported is generic interfaces for Remote Procedure Call (RPC). This is specific to the Service Remoting communication stack, which is what you have with IService and the Remoting Communication Listener.

So in your case, no, generics are not yet supported. But this is a limitation of that specific service communication stack, not of services in general, and of course you can use any communication stack you want.

Service Fabric is using this extension methods to remove non service type interfaces

internal static class ServiceTypeExtensions
{
    public static Type[] GetServiceInterfaces(this Type serviceType)
    {
        List<Type> typeList = new List<Type>(((IEnumerable<Type>)serviceType.GetInterfaces()).Where(t => typeof(IService).IsAssignableFrom(t)));

        typeList.RemoveAll(t => t.GetNonServiceParentType() != null);

        return typeList.ToArray();
    }

    internal static Type GetNonServiceParentType(this Type type)
    {
        List<Type> typeList = new List<Type>(type.GetInterfaces());
        if (typeList.RemoveAll(t => t == typeof(IService)) == 0)
            return type;
        foreach (Type type1 in typeList)
        {
            Type serviceParentType = type1.GetNonServiceParentType();
            if (serviceParentType != null)
                return serviceParentType;
        }
        return null;
    }
}

and checks for the result

if (serviceInterfaces.Length == 0 && !serviceType.IsAbstract)
    throws the exception in question

So I found a workaround for this case

public interface IServiceWrapper : IService
{
}

public interface INinjaService : IServiceWrapper, IFooService<SuperNinja>
{
}

UPDATED

After some investigation I found that proxy is checking that all interface parents need to be IService like, which means

  Type serviceParentType = serviceInterfaceType.GetNonServiceParentType();
  if (serviceParentType != null)
     throws another exception

So you need to make IFooService<T> derived from IService which is not supported at the moment.

So generics are not supported :(

It might be late for this answer but it might help someone. I solved this by making sure that each interface implemented in the service class inherits from IService. In you case, you'd have the following:

public interface IFooService<T> : IService
{
   Task<T> Get(int id);
}
public interface INinjaService : IFooService<SuperNinja>
{

}
public class NinjaService : StatelessService, INinjaService
{
}

Try that, it should work. In case you need to implement more than one interface in your service, I have tried the following to options:

public interface MyInterfaceA : IService { }
public interface MyInterfaceB : IService { }
public class MyServiceAB : StatelessService, MyInterfaceA, MyInterfaceB
{
}

or

public interface MyInterfaceA : IService { }
public interface MyInterfaceB : IService { }
public interface MyInterfaceC : MyInterfaceA, MyInterfaaceB { }
public class MyServiceC : StatelessService, MyInterfaceC
{
}

Hope that helps.

Since I wanted to do a call from one Service to another Service the workaround I did was passing a string with the Type name and with reflection call the other method with it.

    public async Task<Result> GetResultAsync(Site site, string indexableType, SearchQuery searchQuery)
    {
        using (var provider = new Provider(Logger))
        {
            var indexableT = typeof(IIndexable).Assembly
                                        .GetTypes()
                                        .FirstOrDefault(t => typeof(IIndexable).IsAssignableFrom(t) && t.Name == indexableType);


            if (indexableT != null)
            {
                var generic = provider
                                .GetType()
                                .GetMethod("METHOD_NAME_IN_PROVIDER"))
                                .MakeGenericMethod(indexableT);                    

                //This is the same as calling: await provider.METHOD_NAME_IN_PROVIDER<T>(site, searchQuery);
                return await (Task<Result>) generic.Invoke(provider, new object[] { site, searchQuery });
            }

            return null;
        }
    }

Hope it helps someone.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!