Я возобновил работу над этим проектом в течение последних нескольких недель и, наконец, нашел решение.Учитывая комментарии и мысли людей, размещенные выше, я не уверен, что кто-либо, кроме меня, понимает, что я пытаюсь сделать, но я подумал, что, возможно, стоит опубликовать, как я решил это.Как минимум, его написание гарантирует, что I поймет это: -).
Обобщая вопрос еще раз:
У меня есть сервер управления домом, который выставляет объекты в моемдомой через интерфейс SOAP.Home.LivingRoom.Fireplace
, например, выставляется как:
http://server/Home/LivingRoom/Fireplace?property=DisplayName
http://server/Home/LivingRoom/Fireplace?property=PowerState
Выполнение HTTP GET для них приведет к ответу HTTP, содержащему значение свойства (например, «Камин в гостиной»)."и" Выкл. "соответственно).
Дверь гаража (например, Home.Garage.EastGarageDoor
) отображается как:
http://server/Home/Upstairs/EastGarageDoor?property=DisplayName
http://server/Home/Upstairs/EastGarageDoor?property=GarageDoorOpened
http://server/Home/Upstairs/EastGarageDoor?property=Trigger
Здесь мыесть свойство, которое, если установлено, вызывает действие (Trigger
).Выполнение POST против этого с HTTP-телом «True» приведет к открытию / закрытию двери.
Я создаю приложение WP7 в качестве внешнего интерфейса для этого.Я решил следовать модели Mvvm и использую Mvvm-Light.
WP7 не имеет встроенного способа поддержки уведомлений от интерфейсов REST, и я еще не готов построить свой собственный (хотя этона моем радаре).Поэтому, чтобы пользовательский интерфейс отображал актуальное состояние, мне нужно опросить.Количество сущностей и объем данных относительно невелики, и теперь я доказал, что могу сделать так, чтобы они хорошо работали с опросом, но есть некоторые оптимизации, которые я могу сделать, чтобы улучшить их (в том числе добавление смарт-символов на сервер, чтобы включить систему, аналогичную уведомлениям).).
В моем решении я размыл грани между моей моделью и моей моделью представления.Если вы действительно хотите быть в тени, моя «Модель» - это просто низкоуровневые классы, которые я использую для упаковки моих запросов Http (например, GetPropertyAsync(objectLocation, propertyName, completionMethod)
).
В итоге я определил универсальный класс длясвойства.Это выглядит так:
namespace Premise.Model
{
//where T : string, bool, int, float
public class PremiseProperty<T>
{
private T _Value;
public PremiseProperty(String propertyName)
{
PropertyName = propertyName;
UpdatedFromServer = false;
}
public T Value
{
get { return _Value; }
set { _Value = value; }
}
public String PropertyName { get; set; }
public bool UpdatedFromServer { get; set; }
}
}
Затем я создал ViewModelBase
(из Mvvm-Light) производный базовый класс PremiseObject
, который представляет базовый класс, на котором основан каждый объект в системе управления (например, которыйбуквально называется `Object ').
Самый важный метод в PremiseObject
- это переопределение RaisePropertyChanged
:
/// </summary>
protected override void RaisePropertyChanged<T>(string propertyName, T oldValue, T newValue, bool sendToServer)
{
if (sendToServer)
SendPropertyChangeToServer(propertyName, newValue);
// Check if we are on the UI thread or not
if (App.Current.RootVisual == null || App.Current.RootVisual.CheckAccess())
{
// broadcast == false because I don't know why it would ever be true
base.RaisePropertyChanged(propertyName, oldValue, newValue, false);
}
else
{
// Invoke on the UI thread
// Update bindings
// broadcast == false because I don't know why it would ever be true
GalaSoft.MvvmLight.Threading.DispatcherHelper.CheckBeginInvokeOnUI(() =>
base.RaisePropertyChanged(propertyName, oldValue, newValue, false));
}
}
Обратите внимание на несколько вещей: 1) Я переусердствовалИспользование / повторное использование параметра broadcast
.Если это правда, то изменение свойства «отправляется на сервер» (я делаю HTTP POST).Я не использую изменения свойств вещания где-либо еще (и я даже не уверен, для чего я это использовал).2) Я всегда передаю широковещательную рассылку False при вызове base.
.
PremiseObject
имеет набор стандартных свойств PremiseProperty
: Location (URL-адрес объекта), Name, DisplayName, Value (значение свойства).DisplayName выглядит следующим образом:
protected PremiseProperty<String> _DisplayName = new PremiseProperty<String>("DisplayName");
public string DisplayName
{
get
{
return _DisplayName.Value;
}
set
{
if (_DisplayName.Value == value)
{
return;
}
var oldValue = _DisplayName;
_DisplayName.Value = value;
// Update bindings and sendToServer change using GalaSoft.MvvmLight.Messenging
RaisePropertyChanged(_DisplayName.PropertyName,
oldValue, _DisplayName, _DisplayName.UpdatedFromServer);
}
}
Таким образом, это означает, что в любое время .DisplayName
изменения в моей программе передаются на весь пользовательский интерфейс и ЕСЛИ И ТОЛЬКО ЕСЛИ _DisplayName.UpdatedFromServer
Истина также вернополучить обратно на сервер.
Так как же установить .UpdatedFromServer
?Когда мы получаем наш обратный вызов из асинхронного запроса Http:
protected void DisplayName_Get(PremiseServer server)
{
String propertyName = _DisplayName.PropertyName;
_DisplayName.UpdatedFromServer = false;
server.GetPropertyAsync(Location, propertyName, (HttpResponseArgs) =>
{
if (HttpResponseArgs.Succeeded)
{
//Debug.WriteLine("Received {0}: {1} = {2}", DisplayName, propertyName, HttpResponseArgs.Response);
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
DisplayName = (String)HttpResponseArgs.Response; // <-- this is the whole cause of this confusing architecture
_DisplayName.UpdatedFromServer = true;
HasRealData = true;
});
}
});
}
Всякий раз, когда пользовательский интерфейс хочет свежих данных, эти функции XXX_Get вызывают (например, по таймеру опроса, когда изменяется представление, запуск приложения и т. Д.))
Мне нужно продублировать приведенный выше код для каждого определенного мной свойства, что довольно болезненно, но я пока не нашел способа его обобщить (поверьте, я пробовал, но мои знания C #просто не достаточно сильный, и я просто продолжаю двигаться проблема).Но это работает и работает хорошо.
Чтобы охватить все базы, вот пример свойства Trigger класса GarageDoor:
protected PremiseProperty<bool> _Trigger = new PremiseProperty<bool>("Trigger");
public bool Trigger
{
set
{
if (value == true)
RaisePropertyChanged(_Trigger.PropertyName, false, value, true);
}
}
Обратите внимание, как заставить параметр broadcast
RaisePropertyChanged
в true, и как это свойство "Только запись"?В результате создается HTTP-запрос POST по URL-адресу «GarageDoor.Location» + ?propertyName=
+ value.ToString()
.
Я очень доволен тем, что получилось. Это что-то вроде хака, но теперь я реализовал несколько сложных представлений, и это хорошо работает. Разделение, которое я создал, позволит мне изменить базовый протокол (например, группировать запросы и заставить сервер отправлять только измененные данные), и мои ViewModels не должны будут меняться.
Мысли, комментарии, предложения?