(需要关于可怕代码的建议)
我正试图通过将MVVM和所有视图模型的通用DataService类结合起来,调用它们相应的web服务处理程序,为我的旧Windows Phone应用程序代码带来一些优雅。
例如,在通知ViewModel中,我有:
private ObservableCollection<Notification> _notifications;
public ObservableCollection<Notification> Notifications
{
get
{
return _notifications;
}
}
public void GetNotifications()
{
new DataService().DownloadViewModelData<ObservableCollection<Notification>>(GetNotificationsCallback, "getnotificationslist.ashx");
this.IsDataLoaded = true;
}
public void GetNotificationsCallback(ObservableCollection<Notification> notificationsList)
{
_notifications = notificationsList;
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
NotifyPropertyChanged("Notifications");
});
this.IsDataLoaded = true;
}
在DataService类中,我试图制作一个与服务通信的通用方法:
public static string ServerUrl = "http://<ip-address>:<port>/";
public void DownloadViewModelData<T>(Action<T> callbackFunction, string handlerName, bool methodIsPOST = false, List<KeyValuePair<string, string>> querySet = null) where T : class
{
var queryString = "";
if(null != querySet)
{
foreach (KeyValuePair<string, string> tuple in querySet)
{
queryString += tuple.Key + "=" + tuple.Value + "&";
}
queryString = queryString.Remove(queryString.Length - 1, 1);
}
var urlQueryString = ServerUrl + handlerName;
if (!methodIsPOST)
urlQueryString += queryString;
var webRequest = HttpWebRequest.CreateHttp(urlQueryString);
webRequest.ContentType = "application/x-www-form-urlencoded";
Func<AsyncCallback, object, IAsyncResult> requestingMethod = null;
if (methodIsPOST)
{
webRequest.Method = "POST";
webRequest.ContentLength = queryString.Length;
webRequest.BeginGetRequestStream(
a =>
{
System.IO.Stream postStream = webRequest.EndGetRequestStream(a);
byte[] byteArray = System.Text.Encoding.UTF8.GetBytes(queryString);
postStream.Write(byteArray, 0, queryString.Length);
postStream.Close();
webRequest.BeginGetResponse(
b =>
{
using (WebResponse response = webRequest.EndGetResponse(b))
{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
callbackFunction(new DataContractJsonSerializer(typeof(T)).ReadObject(response.GetResponseStream()) as T);
}
}
},
null
);
},
null
);
}
else
{
webRequest.Method = "GET";
webRequest.BeginGetResponse(
a =>
{
using (WebResponse response = webRequest.EndGetResponse(a))
{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
callbackFunction(new DataContractJsonSerializer(typeof(T)).ReadObject(response.GetResponseStream()) as T);
}
}
},
null
);
}
}
问题:
使用lambda表达式的正确(现代)方式和处理MVVM数据层的总体正确方法(假设我们的项目中有多个模型、视图和视图模型)吗?
你会推荐这种方法来调用可移植类库(PCL)中的web服务吗?
在我看来,有一种更好的方法可以做到这一点,这样ViewModel就不知道数据服务的实现。
我会考虑做几件事。
- 开始使用像Ninject这样的依赖注入框架。这将允许您让ViewModel不知道它正在使用的数据服务类型。您可以使用DI框架为代码注入特定的实现
- 创建一个表示数据服务的接口。为了便于论证,我将把我的示例称为
IDataService
。该接口的主要目标是抽象出检索数据的方法。因此,可以从数据库、Web服务或文件访问数据,视图模型具有任何知识(或关心)
对于你的代码,我会给它签名:
public interface IDataService
{
Task<IEnumerable<Notification>> ListNotifications();
}
您会注意到我使用的是Task<>
,它允许我使用.Net 4.5异步框架。所以现在我们有一个接口,只说,I have a way of returning a list of notifications, asynchronously
。这是您的视图模型所需的信息。
不要重新创建
ObservableCollection<Notification>
。如果需要更改集合的内容,请Clear()
它,然后重新填充,但不要重新绑定或重新创建集合本身。这也有许多性能优势。如果您将接口定义放在可移植库中,那么如果您需要特定平台的特定实现(例如,WP8与Win8),那么由于您只在ViewModels中引用接口,那么您可能只需要更改很小的数量就可以支持差异实现。
希望一切都有意义。