如何使用 MVVM-Light 和 WCF RIA 服务创建可混合项目

发布于 2024-09-10 03:49:37 字数 262 浏览 2 评论 0原文

我想使用 MVVM 模式构建我的业务应用程序。我选择 MVVM-Light 因为它符合我的需求。 在我见过的有关 MVVM-Light 的每个示例中,没有人使用 WCF RIA。经典的 MIX10 示例在同一项目中使用服务,而 WCF RIA 在 Web 项目中创建服务。 问题是:构建 WCF Ria 创建的整个 DomainContex 的接口看起来非常困难(这对我来说肯定很难!),但是如果没有接口,我如何构建一个假的 DomainContex 以在 Blend 和测试中使用? 我错过了什么吗? 谢谢。

I'd like to build my business application using the MVVM pattern. I choose MVVM-Light because it fits to my needs.
In every example I've seen about MVVM-Light, no-one use the WCF RIA. The classic MIX10 example uses a Service in the same project while WCF RIA create a service in the Web project.
The question is: it looks very hard to build an interface of the whole DomainContex the WCF Ria creates (it is surely hard for me!) but without an interface how could I build a fake DomainContex to be used in Blend and in tests as well?
Am I missing something?
Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

静赏你的温柔 2024-09-17 03:49:37

实际上,我使用的解决方案是可混合的,并且使混合过程更加简单。

以下是我如何使用此框架实现此目的的示例:

 public class FolderViewModel : VMBase
{
    private string _subject = string.Empty;
    private string _folderName = string.Empty;
    private string _service = string.Empty;
    private string _dept = string.Empty;
    private string _efolderid = string.Empty;
    private string _timingName = string.Empty;
    private WorkplanBase _planBase = null;
    private IEnumerable<PSCustomList> _timingOptions = null;
    private decimal _totalvalue = 0;


    public FolderViewModel()
    {
        registerForMessages();

        if (IsInDesignMode)
        {
            // Code runs in Blend --> create design time data.
            EFOLDERID = "0123456790123456790123456791";
            Subject = "9999-00 - This is a test nVision Subject";
            Service = "AUDCOMP";
            Department = "AUDIT";
            FolderName = "NVSN003000";


            List<PSCustomList> listItems = new List<PSCustomList>();
            listItems.Add(new PSCustomList()
            {
                ID = "1234",
                ParameterValue = "Busy Season"
            });
            listItems.Add(new PSCustomList()
            {
                ID = "1134",
                ParameterValue = "Another Season"
            });

            _timingOptions = listItems.ToArray();
            _totalvalue = 12000;

            PlanBase = new WorkplanBase()
            {
                ClientFee = 15000,
                Timing = "1234"
            };
        }
    }
}

然后,所有示例数据都在绑定在 ViewModelLocator 类中的实际视图模型的构造函数中定义。当您处于混合状态时,VMBase 不会尝试实例化 DataContext。

Actually, the solution I am using is blendable, and makes the blending process even simpler.

Here is an exmample of how I accomplish this using this framework:

 public class FolderViewModel : VMBase
{
    private string _subject = string.Empty;
    private string _folderName = string.Empty;
    private string _service = string.Empty;
    private string _dept = string.Empty;
    private string _efolderid = string.Empty;
    private string _timingName = string.Empty;
    private WorkplanBase _planBase = null;
    private IEnumerable<PSCustomList> _timingOptions = null;
    private decimal _totalvalue = 0;


    public FolderViewModel()
    {
        registerForMessages();

        if (IsInDesignMode)
        {
            // Code runs in Blend --> create design time data.
            EFOLDERID = "0123456790123456790123456791";
            Subject = "9999-00 - This is a test nVision Subject";
            Service = "AUDCOMP";
            Department = "AUDIT";
            FolderName = "NVSN003000";


            List<PSCustomList> listItems = new List<PSCustomList>();
            listItems.Add(new PSCustomList()
            {
                ID = "1234",
                ParameterValue = "Busy Season"
            });
            listItems.Add(new PSCustomList()
            {
                ID = "1134",
                ParameterValue = "Another Season"
            });

            _timingOptions = listItems.ToArray();
            _totalvalue = 12000;

            PlanBase = new WorkplanBase()
            {
                ClientFee = 15000,
                Timing = "1234"
            };
        }
    }
}

