0

There is QueueClient abstract class in Microsoft.ServiceBus.Messaging namespace. I want to create a child class name MyQueueClient. I override all the methods that I can see in the QueueClient class, but I still get compile error:

Error CS0534 'MyQueueClient' does not implement inherited abstract member 'QueueClient.OnBeginCreateBrowser(TimeSpan, AsyncCallback, object)' Core

When I use VS intellisense suggestion to generate that method, then it gives me another compilation error:

'MyQueueClient.OnBeginCreateBrowser(TimeSpan, AsyncCallBack, object)', no suitable method found to override.

Here is QueueClient class (sorry for long code, but you can see all the abstract methods are coming at the end)

//
// Summary:
//     Represents the queue client object.
public abstract class QueueClient : MessagingEntityClient, IMessageSessionEntity, IMessageClientEntity, IMessageSender, IMessageReceiver, IMessageBrowser
{

    public MessagingFactory MessagingFactory { get; }

    public ReceiveMode Mode { get; }

    public int PrefetchCount { get; set; }

    public static QueueClient Create(string path, ReceiveMode mode);

    public static QueueClient Create(string path);

    public static QueueClient Create(Uri endpointAddress, string path, AuthenticationContext authContext, ClientAssertionCertificate clientAssertionCertificate, ReceiveMode mode = ReceiveMode.PeekLock, TimeSpan? operationTimeout = null, TransportType transportType = TransportType.NetMessaging);

    public static QueueClient Create(Uri endpointAddress, string path, AuthenticationContext authContext, string clientId, UserPasswordCredential userPasswordCredential, ReceiveMode mode = ReceiveMode.PeekLock, TimeSpan? operationTimeout = null, TransportType transportType = TransportType.NetMessaging);

    public static QueueClient Create(Uri endpointAddress, string path, AuthenticationContext authContext, string clientId, Uri redirectUri, IPlatformParameters platformParameters, UserIdentifier userIdentifier = null, ReceiveMode mode = ReceiveMode.PeekLock, TimeSpan? operationTimeout = null, TransportType transportType = TransportType.NetMessaging);

    public static QueueClient Create(Uri endpointAddress, string path, AuthenticationContext authContext, ClientCredential clientCredential, ReceiveMode mode = ReceiveMode.PeekLock, TimeSpan? operationTimeout = null, TransportType transportType = TransportType.NetMessaging);

    public static QueueClient CreateFromConnectionString(string connectionString, string path);

    public static QueueClient CreateFromConnectionString(string connectionString);

    public static QueueClient CreateFromConnectionString(string connectionString, string path, ReceiveMode mode);

    public static QueueClient CreateFromConnectionString(string connectionString, ReceiveMode mode);

    public static QueueClient CreateWithManagedServiceIdentity(Uri endpointAddress, string path, ReceiveMode mode = ReceiveMode.PeekLock, TimeSpan? operationTimeout = null, TransportType transportType = TransportType.NetMessaging);

    public void Abandon(Guid lockToken, IDictionary<string, object> propertiesToModify);

    public void Abandon(Guid lockToken);

    public Task AbandonAsync(Guid lockToken);

    public Task AbandonAsync(Guid lockToken, IDictionary<string, object> propertiesToModify);

    public MessageSession AcceptMessageSession(TimeSpan serverWaitTime);

    public MessageSession AcceptMessageSession(bool isExclusiveMode, TimeSpan serverWaitTime);

    public MessageSession AcceptMessageSession(bool isExclusiveMode);

    public MessageSession AcceptMessageSession(string sessionId, Guid lockToken, TimeSpan serverWaitTime);

    public MessageSession AcceptMessageSession(string sessionId, bool isExclusiveMode, TimeSpan serverWaitTime);

    public MessageSession AcceptMessageSession(string sessionId, Guid lockToken);

    public MessageSession AcceptMessageSession(string sessionId, bool isExclusiveMode);

    public MessageSession AcceptMessageSession(string sessionId);

    public MessageSession AcceptMessageSession(string sessionId, TimeSpan serverWaitTime);

    public MessageSession AcceptMessageSession();

    public Task<MessageSession> AcceptMessageSessionAsync(string sessionId, bool isExclusiveMode);

    public Task<MessageSession> AcceptMessageSessionAsync(string sessionId, Guid lockToken);

    public Task<MessageSession> AcceptMessageSessionAsync(string sessionId);

    public Task<MessageSession> AcceptMessageSessionAsync(string sessionId, bool isExclusiveMode, TimeSpan serverWaitTime);

