简体   繁体   中英

Service Fabric with Generic Services

I am looking to have a generic type service ie -

public interface IFooService<T> 
{
   Task<T> Get(int id);
}

However, service fabric does not allow generic classes or generic methods. I have also tried something like

public interface INinjaService : IService, IFooService<SuperNinja>
{


}

but it does not pick up inherited interfaces stating

The service type 'Omni.Fabric.Services.NinjaService' does not implement any service interfaces. A service interface is the one that derives from 'Microsoft.ServiceFabric.Services.Remoting.IService' type.

I can't seem to find any reference to generics on Service Fabric Documentation or stackoverflow. Either it is still too new or possibly I am headed down the wrong path. Has anyone had any luck implementing this sort of pattern? Can it be done? Should it be done?

NinjaService as requested

public class NinjaService : StatelessService, INinjaService
{

    public NinjaService(StatelessServiceContext serviceContext) : base(serviceContext)
    {
    }


    protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
    {
            return new[] { new ServiceInstanceListener(context => this.CreateServiceRemotingListener(context)) };
    }


    public Task<SuperNinja> Get(int id)
    {
        return Task.FromResult(new SuperNinja());
    }
}

Consuming Code (called from an Owin WebApi Service

    public async Task<SuperNinja> Get(int key)
    {
        try
        {
            INinjaService service = ServiceProxy.Create<INinjaService>(new Uri("fabric:/Omni.Fabric/Services"));

            var t = await service.Get(key).ConfigureAwait(false);

            return t;
        }
        catch (Exception ex)
        {
            throw;
        }
    }

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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