Мне показалось, что для меня хорошо работает следующее (что я взял части из MVVM light и бизнес-шаблона RIA).
Я создаю новый класс ViewModelBase, унаследованный от ViewModelBase MVVM Lightкласс, в котором я реализую DomainContext, список возможных ожидающих операций, текущую операцию, свойство IsBusy, SaveCommand и защищенный метод для регистрации любых операций, созданных ViewModels, которые наследуются от этого класса.
Вот пример:
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.После этого это руки прочь.Затем вы можете связать BusyIndicator с вашей собственностью IsWorking, и у вас есть упрощенная реализация RIA.
Надеюсь, это поможет вам начать работу.