    public Task<MessageSession> AcceptMessageSessionAsync(string sessionId, Guid lockToken, TimeSpan serverWaitTime);

    public Task<MessageSession> AcceptMessageSessionAsync(TimeSpan serverWaitTime);

    public Task<MessageSession> AcceptMessageSessionAsync(string sessionId, TimeSpan serverWaitTime);

    public Task<MessageSession> AcceptMessageSessionAsync(bool isExclusiveMode);

    public Task<MessageSession> AcceptMessageSessionAsync(bool isExclusiveMode, TimeSpan serverWaitTime);

    public Task CancelScheduledMessageAsync(long sequenceNumber);

    public void Complete(Guid lockToken);

    public void CompleteBatch(IEnumerable<Guid> lockTokens);

    public Task CompleteBatchAsync(IEnumerable<Guid> lockTokens);

    public void DeadLetter(Guid lockToken);

    public void DeadLetter(Guid lockToken, string deadLetterReason, string deadLetterErrorDescription);

    public void DeadLetter(Guid lockToken, IDictionary<string, object> propertiesToModify);

    public Task DeadLetterAsync(Guid lockToken, IDictionary<string, object> propertiesToModify);

    public Task DeadLetterAsync(Guid lockToken);

    public Task DeadLetterAsync(Guid lockToken, string deadLetterReason, string deadLetterErrorDescription);

    public void Defer(Guid lockToken);

    public void Defer(Guid lockToken, IDictionary<string, object> propertiesToModify);

    public Task DeferAsync(Guid lockToken);

    public Task DeferAsync(Guid lockToken, IDictionary<string, object> propertiesToModify);

    public IEnumerable<MessageSession> GetMessageSessions();

    public IEnumerable<MessageSession> GetMessageSessions(DateTime lastUpdatedTime);

    public Task<IEnumerable<MessageSession>> GetMessageSessionsAsync(DateTime lastUpdatedTime);

    public Task<IEnumerable<MessageSession>> GetMessageSessionsAsync();

    public void OnMessage(Action<BrokeredMessage> callback);

    public void OnMessage(Action<BrokeredMessage> callback, OnMessageOptions onMessageOptions);

    public void OnMessageAsync(Func<BrokeredMessage, Task> callback, OnMessageOptions onMessageOptions);

    public void OnMessageAsync(Func<BrokeredMessage, Task> callback);

    public BrokeredMessage Peek();

    public BrokeredMessage Peek(long fromSequenceNumber);

    public Task<BrokeredMessage> PeekAsync();

    public Task<BrokeredMessage> PeekAsync(long fromSequenceNumber);

    public IEnumerable<BrokeredMessage> PeekBatch(int messageCount);

    public IEnumerable<BrokeredMessage> PeekBatch(long fromSequenceNumber, int messageCount);

    public Task<IEnumerable<BrokeredMessage>> PeekBatchAsync(long fromSequenceNumber, int messageCount);

    public Task<IEnumerable<BrokeredMessage>> PeekBatchAsync(int messageCount);

    public BrokeredMessage Receive(long sequenceNumber);

    public BrokeredMessage Receive(TimeSpan serverWaitTime);

    public BrokeredMessage Receive();

    public Task<BrokeredMessage> ReceiveAsync(TimeSpan serverWaitTime);

    public Task<BrokeredMessage> ReceiveAsync();

    public Task<BrokeredMessage> ReceiveAsync(long sequenceNumber);

    public IEnumerable<BrokeredMessage> ReceiveBatch(IEnumerable<long> sequenceNumbers);

    public IEnumerable<BrokeredMessage> ReceiveBatch(int messageCount, TimeSpan serverWaitTime);

    public IEnumerable<BrokeredMessage> ReceiveBatch(int messageCount);

    public Task<IEnumerable<BrokeredMessage>> ReceiveBatchAsync(int messageCount, TimeSpan serverWaitTime);

    public Task<IEnumerable<BrokeredMessage>> ReceiveBatchAsync(int messageCount);

    public Task<IEnumerable<BrokeredMessage>> ReceiveBatchAsync(IEnumerable<long> sequenceNumbers);

    public void RegisterSessionHandler(Type handlerType, SessionHandlerOptions options);

    public void RegisterSessionHandler(Type handlerType);

    public Task RegisterSessionHandlerAsync(Type handlerType, SessionHandlerOptions options);

    public Task RegisterSessionHandlerAsync(Type handlerType);