Then all the sample data is defined in the constructor of the Acutal View Models that are bound in your ViewModelLocator class. The VMBase takes care of not trying to instantiate the DataContext when you are in blend.

冰之心 2024-09-17 03:49:37

我发现以下内容对我来说效果很好(我从 MVVM light 和 RIA 业务模板中获取了部分内容)。

我构建了一个新的 ViewModelBase 类,继承自 MVVM Light 的 ViewModelBase 类,在其中实现了一个 DomainContext、可能挂起的操作列表、当前操作、IsBusy 属性、SaveCommand 和一个受保护的方法,用于记录从此继承的 ViewModel 创建的任何操作班级。

这是一个示例:

public class VMBase : ViewModelBase
{
    protected DomainContext _context;
    protected IList<OperationBase> operations = new List<OperationBase>();
    protected OperationBase currentOperation;

    public VMBase()
    {
        if (IsInDesignMode == false)
        {
            _context = new BudgetContext();
            _context.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(_context_PropertyChanged);
        }
        SaveChangesCommand = new RelayCommand(
                () =>
                {
                    currentOperation = _context.SubmitChanges((so) =>
                    {
                        DispatcherHelper.CheckBeginInvokeOnUI(() =>
                        {
                            OnSaveComplete();
                            SaveChangesCommand.RaiseCanExecuteChanged();
                            CancelChangesCommand.RaiseCanExecuteChanged();
                        });
                    },
                        null);
                    logCurrentOperation();
                },
                () =>
                {
                    return _context != null && _context.HasChanges;
                });
        CancelChangesCommand = new RelayCommand(
                () =>
                {
                    _context.RejectChanges();
                    SaveChangesCommand.RaiseCanExecuteChanged();
                    CancelChangesCommand.RaiseCanExecuteChanged();
                },
                () =>
                {
                    return _context != null && _context.HasChanges;
                });
    }

    /// <summary>
    /// This is called after Save is Completed
    /// </summary>
    protected virtual void OnSaveComplete() { }

    void _context_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "HasChanges")
        {
            DispatcherHelper.CheckBeginInvokeOnUI(() =>
            {
                SaveChangesCommand.RaiseCanExecuteChanged();
                CancelChangesCommand.RaiseCanExecuteChanged();
            });
        }
    }

    /// <summary>
    /// Bind to Busy Indicator to show when async server
    /// call is being made to submit or load data via the
    /// DomainContext/
    /// </summary>
    public bool IsWorking
    {
        get
        {
            if (currentOperation != null)
                return !currentOperation.IsComplete;
            else
            {
                return operations.Any(o => o.IsComplete == false);
            }
        }
    }

    /// <summary>
    /// Call this command to save all changes
    /// </summary>
    public RelayCommand SaveChangesCommand { get; private set; }
    /// <summary>
    /// Revert all changes not submitted
    /// </summary>
    public RelayCommand CancelChangesCommand { get; private set; }

    /// <summary>
    /// Occurs after each operation is completed, which was registered with logCurrentOperation()
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void currentOperation_Completed(object sender, EventArgs e)
    {
        currentOperation = null;
        operations.Remove((OperationBase)sender);
        DispatcherHelper.CheckBeginInvokeOnUI(() =>
        {
            RaisePropertyChanged("IsWorking");
        });
    }

    /// <summary>
    /// Logs and notifies IsBusy of the Current Operation
    /// </summary>
    protected void logCurrentOperation()
    {
        currentOperation.Completed += new EventHandler(currentOperation_Completed);
        operations.Add(currentOperation);
        RaisePropertyChanged("IsWorking");
    }

    /// <summary>
    /// Just make sure any async calls are done
    /// </summary>
    public override void Cleanup()
    {
        // Clean own resources if needed
        foreach (OperationBase op in operations)
        {
            if (op.IsComplete == false)
            {
                if (op.CanCancel)
                    op.Cancel();
            }
        }

        base.Cleanup();
    }
}

