Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve support for generic grains with generic state #2715

Merged
merged 1 commit into from
Feb 10, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion src/OrleansRuntime/GrainTypeManager/GenericGrainTypeData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Orleans.Runtime
[Serializable]
internal class GenericGrainTypeData : GrainTypeData
{
private static readonly Type GenericGrainStateType = typeof(Grain<>);
private readonly Type activationType;
private readonly Type stateObjectType;

Expand All @@ -23,9 +24,26 @@ public GrainTypeData MakeGenericType(Type[] typeArgs)
{
// Need to make a non-generic instance of the class to access the static data field. The field itself is independent of the instantiated type.
var concreteActivationType = activationType.MakeGenericType(typeArgs);
var concreteStateObjectType = (stateObjectType != null && stateObjectType.GetTypeInfo().IsGenericType) ? stateObjectType.GetGenericTypeDefinition().MakeGenericType(typeArgs) : stateObjectType;
var typeInfo = this.stateObjectType?.GetTypeInfo();
var concreteStateObjectType = typeInfo != null && (typeInfo.IsGenericType || typeInfo.IsGenericParameter)
? GetGrainStateType(concreteActivationType.GetTypeInfo())
: this.stateObjectType;

return new GrainTypeData(concreteActivationType, concreteStateObjectType);
}

private static Type GetGrainStateType(TypeInfo grainType)
{
while (true)
{
if (grainType == null) return null;
if (grainType.IsGenericType && grainType.GetGenericTypeDefinition() == GenericGrainStateType)
{
return grainType.GetGenericArguments()[0];
}

grainType = grainType.BaseType?.GetTypeInfo();
}
}
}
}
15 changes: 13 additions & 2 deletions test/DefaultCluster.Tests/GenericGrainTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -743,10 +743,21 @@ public async Task Generic_CastGenericInterfaceToNonGenericInterfaceBeforeActivat

Assert.Equal(result, "Hello!");
}

/// <summary>
/// Tests that generic grains can have generic state and that the parameters to the Grain{TState}
/// class do not have to match the parameters to the grain class itself.
/// </summary>
/// <returns></returns>
[Fact, TestCategory("BVT"), TestCategory("Generics")]
public async Task GenericGrainStateParameterMismatchTest()
{
var grain = this.GrainFactory.GetGrain<IGenericGrainWithGenericState<int, List<Guid>, string>>(Guid.NewGuid());
var result = await grain.GetStateType();
Assert.Equal(typeof(List<Guid>), result);
}
}



namespace Generic.EdgeCases
{
using UnitTests.GrainInterfaces.Generic.EdgeCases;
Expand Down
11 changes: 11 additions & 0 deletions test/TestGrainInterfaces/IGenericInterfaces.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@

namespace UnitTests.GrainInterfaces
{
public interface IGenericGrainWithGenericState<TFirstTypeParam, TStateType, TLastTypeParam> : IGrainWithGuidKey
{
Task<Type> GetStateType();
}

public class GenericGrainWithGenericState<TFirstTypeParam, TStateType, TLastTypeParam> : Grain<TStateType>,
IGenericGrainWithGenericState<TFirstTypeParam, TStateType, TLastTypeParam> where TStateType : new()
{
public Task<Type> GetStateType() => Task.FromResult(this.State.GetType());
}

public interface IGenericGrain<T, U> : IGrainWithIntegerKey
{
Task SetT(T a);
Expand Down