65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 MVVM、IoC、DI 和 API 的天气应用程序

starIconstarIconstarIconstarIconstarIcon

5.00/5 (9投票s)

2016 年 7 月 29 日

CC (ASA 2.5)

5分钟阅读

viewsIcon

21685

这是一个为 Windows (通用) 10 设备开发的应用程序,采用了 MVVM、IoC 容器和依赖注入。还集成了来自 Yahoo 的 API 和 SQLite 服务。

引言

我开发了一个适用于 Windows 通用 10 设备的应用程序。它通过 Yahoo 的 API (Application Programming Interface,应用程序编程接口) 提供世界各地多个城市的天气预报。它包含了 MVVM (Model View View-Model,模型-视图-视图模型) 模式 (更具体地说,是 MVVM light 库) 和 IoC (Inversion of Control,控制反转) 架构。我还使用 SQLite (Structured Query Language lite,结构化查询语言精简版) 数据库来存储搜索结果。那么,它是如何实现的呢?这里有一些信息

MVVM (Model View View-Model)

MVVM 架构MVVM 是一种软件架构模式,顾名思义,它由三个部分组成。在您的项目中,有三个部分:模型、视图和视图模型,如后面的图像所示。

模型提供数据库访问,但在此项目中,我使用了 Yahoo API,并将其映射到 Repositories 文件夹。

视图模型包含 ICommands 和一些绑定属性。所有视图模型都继承自 ViewModelBaseClass,该类实现了 INotifyPropetyChanged 接口。

最后一部分是视图。它包含 XAML 页面和数据模板。数据模板存储在 DataTemplates 文件夹中。

每个部分之间相互通信

  • 视图与视图模型通信
  • 视图模型与模型 (和存储库) 通信
  • 存储库访问 API (或其他数据源)

理论上是正确的,但在实际情况中,视图也与模型通信。这可能是为了元素属性到模型属性的数据绑定。

在图像中,还有第四部分称为单元测试。这些是您可以用来测试代码的测试。此项目未实现此功能,本文也未对此进行解释。

IoC 容器 (Inversion of Control Container,控制反转容器)

IoC 容器是具有基本功能以注册和解析实例的容器。在此项目中,我们将使用 MVVM light 中的 SimpleIoc 类。在此项目中,我们将使用以下方法

名称 描述
Register<TClass>(Boolean)

注册给定类型,并可以选择立即创建实例。

GetInstance<TService>()

提供获取给定类型实例的方法。如果之前没有实例化任何实例,则会创建一个新实例。如果已经创建了一个实例,则返回该实例。

注意:如果类未注册,则该方法将返回 null

有关更多信息和方法,您可以阅读 mvvmlight.net 上的文档:mvvmlight.net

还有一个默认的无参数构造函数,它初始化 SimpleIoc 类的新实例。

依赖注入或 DI

依赖注入是一种工作方式,可以使代码独立于您正在工作的类。为此,您可以使用接口。它们只定义了方法、属性和变量的签名。一个示例是,在开发过程中使用一个模拟类,或者在部署时使用一个 Web 服务。下面是一个代码示例

public interface IDataSource
{
    IEnumarble<object> Get(); // used for get all the objects
}

public class DummySource : IDataSource
{
    public IEnumarble<object> Get()
    {
        return new List<object> {
            new { Letter = "A", Number = "1" },
            new { Letter = "A", Number = "1" }
        }
    }
}

public class WebSource : IDataSource
{
    public IEnumarble<object> Get()
    {
        //return all objects of a web service 
    }
}

在调用 Get() 方法的地方,类是什么并不重要。它可以是 dummy 类,也可以是 webservice

注意:在调用方法的地方,不要使用类,而是使用接口,如下面的代码所示

IDataSource source = new DummySource();
IEnumarble<object> dummyData = source.Get();

source = new WebSource();
IEnumarble<object> webData = source.Get();

这段代码之所以能工作,是因为变量 source 独立于 DummySourceWebSource 类,因为我创建了一个名为 IDataSource 的“包装器” interface,它具有方法的签名。

构建应用程序

解决方案的构建