然后,在实际的视图模型中,您可以专注于视图模型的属性和命令 - 并且所有域上下文都已经为您真正连接起来。只需使用 _context 属性 - 例如:

void loadFolderInfo()
    {
        if (_context != null)
        {
            EntityQuery<eFolder> query = _context.GetEFoldersByFolderQuery(_efolderid);
            currentOperation =
                _context.Load<eFolder>(query,
                new Action<LoadOperation<eFolder>>((lo) =>
                    {
                        if (lo.HasError)
                        {
                            Messenger.Default.Send<DialogMessage>(
                                new DialogMessage(lo.Error.Message, (mbr) =>
                                    {}));
                        }
                        DispatcherHelper.CheckBeginInvokeOnUI(
                            () =>
                            {
                                eFolder myFolder = lo.Entities.First();
                                Subject = myFolder.eSubject;
                                FolderName = myFolder.eFolderName;


                            });

                    }), null);
            logCurrentOperation();
        }
    }

属性可能看起来像这样:

public string EFOLDERID
    {
        get
        {
            return _efolderid;
        }

        set
        {
            if (_efolderid == value)
            {
                return;
            }

            var oldValue = _efolderid;
            _efolderid = value;

            // Update bindings and broadcast change using GalaSoft.MvvmLight.Messenging
            RaisePropertyChanged("EFOLDERID", oldValue, value, true);
            loadFolderInfo();
        }
    }

关键部分是 VMBase 类处理 DomainContext 的所有连接和管理。为了实现这一点,在 ViewModel 实现中,请确保将任何 _context.BaseOperationCall(..) 的返回值分配给 currrentOperation,然后立即调用 logCurrentOperation。在那之后,就放手了。然后,您可以将 BusyIndi​​cator 绑定到 IsWorking 属性,这样您就拥有了简化的 RIA 实现。

希望这可以帮助您入门。

What I have found that works well for me is the following (which I have taken parts from both MVVM light and the RIA business template).

I build a new ViewModelBase class, inheriting from MVVM Light's ViewModelBase class, where I implementment a DomainContext, list of possibly pending operations, current operation, IsBusy property, a SaveCommand, and a protected method to log any operations created by ViewModels that inherit from this class.

Here is an example:

public class VMBase : ViewModelBase
{
    protected DomainContext _context;
    protected IList<OperationBase> operations = new List<OperationBase>();
    protected OperationBase currentOperation;

    public VMBase()
    {
        if (IsInDesignMode == false)
        {
            _context = new BudgetContext();
            _context.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(_context_PropertyChanged);
        }
        SaveChangesCommand = new RelayCommand(
                () =>
                {
                    currentOperation = _context.SubmitChanges((so) =>
                    {
                        DispatcherHelper.CheckBeginInvokeOnUI(() =>
                        {
                            OnSaveComplete();
                            SaveChangesCommand.RaiseCanExecuteChanged();
                            CancelChangesCommand.RaiseCanExecuteChanged();
                        });
                    },
                        null);
                    logCurrentOperation();
                },
                () =>
                {
                    return _context != null && _context.HasChanges;
                });
        CancelChangesCommand = new RelayCommand(
                () =>
                {
                    _context.RejectChanges();
                    SaveChangesCommand.RaiseCanExecuteChanged();
                    CancelChangesCommand.RaiseCanExecuteChanged();
                },
                () =>
                {
                    return _context != null && _context.HasChanges;
                });
    }

