Проверка данных на UI с WCF RIA и шаблоном MVVM
-
25-09-2019 - |
Вопрос
Есть ли лучшая практика или широко распространенный способ структурирования и проверки данных с использованием MVVM в сочетании с RIA-сервисами в Silverlight?
Вот суть моей проблемы. Скажем, у меня есть сотрудник, сотрудничаторВизмModel и какой-то субъект сотрудника. В обычных приложениях RIA я буду выдержать, что субъект сотрудника по мнению, и я получаю проверку «бесплатно», потому что объекты реализуют inotifydataErrorInfo и idataeRrorinfo (правильно?).
Теперь, если я хочу разоблачить свойства сотрудников через ViewModel вместо непосредственно через сущность, он становится все сложнее. Я мог бы разоблачить биты, которые мне нужен прямо и подключите их к объекту на бэкэнде, как это:
private Employee _employee;
public EmployeeViewModel()
{
_employee = new Employee();
}
public string Name
{
get { return _employee.Name; }
set
{
_employee.Name = value;
// fire property change, etc.
}
}
... но я теряю вкусно «свободную» валидацию организаций. В противном случае я мог бы разоблачить сущность непосредственно в модели зрения, как так
private Employee _employee;
public Employee Employee
{
get { return _employee; }
}
public EmployeeViewModel()
{
_employee = new Employee();
}
В этом случае представление будет связываться непосредственно к субъекту сотрудника и находить его свойства там, как так:
<StackPanel DataContext="{Binding Employee}">
<TextBox Text="{Binding Name}" />
</StackPanel>
Используя этот метод, мы получаем «свободную» валидацию, но не совсем чистая реализация MVVM.
Третий вариант будет реализовывать inotifydataerrorInfo и iDataErrinfo себя в VMS, но это похоже на ужасное много сантехнического кода, учитывая, насколько легко было бы для меня использовать вышеуказанное решение и иметь что-то немного меньше «чистота», но черт возьми намного проще в конце дня.
Поэтому я предполагаю, что мой вопрос, какой из этих подходов подходит, в какой ситуации? Есть ли лучший подход, который мне не хватает?
Если это актуально, я смотрю на каркас Caliburn.micro MVVM, но я был бы стремиться увидеть ответы, которые вновь применяются.
Решение
Я использую RIA с Caliburn.micro и очень доволен своим решением для проверки на стороне клиента.
Что я сделал, положить ValidationBaseViewModel
между Screen
(предоставляется Caliburn.micro) и моим фактическим приложением VMS (EmployeeViewModel
в твоем случае). ValidationBaseViewModel
внедрение INotifyDataErrorInfo
Так что этот сантехнический кодекс вы говорите, только один раз. Затем я добавляю / удаляю / уведомляю об ошибках через ValidationBaseViewModel
от переопределения (Caliburn.micro) PropertyChangedBase.NotifyOfPropertyChange
со следующим кодом:
public override void NotifyOfPropertyChange(string property)
{
if (_editing == null)
return;
if (HasErrors)
RemoveErrorFromPropertyAndNotifyErrorChanges(property, 100);
if (_editing.HasValidationErrors)
{
foreach (var validationError in
_editing.ValidationErrors
.Where(error => error.MemberNames.Contains(property)))
{
AddErrorToPropertyAndNotifyErrorChanges(property, new ValidationErrorInfo() { ErrorCode = 100, ErrorMessage = validationError.ErrorMessage });
}
}
base.NotifyOfPropertyChange(property);
}
Это на самом деле в другом виртуальной машине (между ValidationBaSeviewModel и OthereeeviewModel) со следующим определением:
public abstract class BaseEditViewModel<TEdit> :
ValidationBaseViewModel where TEdit : Entity
куда Entity
это слияние System.ServiceModel.DomainServices.Client.Entity
и то _editing
Участник класса является экземпляром этого типа TEdit
который редактируется текущей VM.
В сочетании с Caliburn Conoutines это позволяет мне делать несколько прохладных вещей, как следующее:
[Rescue]
public IEnumerable<IResult> Save()
{
if (HasErrors)
{
yield return new GiveFocusByName(PropertyInError);
yield break;
}
...
}
Другие советы
Если вы не хотите использовать внешние ресурсы или рамки, то я мог бы иметь ViewModelBase
Это реализует INotifyDataErrorInfo
.
Этот класс будет иметь ValidateProperty(string propertyName, object value)
проверить специфическое свойство и Validate()
Метод для подтверждения всего объекта. Внутренне используют Validator
класс, чтобы вернуть ValidationResult
с.
Если вы используете отражатель, это может быть довольно легко достичь, побуждая процесс проверки в Entity
класс сам к ViewModelBase
.
Хотя это не «свободно», все еще относительно дешево.
Вот образец реализации IDataErrorInfo
. Отказ Хотя не проверено, даст вам идею.
public class ViewModelBase : INotifyPropertyChanged, INotifyDataErrorInfo
{
/*
* InotifyPropertyChanged implementation
* Consider using Linq expressions instead of string names
*/
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public IEnumerable GetErrors(string propertyName)
{
if (implValidationErrors == null) return null;
return ImplValidationErros.Where(ve =>
ve.MemberNames.Any(mn => mn == propertyName));
}
public bool HasErrors
{
get
{
return implValidationErrors == null || ImplValidationErros.Any();
}
}
private List<ValidationResult> implValidationErrors;
private List<ValidationResult> ImplValidationErros
{
get
{
return implValidationErrors ??
(implValidationErrors = new List<ValidationResult>());
}
}
private ReadOnlyCollection<ValidationResult> validationErrors;
[Display(AutoGenerateField = false)]
protected ICollection<ValidationResult> ValidationErrors
{
get
{
return validationErrors ??
(validationErrors =
new ReadOnlyCollection<ValidationResult>(ImplValidationErros));
}
}
protected void ValidateProperty(string propertyName, object value)
{
ValidationContext validationContext =
new ValidationContext(this, null, null);
validationContext.MemberName = propertyName;
List<ValidationResult> validationResults =
new List<ValidationResult>();
Validator.TryValidateProperty(
value,
validationContext,
validationResults);
if (!validationResults.Any()) return;
validationResults
.AddRange(ValidationErrors
.Where(ve =>
!ve.MemberNames.All(mn =>
mn == propertyName)));
implValidationErrors = validationResults;
if (ErrorsChanged != null)
ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
}
}
Вы можете использовать частичный класс, чтобы продлить свой Entitty и добавлять проверку данных там через iDataErrorInfo.