在解决方案中,我创建了三个项目,分别命名为

  1. Data 包含用于访问 API 的存储库。为此项目下载 Newtonsoft.Json NuGet 包。
  2. Models 包含我们需要的类。
  3. UWP 代表 Universal Windows 10 Project (通用 Windows 10 项目),并包含以下文件夹
    • Assets 是项目中默认添加的文件夹,包含 Windows 商店中 Logo 的图像。
    • DataTemplates 用于存储数据模板
    • Pages 包含我们将要使用的页面
    • Services 包含 IoC 容器
    • ViewModels 包含视图模型和一个文件夹
      • core 用于存储核心数据

我们还需要一些 NuGet 包,您可以从 NuGet 管理器下载。对于数据项目,是 Newtonsoft.JSON;对于 UWP 项目,我们需要 MvvmLight

页面的构建

如果 UWP 项目已构建,您将在该项目中看到以下项

  • Properties 包含该项目的属性。
  • References 包含该项目所需的引用。
  • 我们刚刚创建的文件夹。
  • App.xaml 包含项目的重要资源和信息。
  • MainPage.xaml 是应用程序运行时显示的第一个页面。
  • Package.appxmanifest
  • project.json
  • Wheater.UWP_TemporaryKey.pfx

首先,我们需要 MainPage.xaml

API (Application Programming Interface,应用程序编程接口) 的使用

API 或应用程序编程接口是一种服务,用于获取由其他实例 (如 Yahoo) 提供的例程、协议、工具或数据,软件程序员可以在其程序、应用程序和网站中访问。它还可以被机器人、Dominica、无人机等发现。

我们需要的第一条 URL

首先,我们必须访问 Yahoo 的 API 来获取数据。我们将使用的 URL (Uniform Recourse Locator,统一资源定位符) 是这个

https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.
forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places%20where%20text%3D%22@placemame%22)
&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys

我在 developer.yahoo.com/weather/ 上找到了该 URL。您可以看到参数 q,它是一个 SQL 语句

SELECT *
FROM weather.forecast
WHERE woeid 
IN (SELECT woeid
    FROM geo.places
    WHERE text = @placemame)

这将从 weather.forecast 表中选择所有字段,其中 woeid (WOEID 代表 Where On Earth IDentifier,地球标识符) 等于从 geo.places 表中 select 的值,该表中的 text 字段等于变量 @placename。此变量必须有一个值,这样 URL 才是有效的。在这种情况下,它是一个 string,或者用 SQL 语言来说,它是 nvarchartext 类型,具体取决于 Yahoo 使用的 SQL 语言。

在上面的 URL 中,您还可以看到第二个参数,称为 format。在本例中,我们将使用 JSON (JavaScript Object Notation,JavaScript 对象表示法) 作为接收值。

将 JSON 代码转换为 C# 类

如果您导航到该 URL,您会看到此 JSON 代码。这里有 2016 年 3 月 1 日来自比利时 Meenen 的天气预报

