kalview
kalview

Reputation: 350

WCF with Named Pipes and WPF - Timeout with time-consuming operations

I'm struggling with the project that are copying files from the network(CIFS share). IPC which I'm using is Named Net Pipes with Duplex Channel and for GUI I'm using WPF.

The application is communicating only between the service and the UI on the same machine.

Problem is confusing, because I've prepare some time consuming methods like copying entire directories from the CIFS share to the Client, and during the copy I've encountered many times a problem like this:

System.TimeoutException: 'This request operation sent to net.pipe://localhost/service did not receive a reply within the configured timeout (00:01:00). The time allotted to this operation may have been a portion of a longer timeout. This may be because the service is still processing the operation or because the service was unable to send a reply message. Please consider increasing the operation timeout (by casting the channel/proxy to IContextChannel and setting the OperationTimeout property) and ensure that the service is able to connect to the client.'

I was researching for the solution and I found that the timeout can be increased, but it did not solves my problem.

For small directories it could be fine to increase the timeout to e.g. 3-4 minutes, but for large ones that can weight more than 1GB it could be a big problem.

Independent actions will also take place in the background, such as checking every few minutes if a given CIFS share is responding or logging the application status regardless of what is currently happening (I mean user actions). I use asynchrony, among others, using Tasks, but I'm not sure if it works well with Named Pipes.

I've even prepared a Callback interface that returns a progress of copying, thinking that it could help "hold" the timeout, but it did not.

I'm at a dead end now, because I started testing this solution with a Service separate from the GUI, which contains all the application logic. Does anyone have an idea what I could change, or possibly what technology to use to maintain the idea of ​​separation and similar communication? The separation of these two processes is caused by the difference in user permissions.


@Edit

There are two interfaces that I use:

    [ServiceContract(CallbackContract = typeof(ICopierCallbackService))]
    public interface ICopierService
    {        
        [OperationContract]
        [ServiceKnownType(typeof(PackagePart))]
        Task<List<Package>> CheckPackages();
    }
    
    [ServiceContract]   
    public interface ICopierCallbackService
    {
        [OperationContract(IsOneWay = true)]
        void GetTime(string time);

        [OperationContract(IsOneWay = true)]
        void ReportCopyProgress(string source, string destination, int progress);
    }

I created additional class related to this copying (CopyProgressEventArgs) which implements EventArgs, that allows to pass more than 2 params unlike the typical use of "Invoke".

This is my "ServiceClient":

public class ServiceClient : ICopierCallbackService
    {
        private ICopierService _serviceProxy;
        private DuplexChannelFactory<ICopierService> _duplexChannelFactory;
        private const string NamedPipeAddress = "net.pipe://localhost/service";
        public event EventHandler<CopyProgressEventArgs> CopyProgressChanged;

        public ServiceClient()
        {
            _duplexChannelFactory = new DuplexChannelFactory<ICopierService>(this, new NetNamedPipeBinding(), new EndpointAddress(NamedPipeAddress));
            _serviceProxy = _duplexChannelFactory.CreateChannel();
        }

        public async Task<List<Package>> CheckPackages()
        {
            List<Package> packages = await _serviceProxy.CheckPackages();

            return packages;
        }

        public void ReportCopyProgress(string source, string destination, int progress)
        {
            CopyProgressChanged?.Invoke(this, new CopyProgressEventArgs(source, destination, progress));
        }
    }

And finally in the ViewModel I've prepared a property for monitoring whether the progress has changed, and currently printing it using System.Diagnostics.Debug.Writeline gives me a valid output to the console.


@Edit 2

CheckPackages retrieves some data through API, then Deserializes it etc.:

public async Task<List<Package>> CheckPackages(ICopierCallbackService callback)
        {
            Root root = await ApiHandler.FetchClientPackages(_Settings.Hostname);

            List<Package> packages = root.Data.Packages;

            DirectoryInfo directoryInfo = new DirectoryInfo(_Settings.LocalPackagesRealPath);

            FileInfo[] files = directoryInfo.GetFiles("*.txt");

            packages = await AdjustPackage(files, packages);

            // here I'm getting some constant file paths and copying them using belows method
            await CopyIcons(callback);

            return packages;
        }

Example method that triggers the ReportCopyProgress - I have multiple versions of them, depends of what needs to be copied:

private static async Task<bool> CopyDirectoryInternalAsync(string sourceDirectoryPath, string destinationDirectoryPath, ICopierCallbackService callback)
        {
            try
            {
                EnsureDirectoryExists(destinationDirectoryPath);

                var sourceFiles = Directory.GetFiles(sourceDirectoryPath);
                for (int i = 0; i < sourceFiles.Length; i++)
                {
                    var sourceFilePath = sourceFiles[i];
                    var destinationFilePath = Path.Combine(destinationDirectoryPath, Path.GetFileName(sourceFilePath));

                    bool success = await CopyFileInternalAsync(sourceFilePath, destinationFilePath, callback);

                    if (callback != null)
                    {
                        int progress = (int)((i + 1) / (double)sourceFiles.Length * 100);
                        callback.ReportCopyProgress(sourceFilePath, destinationFilePath, progress);
                    }

                    if (!success)
                    {
                        return false;
                    }
                }

                var subDirectories = Directory.GetDirectories(sourceDirectoryPath);
                foreach (var subDirectory in subDirectories)
                {
                    var destinationSubDirectory = Path.Combine(destinationDirectoryPath, Path.GetFileName(subDirectory));
                    bool success = await CopyDirectoryInternalAsync(subDirectory, destinationSubDirectory, callback);

                    if (!success)
                    {
                        return false;
                    }
                }

                return true;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"Error copying directory: {ex.Message}");
                return false;
            }
        }

Upvotes: 0

Views: 102

Answers (0)

Related Questions