在MVVM应用程序并发架构 [英] Concurrency architecture in an MVVM application

查看:114
本文介绍了在MVVM应用程序并发架构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个客户机/服务器类型的应用程序设置,类似于BitTorrent的下载程序。然而,种子被发送到客户机远程

I have a client/server type application setup, similar to a bittorrent downloading program. However, torrents are sent to the client remotely.

的主片共享数据是文件(种子)的列表来进行下载。

The main piece of shared data is the list of files (torrents) to be downloaded.

我要同时处理这些情况:

I have to handle these cases concurrently:

  • 服务器发送(通过WCF)要下载的文件的更新列表,这意味着一些新的文件被添加到列表中,然后从列表中删除一些(和一些保持不变)
  • 在同一时间的文件可能完成下载/改变状态,因此列表中的项目需要与新州本地更新
  • 在客户端上的本地事件可能导致列表中的到期一些项目,所以他们应该被删除

我使用的是MVV​​M架构,但我相信视图模型应密切映射到视图,所以我增加了一个服务层,这是目前一帮单身人士(我知道)的。其中之一作为所述列表中的共享资源,所以我有一个单一的集合由多个线程更新。

I am using an MVVM architecture but I believe the viewmodel should map closely to the view so I have added a 'services' layer which is currently a bunch of Singletons (I know). One of which acts as a shared resource for said list, so I have a single collection being updated by multiple threads.

我想从单身离开赞成依赖注入和不可变对象,以减少死锁,删除/分离对象和数据完整性的错误,我已经看到。

I want to move away from the Singletons in favor of dependency injection and immutable objects to reduce the deadlocks, "deleted/detached object" and data integrity errors I've been seeing.

不过,我根本不知道在哪里保持列表以及如何管理来自不同的线程进入的事件,可能会取消/负/覆盖列表当前处理。

However, I have little idea where to 'keep' the list and how to manage incoming events from different threads that may cancel/negate/override current processing of the list.

我要寻找有关处理这样的场景在较高水平的指针。

I am looking for pointers on handling such a scenario at a high level.

我使用实体框架在列表中的项目的数据还需要坚持。

I am using Entity Framework for the items in the list as the data also needs to be persisted.

推荐答案

我最近做了类似的一个窗口服务检查的东西。它结束了是很容易实现了。

I have recently done something similar for a windows service checker. It ended up being very easy to implement too.

在你的情况我认为需要以下几点。

In your case I see the need for the following.

文件 - 它的唯一目的就是下载一个文件,并更改通知。
文件管理 - 维护文件和添加新的,删除等的列表。

File - it sole purpose is to download a file and notify of changes.
FileManager - maintains a list of Files and adding new, removing ect.

public class File : INotifyPropertyChanged
    {
        private readonly string _fileName;
        private Thread _thread;
        private Task _task;
        private bool _cancelled;

        private TaskStatus _taskStatus;
        private int _taskProgress;
        private int _taskTotal;

        public event PropertyChangedEventHandler PropertyChanged;

        public File(string fileName)
        {
            _fileName = fileName;
            TaskStatus = TaskStatus.NotStarted;
        }

        public TaskStatus TaskStatus
        {
            get { return _taskStatus; }
            private set
            {
                _taskStatus = value;
                PropertyChanged.Raise(this, x => x.TaskStatus);
            }
        }

        public int TaskProgress
        {
            get { return _taskProgress; }
            private set
            {
                _taskProgress = value;
                PropertyChanged.Raise(this, x => x.TaskProgress);
            }
        }
        public int TaskTotal
        {
            get { return _taskTotal; }
            private set
            {
                _taskTotal = value;
                PropertyChanged.Raise(this, x => x.TaskTotal);
            }
        }

        public void StartTask()
        {
            _cancelled = false;

            //.Net 4 - task parallel library - nice
            _task = new Task(DownloadFile, TaskCreationOptions.LongRunning);
            _task.Start();

            //.Net Other
            _thread = new Thread(DownloadFile);
            _thread.Start();
        }

        public void CancelTask()
        {
            _cancelled = true;
        }

        private void DownloadFile()
        {
            try
            {
                TaskStatus = TaskStatus.Running;

                var fileLength = _fileName.Length;
                TaskTotal = fileLength;

                for (var i = 0; i < fileLength; i++)
                {
                    if (_cancelled)
                    {
                        TaskStatus = TaskStatus.Cancelled;
                        return;
                    }

                    //Some work to download the file
                    Thread.Sleep(1000); //sleep for the example instead 

                    TaskProgress = i;
                }

                TaskStatus = TaskStatus.Completed;

            }
            catch (Exception ex)
            {
                TaskStatus = TaskStatus.Error;
            }
        }
    }

    public enum TaskStatus
    {
        NotStarted,
        Running,
        Completed,
        Cancelled,
        Error
    }

    public static class NotifyPropertyChangedExtention
    {
        public static void Raise<T, TP>(this PropertyChangedEventHandler pc, T source, Expression<Func<T, TP>> pe)
        {
            if (pc != null)
            {
                pc.Invoke(source, new PropertyChangedEventArgs(((MemberExpression)pe.Body).Member.Name));
            }
        }
    }

这样做的好处是,你永远需要更新从后台线程的用户界面。你所更新的,只有背景类将被写入也只读属性。任何出方该类只能读取,所以你不必担心锁定。用户界面绑定系统将获得该属性已更改时的PropertyChanged升起,然后将读取的值的通知。

The beauty of this is that you never need to update the UI from the background thread. What you are updating are readonly properties that only the background class will be writing too. Anything out side this class can only read so you don't have to worry about locking. The UI binding system will get a notification that the property has changed when the PropertyChanged is raised and will then read the value.

现在的经理

public class FileManager
    {
        public ObservableCollection<File> ListOfFiles { get; set; }

        public void AddFile(string fileName)
        {
            var file = new File(fileName);
            file.PropertyChanged += FilePropertyChanged;
            file.StartTask();
            ListOfFiles.Add(file);
        }

        void FilePropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "TaskStatus")
            {
                var file = (File) sender;
                if (file.TaskStatus==TaskStatus.Completed)
                {
                    RemoveFile(file);// ??? automatically remove file from list on completion??
                }
            }
        }

        public void RemoveFile(File file)
        {
            if (file.TaskStatus == TaskStatus.Running)
            {
                file.CancelTask();
            }
            //unbind event
            file.PropertyChanged -= FilePropertyChanged;
            ListOfFiles.Remove(file);
        }
    }

现在你需要做的,您的视图模型是由这是一个观察的集合的文件管理露出ListOfFiles。从该通知将让绑定系统知道当UI需要更新。

Now all you need to do in your view model is expose the ListOfFiles from the FileManager which is an observable collection. Notifications from it will let the binding system know when the UI needs to update.

只需绑定ListOfFiles到Li​​stView或类似的,对于这将让列表视图知道如何使每个文件的文件类添加一个DataTemplate。

Just bind the ListOfFiles to a ListView or similar, add a datatemplate for the File class which will let the list view know how to render each file.

您WCF服务器和视图模型应该有一个参考到同一个文件管理器,WCF添加和删除文件,视图模型使其可用于用户界面的ListOfFiles。
这只是一个粗略的黑客传达的理念。您将需要添加你的东西,你认为合适。

Your WCF server and view model should have a reference to the same File Manager, WCF adds and removes files, viewmodel makes the ListOfFiles available to the UI.
This is just a rough hack to get the concept across. You will need to add your stuff as you see fit.

让我知道,如果这有助于。

Let me know if this helped.

这篇关于在MVVM应用程序并发架构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