Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Variance on a method returning ContainerResource or ProjectResource#13078

AnsweredbyFalco20019
Falco20019 asked this question inQ&A
Discussion options

I am currently just playing around with the idea to have an extension returning either the project (for env=development) or a deployed version of some (for env=production).

AddContainer returns anIResourceBuilder<ContainerResource> whereas theAddProject returns aIResourceBuilder<ProjectResource>.

Both share a lot of interfaces likeIResource,IResourceWithEnvironment,IResourceWithEndpoints.

Is there some way using generics to have a method that returnsIResourceBuilder<T> that offers all common interfaces (maybe through constraints or added types/interfaces)? I didn't find any besides returningIResourceBuilder<IResource> and using pattern matching to upcast again, cluttering the code as chaining won't work as nicely.

It might also be possible to write a class that wraps it using constrains, but then I again don't know how to call it as I would need to pass it a type that's part of the inheritance chain.

The idea is that the references between them, stuff like environment variables or endpoints are still the same, no matter the environment.

You must be logged in to vote

I was able to find a light-weight solution that doesn't crash. Just in case someone else has a similar need.

internalinterfaceIProjectOrContainerResource:IResourceWithEnvironment,IResourceWithArgs,IResourceWithEndpoints,IResourceWithWaitSupport,IComputeResource;internalclassProjectOrContainerResourceBuilder<T>(IResourceBuilder<T>builder):IResourceBuilder<IProjectOrContainerResource>whereT:IResourceWithEnvironment,IResourceWithArgs,IResourceWithEndpoints,IResourceWithWaitSupport,IComputeResource{privateIResourceBuilder<T>_builder=builder;publicIResourceBuilder<IProjectOrContainerResource>WithAnnotation<TAnnotation>(TAnnotationannotation,

Replies: 2 comments 6 replies

Comment options

I've also run into this and have had the same observation/reflection when setting defaults for my resources automatically. I usually need a resource object with two or more interfaces mentioned. Right now we have to pattern match for ProjectResource, ContainerResource, etc and duplicate code for them.

Basically I wantif (resource is (IResourceWithEndpoints, IResourceWithEnvironment) res) :)

You must be logged in to vote
4 replies
@davidfowl
Comment options

C# 25 maybe 😬

@Falco20019
Comment options

Thanks@davidfowl, I take this as "not supported by the language" instead of "you just missed some easy to use variance feature" 😁

I really thought I've just overlooked something I could apply to a method that hints the return value to be promised to contain those interfaces on the generic type. Like on the input type. But I assume due to limited scoping, it's a lot easier to have on inputs than on outputs.

@KennethHoff
Comment options

The closest you can get is something like:

IResourceBuilder<T> DoThing<T>(IResourceBuilder<T> builder) where T: IResourceWithEndpoints, IResourceWithEnvironment;
@Falco20019
Comment options

@KennethHoff Thats what I did, but sadly this would only work to ensure the input parameter is already implementing those interfaces and passing through the original type. In my case, I would create the objects inside and therefore wouldn't be able to choose the<T> at the caller side as there is no type that that would match. Also stuff like creating my own interface that inherits all and casting the objects withUnsafe.As was not working and resulted in runtime errors (EntryPointNotFoundException). I assume due to ordering/mapping problems in the virtual table.

The only other options I could come up were either usingIDynamicInterfaceCastable (too cumbersome) or creating a wrapper class for the resource that implements all interfaces and another wrapper for theIResourceBuilder to forward all calls to the original one or the wrapped resource.

Comment options

I was able to find a light-weight solution that doesn't crash. Just in case someone else has a similar need.

internalinterfaceIProjectOrContainerResource:IResourceWithEnvironment,IResourceWithArgs,IResourceWithEndpoints,IResourceWithWaitSupport,IComputeResource;internalclassProjectOrContainerResourceBuilder<T>(IResourceBuilder<T>builder):IResourceBuilder<IProjectOrContainerResource>whereT:IResourceWithEnvironment,IResourceWithArgs,IResourceWithEndpoints,IResourceWithWaitSupport,IComputeResource{privateIResourceBuilder<T>_builder=builder;publicIResourceBuilder<IProjectOrContainerResource>WithAnnotation<TAnnotation>(TAnnotationannotation,ResourceAnnotationMutationBehaviorbehavior=ResourceAnnotationMutationBehavior.Append)whereTAnnotation:IResourceAnnotation{_builder=_builder.WithAnnotation(annotation,behavior);returnthis;}publicIDistributedApplicationBuilderApplicationBuilder=>_builder.ApplicationBuilder;publicIProjectOrContainerResourceResource=>Unsafe.As<IProjectOrContainerResource>(_builder.Resource);}

This can then be used likenew ProjectOrContainerResourceBuilder<ContainerResource>(builder.AddContainer(...)) ornew ProjectOrContainerResourceBuilder<ProjectResource>(builder.AddProject<TProject>(...)). Both can be assigned to a variable of typeIResourceBuilder<IProjectOrContainerResource> while keeping the original resource with all its traits. The only limitation is, thatIContainerFilesDestinationResource andIResourceWithServiceDiscovery fromProjectResource would be unknown but could still be handled usingbuilder.Resource is IResourceWithServiceDiscovery. As those are not adding any members, it would also work to just add them to the interface and type constraint, but this is the IMO safer way to ensure type safety and the intentions of Aspire.

Still not sure why directly usingUnsafe.As<IResourceBuilder<IProjectOrContainerResource>>(builder.Add...) throwsEntryPointNotFoundException whileUnsafe.As<IProjectOrContainerResource>(_builder.Resource) works fine, but I assume it's related to the nested generics.

Sadly it's not possible to also inheritIResourceBuilder<T> as this will lead toCS0695 on compilation. This would have simplified the usage tobuilder is IResourceBuilder<IResourceWithServiceDiscovery>.

You must be logged in to vote
2 replies
@Falco20019
Comment options

/CC@jrunestone as this at least covers the ones you need.

@jrunestone
Comment options

Thanks for the tip but I'd rather not use unsafe type casting :D

I'm iterating the resource collection and casting toProjectResource,ContainerResource,ExecutableResource instead. Less code, more readable. At least@davidfowl has seen this so now we wait for some aggregate interfaces or something ;)

Answer selected byFalco20019
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Category
Q&A
Labels
None yet
4 participants
@Falco20019@davidfowl@jrunestone@KennethHoff

[8]ページ先頭

©2009-2025 Movatter.jp