Tony
Tony

Reputation: 1307

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

Upvotes: 5

Views: 3997

Answers (4)

Diogo Luis
Diogo Luis

Reputation: 228

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.

Upvotes: 0

Maxwell Weru
Maxwell Weru

Reputation: 463

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.

Upvotes: 1

Vaclav Turecek
Vaclav Turecek

Reputation: 9050

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.

Upvotes: 7

Mike
Mike

Reputation: 4051

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 :(

Upvotes: 1

Related Questions