    /// <summary>
    /// This is called after Save is Completed
    /// </summary>
    protected virtual void OnSaveComplete() { }

    void _context_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "HasChanges")
        {
            DispatcherHelper.CheckBeginInvokeOnUI(() =>
            {
                SaveChangesCommand.RaiseCanExecuteChanged();
                CancelChangesCommand.RaiseCanExecuteChanged();
            });
        }
    }

    /// <summary>
    /// Bind to Busy Indicator to show when async server
    /// call is being made to submit or load data via the
    /// DomainContext/
    /// </summary>
    public bool IsWorking
    {
        get
        {
            if (currentOperation != null)
                return !currentOperation.IsComplete;
            else
            {
                return operations.Any(o => o.IsComplete == false);
            }
        }
    }

    /// <summary>
    /// Call this command to save all changes
    /// </summary>
    public RelayCommand SaveChangesCommand { get; private set; }
    /// <summary>
    /// Revert all changes not submitted
    /// </summary>
    public RelayCommand CancelChangesCommand { get; private set; }

    /// <summary>
    /// Occurs after each operation is completed, which was registered with logCurrentOperation()
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void currentOperation_Completed(object sender, EventArgs e)
    {
        currentOperation = null;
        operations.Remove((OperationBase)sender);
        DispatcherHelper.CheckBeginInvokeOnUI(() =>
        {
            RaisePropertyChanged("IsWorking");
        });
    }

    /// <summary>
    /// Logs and notifies IsBusy of the Current Operation
    /// </summary>
    protected void logCurrentOperation()
    {
        currentOperation.Completed += new EventHandler(currentOperation_Completed);
        operations.Add(currentOperation);
        RaisePropertyChanged("IsWorking");
    }

    /// <summary>
    /// Just make sure any async calls are done
    /// </summary>
    public override void Cleanup()
    {
        // Clean own resources if needed
        foreach (OperationBase op in operations)
        {
            if (op.IsComplete == false)
            {
                if (op.CanCancel)
                    op.Cancel();
            }
        }

        base.Cleanup();
    }
}

Then, in your actual view models, you can focus on the properties, and commands for the view model - and all the domain context is really wired for you already. Just use the _context property - for example:

void loadFolderInfo()
    {
        if (_context != null)
        {
            EntityQuery<eFolder> query = _context.GetEFoldersByFolderQuery(_efolderid);
            currentOperation =
                _context.Load<eFolder>(query,
                new Action<LoadOperation<eFolder>>((lo) =>
                    {
                        if (lo.HasError)
                        {
                            Messenger.Default.Send<DialogMessage>(
                                new DialogMessage(lo.Error.Message, (mbr) =>
                                    {}));
                        }
                        DispatcherHelper.CheckBeginInvokeOnUI(
                            () =>
                            {
                                eFolder myFolder = lo.Entities.First();
                                Subject = myFolder.eSubject;
                                FolderName = myFolder.eFolderName;


                            });

                    }), null);
            logCurrentOperation();
        }
    }

and a property might look something like this:

public string EFOLDERID
    {
        get
        {
            return _efolderid;
        }

        set
        {
            if (_efolderid == value)
            {
                return;
            }

            var oldValue = _efolderid;
            _efolderid = value;

            // Update bindings and broadcast change using GalaSoft.MvvmLight.Messenging
            RaisePropertyChanged("EFOLDERID", oldValue, value, true);
            loadFolderInfo();
        }
    }

The key part is that the VMBase class handles all wiring and management of the DomainContext. To make this happen, in your ViewModel implementations, be sure to assign any _context.BaseOperationCall(..)'s return value to the currrentOperation, then immediately call the logCurrentOperation. After that, it's hands off. You can then bind a BusyIndicator to your IsWorking propery, and you have a simplified RIA implementation.

Hopefully this helps you get started.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文