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

Replace static codegen'd factory classes with generic factories #59

Closed
ReubenBond opened this issue Jan 31, 2015 · 10 comments · Fixed by #74
Closed

Replace static codegen'd factory classes with generic factories #59

ReubenBond opened this issue Jan 31, 2015 · 10 comments · Fixed by #74

Comments

@ReubenBond
Copy link
Member

Currently, we are exposing codegen'd factory classes for IGrainObservers (there may be others, please add them here).

We should create a generic factory which provides access to the codegen'd factories and hide the concrete implementation.

This will help us with Dependency Injection and give us the flexibility to use runtime codegen (eg, via Roslyn)

@sergeybykov
Copy link
Contributor

@ReubenBond - Do you mean to add GrainFactory.CreateObjectReference()?

@ReubenBond
Copy link
Member Author

I was thinking of "ObserverFactory" or "ClientFactory" but yes, pretty much.

-----Original Message-----
From: "Sergey Bykov" notifications@github.com
Sent: ‎02/‎02/‎2015 06:40
To: "dotnet/orleans" orleans@noreply.github.com
Cc: "Reuben Bond" reuben.bond@gmail.com
Subject: Re: [orleans] Replace static codegen'd factory classes with genericfactories (#59)

@ReubenBond - Do you mean to add GrainFactory.CreateObjectReference()?

Reply to this email directly or view it on GitHub.=

@sergeybykov
Copy link
Contributor

My personal preference would be to stick to one static factory class, GrainFactory, for better discoverability.

@ReubenBond
Copy link
Member Author

The proposal is to add

public static Task<TGrainObserverInterface> GrainFactory.CreateObjectReference<TGrainObserverInterface>(TGrainObserverInterFace obj);

As well as the matching DeleteObjectReference method.

LGTYou?

@ReubenBond
Copy link
Member Author

What should the behavior be when a user calls GrainFactory.CreateObjectReference(observer) where observer is a concrete type (i.e, not an interface).

Currently it will crash at runtime when it is unable to find the concrete type's factory.

I think the best approach is to force the user to specify the interface in the type parameters, which equates to changing the signature to:

CreateObjectReference<TGrainObserverInterface>(IGrainObserver obj)
    : where TGrainObserverInterface : IGrainObserver

Usage then becomes:

var obj = await GrainFactory.CreateObjectReference<ISimpleGrainObserver>(observer);

I'm performing a runtime check that the argument matches the type parameter:

if (!interfaceType.IsInstanceOfType(obj))
{
    throw new ArgumentException(
        string.Format("The provided object must implement '{0}'.", interfaceType.FullName),
        "obj");
}

What do you think?

@jkonecki
Copy link
Contributor

jkonecki commented Feb 2, 2015

Do you really need to perform the check at runtime? The compiler won't
allow passing of the object that violates generic parameter restrictions...

On Sun, 1 Feb 2015 22:41 Reuben Bond notifications@github.com wrote:

What should the behavior be when a user calls
GrainFactory.CreateObjectReference(observer) where observer is a concrete
type (i.e, not an interface).

Currently it will crash at runtime when it is unable to find the concrete
type's factory.

I think the best approach is to force the user to specify the interface in
the type parameters, which equates to changing the signature to:

CreateObjectReference(IGrainObserver obj)
: where TGrainObserverInterface : IGrainObserver

Usage then becomes:

var obj = await GrainFactory.CreateObjectReference(observer);

I'm performing a runtime check that the argument matches the type
parameter:

if (!obj.GetType().IsAssignableFrom(interfaceType))
{
throw new ArgumentException(
string.Format("The provided object must implement '{0}'.", interfaceType.FullName),
"obj");
}

What do you think?


Reply to this email directly or view it on GitHub
#59 (comment).

@ReubenBond
Copy link
Member Author

I can't have generic parameter restrictions to say T is an interface, though. The compiler will infer the most specific type possible, which will be the concrete type.

To make it less error prone, I changed the type in the parameter list to IGrainObserver rather than TGrainObserverInterface - that forces the consumer to specify the interface type (which I can only check at runtime).

Maybe I'm missing something, though. Do you have a suggestion for how I can improve this?

@veikkoeeva
Copy link
Contributor

@sergeybykov
Copy link
Contributor

The compile will already enforce
where TGrainObserverInterface : IGrainObserver

At run time, don't we just need to check that it's an interface via
obj.GetType().IsInterface
?

@ReubenBond
Copy link
Member Author

We do a couple of sanity checks:

  1. Is TGrainObserverInterface an interface: the compiler enforces that it implements IGrainObserver as you said, @sergeybykov.
  2. Is obj an instance of TGrainObserverInterface: the compiler only check that it implements IGrainObserver, but not its relationship to TGrainObserverInterface.

The type system cannot infer the interface from the concrete type, so the user must explicitly provide it. We cannot specify a relationship between obj and TGrainObserverInterface while forcing the user to specify TGrainObserverInterface unless we also force them to explicitly specify the type of obj.

i.e:

// Bad: TInterface is concrete type
Task<TInterface> CreateObjectReference<TInterface>(TInterface obj) where TInterface : IGrainObserver

// Correct, but the user has to specify both types explicitly
Task<TInterface> CreateObjectReference<TInterface,TConcrete>(TConcrete obj) where TInterface : IGrainObserver where TConcrete : TInterface

// Only runtime checks can verify relationship between `obj` and TInterface
Task<TInterface> CreateObjectReference<TInterface>(IGrainObserver obj) where TInterface : IGrainObserver

ReubenBond pushed a commit to ReubenBond/orleans that referenced this issue Feb 4, 2015
Fixes dotnet#59
Add test which includes legacy observer factory.
Refactored common delegate generation code into separate MakeFactoryDelegate method.
@ghost ghost locked as resolved and limited conversation to collaborators Sep 30, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants