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

Refactor System.Servicemodel.ClientBase to have non-generic base #13940

Closed
rcollette opened this issue Dec 19, 2014 · 5 comments
Closed

Refactor System.Servicemodel.ClientBase to have non-generic base #13940

rcollette opened this issue Dec 19, 2014 · 5 comments
Assignees
Labels
api-needs-work API needs work before it is approved, it is NOT ready for implementation area-Meta
Milestone

Comments

@rcollette
Copy link

The following is an example of a generic extension method I would like to write, but cannot because ClientBase<T> does not inherit from a base class containing the non generic members. It would be very helpful if ClientBase<T> was refactored into ClientBase<T>:ClientBase

using DotNetOpenAuth.OAuth2;
using System;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading;
using System.Threading.Tasks;

namespace OAuthClient
{
    public static class ExtensionMethods
    {
        public static async Task<T> CallAsync<TClient, TChannel, T>(this TClient wcfClient, Func<TClient, T> predicate, IAuthorizationState authorization, CancellationToken cancellationToken) where TClient : ClientBase<TChannel> where TChannel : class
        {
            if (authorization == null)
            {
                throw new InvalidOperationException("No access token!");
            }

            // Refresh the access token if it expires and if its lifetime is too short to be of use.
            if (authorization.AccessTokenExpirationUtc.HasValue)
            {
                await AuthorizationServer.Client.RefreshAuthorizationAsync(authorization, TimeSpan.FromSeconds(30));
            }

            var httpRequest = (HttpWebRequest)WebRequest.Create(wcfClient.Endpoint.Address.Uri);
            ClientBase.AuthorizeRequest(httpRequest, authorization.AccessToken);

            var httpDetails = new HttpRequestMessageProperty();
            httpDetails.Headers[HttpRequestHeader.Authorization] = httpRequest.Headers[HttpRequestHeader.Authorization];
            using (var scope = new OperationContextScope(wcfClient.InnerChannel))
            {
                OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpDetails;
                bool isError = true;
                try
                {
                    T result = predicate(wcfClient);
                    // If there is an error on the channel, the close call will throw an exception.
                    wcfClient.Close();
                    isError = false;
                    return result;
                }
                finally
                {
                    // If we have an error on the channel, we cannot close the channel so we abort.
                    // Exceptions that occur will still be raised unmodified.
                    if (isError)
                    {
                        wcfClient.Abort();
                    }
                }
            }
        }
    }
}
@davkean
Copy link
Member

davkean commented Dec 19, 2014

Can you provide more details on this? I'm not sure I understand. Which method cannot you not write? AuthorizeRequest method that you call above? Or CallAsync method?

@rcollette
Copy link
Author

When making a call to the extension like:

var client = new ProfileServiceClient();
UserProfile userProfile = 
    await client.CallAsync(c => c.UserProfile(), authorization, response.ClientDisconnectedToken);

The compiler generates the following error at the call to the extension function:

Error 89 'OAuthClient.SampleResourceServer.ProfileServiceClient' does not contain a definition for 'CallAsync' and no extension method 'CallAsync' accepting a first argument of type 'OAuthClient.SampleResourceServer.ProfileServiceClient' could be found (are you missing a using directive or an assembly reference?)

While intellisense shows the extension method as available for ProfileServiceClient, I'm assuming the compiler isn't able to infer the type TChannel (At least not in VS 2012) because there is no parameter associated with that type.

I need to be able to access ClientBase<T> properties like InnerChannel. If the non-generic properties and methods of ClientBase<T> were refactored into a non-generic base class ClientBase, then I could declare the extension method like:

public static async Task<T> CallAsync<TClient, T>(this TClient wcfClient, Func<TClient, T> predicate, IAuthorizationState authorization, CancellationToken cancellationToken) where TClient : ClientBase

The problem with inferring the type of TChannel then goes away. In general I have found it a useful design pattern to place all non-generic members of a generic class in non-generic base, for just this reason.

@mconnew
Copy link
Member

mconnew commented May 1, 2015

I haven't tested this, but wouldn't something along these lines work:

    public static class ExtensionMethods
    {
        public static async Task<T> CallAsync<TChannel, T>(this ClientBase<TChannel> wcfClient, Func<TChannel, T> predicate, IAuthorizationState authorization, CancellationToken cancellationToken)
        {
            var uri = wcfClient.Endpoint.Address.Uri; // Endpoint exists on ClientBase<TChannel>
            // In theory the following could throw a runtime cast exception if the class derived from ClientBase<T>
            // didn't implement the service interface, but then you would have a pretty broken client anyway and your
            // problems are likely much bigger!!
            TChannel channel = (TChannel)wcfClient;
            T result = predicate(channel);
        }
    }

@rcollette
Copy link
Author

No, it isn't able to infer the TChannel from the wcfCient parameter.

@roncain
Copy link
Contributor

roncain commented May 27, 2015

Thanks @rcollette . I have reopened this issue in the new WCF repo as Issue dotnet/wcf#42 where we can continue to discuss this.

@roncain roncain closed this as completed May 27, 2015
Olafski referenced this issue in Olafski/corefx Jun 15, 2017
Add commit file reference to 1.1 Preview 1 relnotes
MaximLipnin referenced this issue in MaximLipnin/corefx Jun 26, 2019
@msftgits msftgits transferred this issue from dotnet/corefx Jan 31, 2020
@msftgits msftgits added this to the 1.0.0-rtm milestone Jan 31, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Jan 8, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-needs-work API needs work before it is approved, it is NOT ready for implementation area-Meta
Projects
None yet
Development

No branches or pull requests

5 participants