    public void RegisterSessionHandlerFactory(IMessageSessionAsyncHandlerFactory factory, SessionHandlerOptions options);

    public void RegisterSessionHandlerFactory(IMessageSessionHandlerFactory factory, SessionHandlerOptions options);

    public Task RegisterSessionHandlerFactoryAsync(IMessageSessionHandlerFactory factory, SessionHandlerOptions options);

    public Task RegisterSessionHandlerFactoryAsync(IMessageSessionAsyncHandlerFactory factory, SessionHandlerOptions options);

    public DateTime RenewMessageLock(Guid lockToken);

    public Task<DateTime> RenewMessageLockAsync(Guid lockToken);

    public Task<long> ScheduleMessageAsync(BrokeredMessage message, DateTimeOffset scheduleEnqueueTimeUtc);

    public void Send(BrokeredMessage message);

    public Task SendAsync(BrokeredMessage message);

    public void SendBatch(IEnumerable<BrokeredMessage> messages);

    public Task SendBatchAsync(IEnumerable<BrokeredMessage> messages);

    protected abstract IAsyncResult OnBeginAcceptMessageSession(string sessionId, ReceiveMode receiveMode, int prefetchCount, bool isExclusiveMode, Guid? lockToken, TimeSpan serverWaitTime, TimeSpan timeout, AsyncCallback callback, object state);

    protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state);

    protected abstract IAsyncResult OnBeginCreateReceiver(ReceiveMode receiveMode, TimeSpan timeout, AsyncCallback callback, object state);

    protected abstract IAsyncResult OnBeginCreateSender(TimeSpan timeout, AsyncCallback callback, object state);

    protected abstract MessageSession OnEndAcceptMessageSession(IAsyncResult result);

    protected override void OnEndClose(IAsyncResult result);

    protected abstract MessageReceiver OnEndCreateReceiver(IAsyncResult result);

    protected abstract MessageSender OnEndCreateSender(IAsyncResult result);

    protected abstract IEnumerable<MessageSession> OnEndGetMessageSessions(IAsyncResult result);
}

And here is my child class:

public class MyQueueClient : QueueClient
{
    protected override IAsyncResult OnBeginAcceptMessageSession(string sessionId, ReceiveMode receiveMode, int prefetchCount, bool isExclusiveMode, Guid? lockToken, TimeSpan serverWaitTime, TimeSpan timeout, AsyncCallback callback, object state)
    {
        throw new NotImplementedException();
    }

    protected override IAsyncResult OnBeginCreateReceiver(ReceiveMode receiveMode, TimeSpan timeout, AsyncCallback callback, object state)
    {
        throw new NotImplementedException();
    }

    protected override IAsyncResult OnBeginCreateReceiver(string subQueueName, ReceiveMode receiveMode, TimeSpan timeout, AsyncCallback callback, object state)
    {
        throw new NotImplementedException();
    }

    protected override IAsyncResult OnBeginCreateSender(TimeSpan timeout, AsyncCallback callback, object state)
    {
        throw new NotImplementedException();
    }

    protected override IAsyncResult OnBeginGetMessageSessions(DateTime lastUpdatedTime, AsyncCallback callback, object state)
    {
        throw new NotImplementedException();
    }

    protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
    {
        throw new NotImplementedException();
    }

    protected override MessageSession OnEndAcceptMessageSession(IAsyncResult result)
    {
        throw new NotImplementedException();
    }

    protected override MessageReceiver OnEndCreateReceiver(IAsyncResult result)
    {
        throw new NotImplementedException();
    }

    protected override MessageSender OnEndCreateSender(IAsyncResult result)
    {
        throw new NotImplementedException();
    }

    protected override IEnumerable<MessageSession> OnEndGetMessageSessions(IAsyncResult result)
    {
        throw new NotImplementedException();
    }

    protected override void OnEndOpen(IAsyncResult result)
    {
        throw new NotImplementedException();
    }
}

There are 9 abstract methods in QueueClient[from metadata] class, and I override all of them with no error. But then it asked to implement 'OnBeginCreateBrowser' method, which is not there.

