Skip to content

WorkerService WcfClient Dispose issue creating memory leakage #4564

@Reena-Patel

Description

@Reena-Patel

Describe the bug
Memory leakage problem in .net core worker service

To Reproduce
Steps to reproduce the behavior:

  1. Use a worker service type project and one of the class is inherited by IHostedService.
    in the hostedservice on receiving the message from message broker it is processing the message.

Create WcfProxy Class

/// <summary>
    /// Defines required operations for executing synchronous Func-delegates using a proxy wcf client.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface IWcfProxy<out T> : IDisposable
    {
        /// <summary>
        /// Executes a synchronous action-delegate by using the contained client proxy.
        /// </summary>
        /// <param name="action">Action used on the client.</param>
        void Execute(Action<T> action);

        /// <summary>
        /// Executes a synchronous func-delegate by using the contained client proxy.
        /// </summary>
        /// <param name="function">Func used on the client.</param>
        /// <typeparam name="TResult">Type of the return value supplied by the called wcf service method.</typeparam>
        /// <returns>Returns value return by the called service method.</returns>
        TResult Execute<TResult>(Func<T, TResult> function);
    }
public class WcfProxy<T> : IWcfProxy<T> where T : class, ICommunicationObject
    {
        private readonly T _client;

        public WcfProxy(string endpointUrl)
        {
            if (string.IsNullOrWhiteSpace(endpointUrl))
            {
                throw new ArgumentException("Endpoint cannot be empty.");
            }

            _client = CreateChannel(new EndpointAddress(endpointUrl));
        }

        /// <summary>
        /// Implicit disposal: garbage collector calls finalizer and the dispose method through it.
        /// </summary>
        ~WcfProxy()
        {
            Dispose();
        }

        /// <summary>
        /// Explicit disposal: manual call to dispose and suppression of further garbage collection calls.
        /// </summary>
        public void Dispose()
        {
            CloseConnection();            
            GC.SuppressFinalize(this);
        }

        private void CloseConnection()
        {
            // Do not repeat dispose if called more than once.
            try
            {
                if (_client != null && _client.State == CommunicationState.Opened)
                {
                    _client.Close();
                    
                }
                else
                {
                    _client?.Abort();
                }
            }
            catch (Exception)
            {
                _client?.Abort();
            }
        }

        private static T CreateChannel(EndpointAddress endpointAddress)
        {
            var binding = new BasicHttpBinding();
            var factory = new ChannelFactory<T>(binding, endpointAddress);
            return factory.CreateChannel();
        }

        public void Execute(Action<T> function)
        {
            function.Invoke(_client);
        }

        public TResult Execute<TResult>(Func<T, TResult> function)
        {
            return function.Invoke(_client);
        }
    }

We are registering WcfProxy as transient instance.

services.AddTransient<IWcfProxy<IOfferService>>(service =>
                new WcfProxy<IOfferServiceChannel>(service.GetRequiredService<IOptions<ServiceEndpointConfiguration>>()
                    .Value.SellerOfferService));

This is the class where we are injecting IWcfProxy class.

  public class SellerOfferService : ISellerOfferService
    {
        private readonly IWcfProxy<IOfferService> _clientProxy;
        private readonly ILogger<SellerOfferService> _logger;

        public SellerOfferService(ILogger<SellerOfferService> logger, IWcfProxy<IOfferService> clientProxy)
        {
            _logger = logger;
            _clientProxy = clientProxy;
        }

        ~SellerOfferService()
        {
            _clientProxy.Dispose();
        }

        /// <summary>
        /// Get Offer database id by Offer salesforce id
        /// </summary>
        /// <param name="offerSalesforceId"></param>
        /// <returns></returns>
        public async Task GetOfferDatabaseIdByOfferSalesforceIdAsync(string offerSalesforceId)
        {
            var response = await _clientProxy.Execute(x => x.GetOfferIdentityBySalesforceIdsAsync(new GetOfferIdentityBySalesforceIdsRequestMessage()));
        }

    }

Expected behavior
Memory should be release for the WcfProxy class.

Screenshots
Finance Message Listener App Memory Graph

Additional context
Could you guys please help us to find out the reason why memory is not getting disposed for the WcfProxy?

Metadata

Metadata

Labels

investigation neededAdditional investigation needed before issue can be triaged.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions