Service Fabric与通用服务

5
我希望您能提供一种通用类型的服务,例如 -
public interface IFooService<T> 
{
   Task<T> Get(int id);
}

然而,Service Fabric 不允许泛型类或泛型方法。我也尝试过类似以下的内容:
public interface INinjaService : IService, IFooService<SuperNinja>
{


}

但它不能识别继承的接口说明。

服务类型“Omni.Fabric.Services.NinjaService”未实现任何服务接口。服务接口是从“Microsoft.ServiceFabric.Services.Remoting.IService”类型派生的接口。

我似乎在Service Fabric文档或stackoverflow上找不到泛型的参考。可能这还太新了,或者我正在走错路。是否有人成功实现过这种模式?能做到吗?应该这样做吗?

NinjaService,如您所请求的

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

消耗代码(从一个 Owin WebApi 服务中调用)

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

服务类型“Omni.Fabric.Services.NinjaService”未实现任何服务接口。服务接口是从“Microsoft.ServiceFabric.Services.Remoting.IService”类型派生的接口。 - Tony
我知道我需要理解的异常是什么时候抛出的。 - Mike
我在我的电脑上创建了类似的示例,并且已经成功发布到集群中,你使用的是哪个版本的 .net? - Mike
我正在使用4.5.2版本。 - Mike
无法工作,它只是将错误移动到尝试调用该服务端点时。我认为这是因为您删除了服务侦听器的创建。 - Tony
显示剩余5条评论
4个回答

7

Service Fabric中的服务可以实现通用接口:

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

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

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

这很好。

不支持通用接口进行远程过程调用(RPC)。这是特定于服务重定向通信堆栈的,这就是您使用IService和Remoting Communication Listener所具有的内容。

因此,在您的情况下,不,泛型尚未得到支持。但是,这是该特定服务通信堆栈的限制,而不是服务一般的限制,当然您可以使用任何通信堆栈


1
我接受了你的答案,因为它是正确的。尽管我不喜欢它 - 哈哈。根据你的回答,我得到的印象是泛型可能是将来可能实现的东西? - Tony
好的!这是我们将来可能支持的内容,但我目前没有具体的日期或细节。 - Vaclav Turecek

1
Service Fabric正在使用这个扩展方法来删除非服务类型的接口。
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;
    }
}

并检查结果。
if (serviceInterfaces.Length == 0 && !serviceType.IsAbstract)
    throws the exception in question

所以我为这种情况找到了一个解决方法

public interface IServiceWrapper : IService
{
}

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

已更新

经过一些调查,我发现代理正在检查所有接口父级是否需要像IService一样,这意味着

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

所以你需要让 IFooService<T> 派生自 IService,但目前不支持这样做。

所以泛型目前不被支持 :(


尝试过这个,但当我尝试使用服务器进行消费时仍然无法工作,例如INinjaService service = ServiceProxy.Create<INinjaService>(... - Tony

1

虽然这个答案可能有点晚,但它可能会帮助到某些人。我通过确保服务类中实现的每个接口都继承自IService来解决了这个问题。在您的情况下,您将拥有以下内容:

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

}
public class NinjaService : StatelessService, INinjaService
{
}

尝试一下,应该能够正常工作。 如果您的服务需要实现多个接口,我已经尝试了以下两个选项:

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

或者

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

希望这有所帮助。

1
这不起作用,仍然将其视为通用接口。 - Cpt Slow

0

由于我想从一个服务调用另一个服务,所以我做的解决方法是传递一个带有类型名称的字符串,并使用反射调用另一个方法。

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

希望能对某些人有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接