Aryan Firouzian
  • 1,940
  • 5
  • 27
  • 41
  • Azure doesn't affect the runtime or the language. Post your code instead of describing it – Panagiotis Kanavos Sep 03 '19 at 14:21
  • Besides, you're not supposed to create a `Microsoft.ServiceBus.Messaging.QueueClient` yourself. You should create it using one of the [Create](https://learn.microsoft.com/en-us/dotnet/api/microsoft.servicebus.messaging.queueclient.create?view=azure-dotnet) factory methods. Did try to follow a tutorial about [Microsoft.Azure.ServiceBus.QueueClient](https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-dotnet-get-started-with-queues) perhaps? That's a concrete class that can be instantiated directly – Panagiotis Kanavos Sep 03 '19 at 14:26
  • I want to create an object to have same behaviour, with additional one receive method. – Aryan Firouzian Sep 03 '19 at 14:40
  • Why? You aren't supposed to do that, you should use `Create`. Are you trying to mock the class perhaps? – Panagiotis Kanavos Sep 03 '19 at 14:49

2 Answers2

2

The OnBeginCreateBrowser and OnEndCreateBrowser methods are internal - so basically, even though it's an abstract class, you can't derive from it yourself.

While this may be frustrating, it can be a very useful design technique within libraries, where you want to provide a layer of abstraction for consumers, but without necessarily providing the ability for them to supply an implementation.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    @AryanFirouzian you aren't supposed to, you need to use the `Create` static method. In any case `Microsoft.ServiceBus.Messaging` was replaced by `Microsoft.Azure.ServiceBus`. Use the new API instead – Panagiotis Kanavos Sep 03 '19 at 14:47
  • I know it is an old API, but the project itself is old and giant. So It might be good idea to start new API at some point. I wanted to create a Receive_RoundRobin method on QueueClient child object. So, I would only change QueueClient object with that child. This would be the cleanest way to achieve that. – Aryan Firouzian Sep 03 '19 at 14:53
  • The designers of that abstract class made sure no constructors are seen from outside the assembly. I really wish the C# compiler would complain ___first___ about there being no base class constructor to "chain". And only after that complained about missing implementations of abstract members. It would be much more clear that it is by design that you cannot derive from that type from another assembly. But of course, the C# compiler has other things to think of as well, so maybe it is a difficult change to introduce(?). – Jeppe Stig Nielsen Sep 03 '19 at 15:07
  • 1
    @JeppeStigNielsen: It would also be nice if it complained in a way that said "You haven't implemented these abstract methods, but you can't do so anyway." – Jon Skeet Sep 03 '19 at 15:12
2

As this probably duplicate question explains, Microsoft.ServiceBus.Messaging is an older API whose QueueClient class can only be created using one of the QueueClient.Create factory methods, eg :

var client=QueueClient.Create(queuePath);

Users of the library aren't meant to inherit from the class and implement its methods.

This was replaced a couple of years ago by the new Microsoft.Azure.ServiceBus namespace. One of the big changes is that the classes have documentation that explains how to use them!

Check for example QueueClient. The rest of the answer is simply copied from the linked doc page :

Create a new QueueClient

 IQueueClient queueClient = new QueueClient(
     namespaceConnectionString,
     queueName,
     ReceiveMode.PeekLock,
     RetryExponential);

Send a message to the queue:

 byte[] data = GetData();
 await queueClient.SendAsync(data);

Register a message handler which will be invoked every time a message is received.

 queueClient.RegisterMessageHandler(
        async (message, token) =>
        {
            // Process the message
            Console.WriteLine($"Received message: SequenceNumber:{message.SystemProperties.SequenceNumber} Body:{Encoding.UTF8.GetString(message.Body)}");

            // Complete the message so that it is not received again.
            // This can be done only if the queueClient is opened in ReceiveMode.PeekLock mode.
            await queueClient.CompleteAsync(message.SystemProperties.LockToken);
        },
        async (exceptionEvent) =>
        {
            // Process the exception
            Console.WriteLine("Exception = " + exceptionEvent.Exception);
            return Task.CompletedTask;
        });

UPDATE - Mocking the client

There's no reason to inherit from either class, unless one wants to mock them. In this case the Microsoft.Azure.ServiceBus.QueueClient class makes mocking easy as it inherits from the IQueueClient interface. One can create a mock client by implementing this interface or using a mocking library like Moq to mock only the relevant methods

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • Thanks for the answer. I know it is an old api, but I have to live with that for now. The reason for creating child was to create some sort of RoundRobin receiver on the QueueClient without being messy. – Aryan Firouzian Sep 03 '19 at 14:55
  • 1
    That's a different question that may already have an answer through some extension point perhaps. If it doesn't, you can create a wrapper to implement the functionality you want on top of QueueClient. – Panagiotis Kanavos Sep 03 '19 at 15:03