I have an Orleans applications with the following structure:
public interface IGraintest : Orleans.IGrainWithGuidCompoundKey
{
Task Init();
}
public abstract class GraintestImpl<T> : Grain, IGraintest, Deserializer<T>
{
string streamName;
public Task Init()
{
return Task.CompletedTask;
}
public override async Task OnActivateAsync()
{
var primaryKey = this.GetPrimaryKey(out streamName);
var streamProvider = GetStreamProvider("SMSProvider");
var stream = streamProvider.GetStream<String>(primaryKey, streamName);
// To resume stream in case of stream deactivation
var subscriptionHandles = await stream.GetAllSubscriptionHandles();
if (subscriptionHandles.Count > 0)
{
foreach (var subscriptionHandle in subscriptionHandles)
{
await subscriptionHandle.ResumeAsync(OnNextMessage);
}
}
await stream.SubscribeAsync(OnNextMessage);
}
public abstract T Process(string l);
private Task OnNextMessage(string message, StreamSequenceToken sequenceToken)
{
T obj = Process(message);
//gonna do something with obj here
return Task.CompletedTask;
}
}
public class ProcessImplA: GraintestImpl<Car>
{
public override Car Process(string l)
{
return new Car(l);
}
}
public class ProcessImplB: GraintestImpl<Boat>
{
public override Boat Process(string l)
{
return new Boat(l);
}
}
Here I have a grain that I use to read messages from a stream and apply some operation to them. Since I have different object types I want to work with I created an abstract class to implement the interface. The problem lies here:
var sourceOne = client.GetGrain<IGraintest>(guid, "Car");
var sourceTwo = client.GetGrain<IGraintest>(guid, "Boat");
When I run the program like this I get the error code:
Exception while trying to run client: Cannot resolve grain interface ID=-<blabla> to a grain class because of multiple implementations of it
So my question is, can I do a minor change to make this work or do I have to create a grain interface for each ProcessImpl grain that I want to utilize?
You can disambiguate the GetGrain
call by using an overload which accepts a grain class name prefix.
var sourceOne = client.GetGrain<IGraintest>(guid, "Car", grainClassNamePrefix: "MyNamespace.ProcessImplA");
var sourceTwo = client.GetGrain<IGraintest>(guid, "Boat", grainClassNamePrefix: "MyNamespace.ProcessImplB");
Otherwise, if there are two implementations of the interface then the runtime does not know how to decide which one to use. Importantly for your case, the information about which class implements which constructed generic interface is not known to the IGrainFactory
implementation, so it is not able to pick an implementation.
Another approach is to add a marker interface to your grain classes, for example, you could have IGrainTestImplBoat
:
public interface IGrainTestImplBoat : Orleans.IGrainWithGuidCompoundKey { }
public class ProcessImplB : GraintestImpl<Boat>, IGrainTestImplBoat { /* ... */ }
var sourceTwo = client.GetGrain<IGrainTestImplBoat>(guid, "Boat");