{  
   "query":{  
      "count":1,
      "created":"2016-03-01T17:31:15Z",
      "lang":"en-US",
      "results":{  
         "channel":{  
            "title":"Yahoo! Weather - Meenen, BE",
            "link":"http://us.rd.yahoo.com/dailynews/rss/weather/Meenen__BE/
                      *http://weather.yahoo.com/forecast/FRXX4403_f.html",
            "description":"Yahoo! Weather for Meenen, BE",
            "language":"en-us",
            "lastBuildDate":"Tue, 01 Mar 2016 6:00 pm CET",
            "ttl":"60",
            "location":{  
               "city":"Meenen",
               "country":"Belgium",
               "region":""
            },
            "units":{  
               "distance":"mi",
               "pressure":"in",
               "speed":"mph",
               "temperature":"F"
            },
            "wind":{  
               "chill":"37",
               "direction":"200",
               "speed":"20"
            },
            "atmosphere":{  
               "humidity":"93",
               "pressure":"29.83",
               "rising":"0",
               "visibility":"3.73"
            },
            "astronomy":{  
               "sunrise":"7:28 am",
               "sunset":"6:30 pm"
            },
            "image":{  
               "title":"Yahoo! Weather",
               "width":"142",
               "height":"18",
               "link":"http://weather.yahoo.com",
               "url":"http://l.yimg.com/a/i/brand/purplelogo//uh/us/news-wea.gif"
            },
            "item":{  
               "title":"Conditions for Meenen, BE at 6:00 pm CET",
               "lat":"50.8",
               "long":"3.12",
               "link":"http://us.rd.yahoo.com/dailynews/rss/weather/Meenen__BE/
                       *http://weather.yahoo.com/forecast/FRXX4403_f.html",
               "pubDate":"Tue, 01 Mar 2016 6:00 pm CET",
               "condition":{  
                   "code":"26",
                   "date":"Tue, 01 Mar 2016 6:00 pm CET",
                   "temp":"45",
                   "text":"Cloudy"
               },
               "description":"\n<img src=\"http://l.yimg.com/a/i/us/we/52/26.gif\"/>
                        <br />\n<b>Current Conditions:</b><br />\nCloudy, 45 F<BR />\n<BR />
                        <b>Forecast:</b><BR />\nTue - Showers Late. High: 48 Low: 42<br />
                        \nWed - PM Rain/Wind. High: 48 Low: 36<br />\nThu - Showers/Wind. 
                        High: 44 Low: 34<br />\nFri - Showers. High: 45 Low: 31<br />
                        \nSat - PM Showers. High: 46 Low: 33<br />\n<br />\n
                        <a href=\"http://us.rd.yahoo.com/dailynews/rss/weather/Meenen__BE/
                        *http://weather.yahoo.com/forecast/FRXX4403_f.html\">
                        Full Forecast at Yahoo! Weather</a><BR/><BR/>\n(provided by 
                        <a href=\"http://www.weather.com\" >The Weather Channel</a>)<br/>\n",
               "forecast":[  
                  {  
                     "code":"45",
                     "date":"1 Mar 2016",
                     "day":"Tue",
                     "high":"48",
                     "low":"42",
                     "text":"Showers Late"
                  },
                  {  
                     "code":"12",
                     "date":"2 Mar 2016",
                     "day":"Wed",
                     "high":"48",
                     "low":"36",
                     "text":"PM Rain/Wind"
                  },
                  {  
                     "code":"11",
                     "date":"3 Mar 2016",
                     "day":"Thu",
                     "high":"44",
                     "low":"34",
                     "text":"Showers/Wind"
                  },
                  {  
                     "code":"11",
                     "date":"4 Mar 2016",
                     "day":"Fri",
                     "high":"45",
                     "low":"31",
                     "text":"Showers"
                  },
                  {  
                     "code":"39",
                     "date":"5 Mar 2016",
                     "day":"Sat",
                     "high":"46",
                     "low":"33",
                     "text":"PM Showers"
                  }
               ],
               "guid":{  
                  "isPermaLink":"false",
                  "content":"FRXX4403_2016_03_05_7_00_CET"
               }
            }
         }
      }
   }
}

当然,您可以手动创建正确的 C# 类,但在互联网上,有一些简单的工具可以为您完成这项工作。其中一个是 json2csharp.com (仅限 C#) 或 jsonutils.com (支持许多其他语言),它们可以为您转换所有代码。通过复制粘贴将类导入您的项目,并将 RootObject 重命名为 YahooWeather

在应用程序中使用 API

在数据项目中创建一个名为 WebRepo 的新类,并将以下代码放入其中以使用 API。

public class WebRepo
{
    public static async YahooWeather GetData(string placename)
    {
        try
        {
            using (HttpClient client = new HttpClient())
            {
                string url = $@"https://query.yahooapis.com/v1/public/yql?q=select%20
                             *%20from%20weather.forecast%20where%20woeid%20in%20
                             (select%20woeid%20from%20geo.places%20where%20text%3D%22
                             {placemame}%22)&format=json&env=store%3A%2F%2Fdatatables.org
                             %2Falltableswithkeys";

                string yahooGeoJSON = await client.GetStringAsync(url);
                return JsonConvert.DeserializeObject<YahooWeather>(yahooGeoJSON); 
            }
        }
        catch
        {
            return null;
        }
    }
}

稍后,我们将为此类实现依赖注入。

© . All rights reserved.