本文章是由機器翻譯。

Windows Azure AppFabric 服務匯流排

使用可攜式類別庫建立連續用戶端

David Kean

下載代碼示例

我感到幸運,住在不斷連接設備的天。我愛我能夠答覆電子郵件使用我的電話,乘公車回家。它是能夠 Skype 和我在世界的另一邊的家人和我的 Xbox 上全國各地與志同道合的玩家合作,令人驚歎。然而,在這個世界的永久的互聯網連接,有,約書亞 Topolsky 說,"在我們的計算體驗的缺失連結"(engt.co/9GVeKl)。

此缺失的環節是指缺乏 Topolsky 所調用的連續的用戶端 ; 就是在今天發生當從一個設備移動到另一個的破碎工作流解決方案。正如我在我的電腦、 平板電腦和電話之間切換中典型的一天,我當前的流覽會話、 文檔、 windows 和應用程式狀態應該自然流所有這些。這樣一來,我會花較少的上下文切換時間和更多的時間對實際工作和娛樂。

在本文中,我將展示如何構建一個簡單的連續用戶端應用程式在跨越多個設備和平臺。我會讓使用的新的可擕式類庫 (PCLs) 以紓緩跨平臺應用程式),發展與雲 — — 特別是 Windows Azure AppFabric 服務巴士中 — — 來處理設備之間的通信。

在你回家的路 … …

這是下午晚些時候,我在試圖快速修復這個最後的 bug,所以我可以避免交通繁忙的工作。不可避免的電話打來電話:"親愛的你能回家的路上拾起一些牛奶、 麵包和鷹嘴豆嗎?"我掛了,去商店,實現忘了買些什麼。最後,我回家與我們已有茶水間的項。這是令人沮喪,和今天的解決方案往往涉及很多-和往復電話諮詢:"你說速凍的豌豆或鷹嘴豆嗎?""鷹嘴豆。和你在那兒,你可以買衛生紙嗎?"

為減輕我們的婚姻緊張局勢,圍繞這個問題 (其他人將不得不等待另一天),我將寫一個簡單的應用程式稱為"對您的方式主頁"我們基於 Windows Phone 的設備和測試版 Windows 8 片上運行,並允許我妻子和我輕鬆地跟蹤我們的購物清單。它會讓我們保持兩個通知的購物清單所做的任何更改的即時這樣在任何時候,我們知道正是我們要買。

鑒於這種情況運行視窗電話和基於 Windows 8 的平板電腦不同的設備,與不同口味的 Microsoft 智慧手機。NET 框架和 Windows,我將使用 PCLs 抽象遠離平臺的差異,並使我分享盡可能多的應用程式邏輯,包括所有的盡可能的與 Windows Azure AppFabric 服務匯流排的通信。我還將使用模型-視圖-ViewModel (MVVM) 模式 (bit.ly/GW7l),以便使用相同的模型和 ViewModels 從我們的特定于設備的意見。

可擕式類庫

在過去,在跨平臺開發。NET 框架坎坷。雖然。NET Framework 作為一個跨平臺運行時大夢想,微軟還沒有尚未完全交貨的承諾。如果你已經過嘗試傳遞。基於.NET 框架的應用程式或跨多個設備的框架,您就會注意妨礙了幾件事。

在運行庫側保理業務,程式集版本控制和程式集名稱是不同之間。網路平臺。例如,System.Net.dll 上的。NET 框架,其中包含對等網路的 Api,意味著在 Silverlight,它是其中包含核心網路堆疊上完全不同的東西。在上找到這些 Api。NET 框架,您需要參考 System.dll。程式集版本也不一樣 ; 而對於通過 2.0.0.0 和 4.0.0.0 Silverlight 將採用 2.0.5.0 版本 2.0 到 4。NET 框架版本 2.0 到 4。這些差異,在過去,防止從運行在另一個平臺編譯的程式集。

從一開始 Visual Studio 側右邊,您需要決定哪一目標平臺 — —。NET 框架、 Silverlight 或 Windows Phone。一旦作出這一決定,它是非常難移到或支援新的平臺。例如,如果您已經目標。NET 框架,靶向。NET 框架和 Silverlight 意味著創建新的專案和要麼複製或連結到該專案的現有檔。如果你幸運的話,你可能已考慮您的應用程式,以一種特定于平臺的作品很容易更換。如果不是 (和這是可能會更高),你會需要 # if 平臺機型的每個方法來生成錯誤直到你有清理生成。

這是新的 PCLs 有助於。PCLs,可作為免費附加到 Visual Studio 2010 年 (bit.ly/ekNnsN),並內置 Visual Studio 11 beta 版,提供一種面向多個平臺使用單個專案的簡單方法。您可以創建新的 PCL,選擇您想為目標的框架 (請參見圖 1),並開始編寫代碼。被子,PCL 工具處理 API 的分歧,所以你看到唯一的類和成員,它們可用,並跨所有的框架的工作你所選篩選智慧感知。然後可以引用生成的程式集,並沒有任何變化,所有指定的框架的情況下運行。


圖 1 可擕式類圖書館目標框架

解決方案佈局

組織使用 PCL 一個跨平臺應用程式的典型方式是有一個或多個包含共用的元件的可擕式專案,有特定于平臺的專案,為每個引用這些專案的平臺。我為此應用程式,需要兩個 Visual Studio 解決方案 — — 一個在 Visual Studio 2010 (OnYourWayHome.VS2010) 我的 Windows Phone 應用程式,在 Visual Studio 11 (OnYourWayHome.VS11) 包含我的 Windows 地鐵樣式應用程式中創建一個包含創建。我需要多個解決方案,因為在寫作時,Windows Phone SDK 7.1 工作只有頂部的 Visual Studio 2010,而新的 Windows 8 工具可僅作為 Visual Studio 11 的一部分。(目前) 沒有同時支援單個版本。不要絕望,雖然 ; 在 Visual Studio 11 中可用的新功能將説明我在這兒。我可以打開在早期版本中創建而不必將它們轉換為新格式的大多數專案。這使我有一個單一的 PCL 專案,並從這兩種解決方案中引用它。

數位 23 顯示我的應用程式的專案佈局。在­WayHome.Core,PCL 專案中,包含模型、 查看模型、 共同事務和平臺的抽象。在­WayHome.ServiceBus,也是一個 PCL 專案包含將談談 Windows Azure 的 Api 的便攜版本。這兩個專案在 Visual Studio 2010 的解決方案和 Visual Studio 11 之間共用。OnYourWayHome.Phone 和 OnYourWayHome.Metro 是針對 Windows Phone 7.5 的特定于平臺的專案和。分別淨地鐵樣式的應用程式。這些包含特定于設備的視圖 (如應用程式中的頁) 和 OnYourWayHome.Core 和 OnYourWayHome.ServiceBus 中找到的抽象的實現。


圖 2 Windows Phone 專案佈局在 Visual Studio 2010 年


圖 3 Windows 地鐵樣式應用程式專案佈局在 Visual Studio 中 11

將現有的庫轉換為 PCLs

與 Windows Azure 進行溝通,我下載了從基於 Silverlight 的其餘部分樣品 servicebus.codeplex.com 和它轉換成 PCL 專案。某些庫是轉換比其他人更容易,但你可能會不可避免地遇到一個給定的類型或方法不可用的情況。這裡是一個給定的 API 可能不支援在 PCLs 中的一些典型原因:

API 並不是由傳統的所有平臺實現的。NET 框架檔 IOs,如 System.IO.File 和 System.IO.Directory,屬於此鬥。Silverlight 和 Windows Phone 使用 System.IO.IsolatedStorage Api (雖然不同的。NET 框架版本),而 Windows 8 地鐵樣式的應用程式使用 Windows.Storage。

API 不相容跨所有平臺一些 Api 的外觀和感覺是一樣的但它的硬碟或不可能的可擕式和一致的方式編寫針對他們的代碼。ThreadStaticAttribute,使靜態欄位,在每個執行緒的唯一值,是一個例子。雖然目前的 Windows Phone 和 Xbox 平臺上,其運行時都不支援它。

在未來的平臺,目前的 API 是被認為已過時或遺產這些 Api 包含不太可能的行為或他們已更換過的較新的技術。BackgroundWorker 是一個例子 ; 它被取而代之的任務和新的非同步程式功能,在 Visual Studio 11 beta 版。

我們用完時間最 Api 而不是編寫的銘記的可攜性。我們花了大量的時間通過每個 API,以確保以可移植的方式對可以程式設計。這可能涉及調整或將添加到使可擕式 API。由於的時間和精力參與我們可在 Visual Studio 庫的 PCLs 的第一個版本,我們優先高價值、 高使用的 Api。System.Xml.Linq.dll 和 System.ComponentModel.DataAnnotations.dll 是不是第一版本中可用,但現在都在 Visual Studio 11 beta 版中可用的 Api 的例子。

有幾個不同的方式處理掉進其中一種情形的 API。有時有簡單的更換。例如,有了密切的方法 (Stream.Close、 TextWriter.Close 等) PCL 中不建議使用,並將其替換為處置。在這種情況下,它只是用後者取代前者的調用。但有時它有點難,並進行更多的工作。一種情況下我遇到轉換服務匯流排 Api 時涉及的 HMAC SHA256 雜湊代碼提供程式。不是可用的 PCL 因為 Windows Phone 和地鐵樣式應用程式的加密技術差異。Windows Phone 應用程式使用。基於網路的 Api 來加密,解密和雜湊資料,而地鐵樣式的應用程式使用新的本機 Windows 運行 (WinRT) 的 Api。

特別是,無法生成後轉換是以下代碼:

using (HMACSHA256 sha256 = new HMACSHA256(issuerSecretBytes))
{
  byte[] signatureBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(token));
  signature = Convert.ToBase64String(signatureBytes);
}

為了説明電話加密 Api 和 WinRT 加密 Api 之間的差距,我發明了一個平臺抽象代表服務匯流排的要求。 在此情況下,服務匯流排,需要一種計算 HMAC SHA256 雜湊值:

public abstract class ServiceBusAdapter
{
  public static ServiceBusAdapter Current
  {
    get;
    set;
  }
  public abstract byte[] ComputeHmacSha256(byte[] secretKey, byte[] data);
}

我添加 ServiceBusAdapter 可擕式的專案,以及用於設置的當前的抽象,稍後將成為重要的靜態屬性。 接下來,我創建了 Windows Phone 和 Windows 8 特定 HMAC SHA256 實現的這一抽象,並把這些按各自的專案,如中所示圖 4

圖 4 HMAC SHA256 實現為 Windows Phone 和 Windows 8

// Windows Phone implementation
public class PhoneServiceBusAdapter : ServiceBusAdapter
{
  public override byte[] ComputeHmacSha256(byte[] secretKey, byte[] data)
  {
    using (var cryptoProvider = new HMACSHA256(secretKey))
    {
      return cryptoProvider.ComputeHash(data);
    }
  }
}
// Windows 8 implementation
public class MetroServiceBusAdapter : ServiceBusAdapter
{
  private const string HmacSha256AlgorithmName = "HMAC_SHA256";
  public override byte[] ComputeHmacSha256(byte[] secretKey, byte[] data)
  {
    var provider = MacAlgorithmProvider.OpenAlgorithm(HmacSha256AlgorithmName);
    var key = provider.CreateKey(_secretKey.AsBuffer());
    var hashed = CryptographicEngine.Sign(key, buffer.AsBuffer());
    return hashed.ToArray();
  }
}

時才在 Windows Phone 專案啟動時,我然後"引導"服務匯流排通過作為當前適配器設置的電話特定適配器:

ServiceBusAdapter.Current = new PhoneServiceBusAdapter();

我也是這樣一種 Windows 8 的專案:

ServiceBusAdapter.Current = new MetroServiceBusAdapter();

在地方的一切,我然後更改原始非編譯的代碼,以通過適配器調用:

var adapter = ServiceBusAdapter.Current;
byte[] signatureBytes = adapter.ComputeHmacSha256(issuerSecretBytes, Encoding.UTF8.GetBytes(token));

所以雖然有兩種不同方式的計算雜湊值取決於平臺,可擕式專案會談都使用一個單一的介面。這可能需要一點點的工作,但我可以方便地重用基礎設施,如我跑進了更多的 Api,需要平臺之間的橋接。

請注意,我使用一個靜態屬性訪問並註冊的適配器,使現有的 Api 移到使用適配器容易。如果您使用的一個依賴項注射框架如管理可擴展性框架 (城域)、 統一或 Autofac,你會發現這是天然的平臺特定適配器註冊到容器並"注入"適配器需要它的可擕式元件的容器。

應用版式

我購物清單應用,對你路回家,有兩個簡單的視圖:ShoppingListView,其中顯示當前專案的清單 ; 和 AddGroceryItemView,它允許使用者在清單中添加更多物品。數位 56 的 Windows Phone 版本,這些視圖的顯示。


圖 5 ShoppingListView


圖 6 AddGroceryItemView

ShoppingListView 顯示所有專案,仍未購買,當你漫步在商店附近,您檢查關閉每個專案,將其添加到購物車的想法。後購買專案,按一下簽出導致採取關閉的清單中,指示他們不再需要購買的選中的項。即時共用相同的購物清單的設備 (好吧,因為它背後的網路允許的即時) 看到另一個人所做的更改。

住在特定于平臺的專案的意見主要包含 XAML 和有很少代碼隱藏中限制了您需要兩個平臺之間複製的代碼的數量。視圖使用 XAML 資料綁定,綁定自己提供的命令和運行意見的資料的可擕式 ViewModels。因為有沒有船舶在所有平臺的常見的 UI 框架,PCL 專案不能引用 UI 特定的 Api。然而,針對框架,支援他們,他們可以利用通常由 ViewModels 使用的 Api。這包括使 XAML 資料綁定的工作,如 INotifyPropertyChanged、 ICommand 和 INotifyCollectionChanged 的核心類型。此外,雖然 WinRT XAML 框架不支援他們,添加了 System.ComponentModel.DataAnnotations 和 INotifyDataErrorInfo 的完整性,和這使自訂 XAML 驗證框架,支援可擕式 ViewModels/模型。

數位 78 顯示視圖/ViewModel 相互作用的示例。圖 7 顯示視窗電話版本 AddGroceryItemView 和其綁定的控制項。這些控制項綁定屬性對上的 AddGroceryItemViewModel,如中所示的 Windows Phone 和 Windows 8 的專案,與共享圖 8

圖 7 AddGroceryItemView 控制項視窗電話

<StackPanel>
  <TextBox Text="{Binding Name, Mode=TwoWay}"
           Width="459"
           Height="80" />
  <Button Command="{Binding Add}"
          Content="add"
          Margin="307,5,0,0" />
  <TextBlock Text="{Binding NotificationText}"
             Margin="12,5,0,0"/>
</StackPanel>

圖 8 為 Windows 8 的的 AddGroceryItemViewModel 類

public class AddGroceryItemViewModel : NavigatableViewModel
{
  private string _name;
  private string _notificationText;
  private ICommand _addCommand;
  [...]
  public ICommand AddCommand
  {
    get { return _addCommand ??
(_addCommand = new ActionCommand(Add)); }
  }
  public string Name
  {
    get { return _name ??
String.Empty; }
    set { base.SetProperty(ref _name, value, "Name"); }
  }
  public string NotificationText
  {
    get { return _notificationText ??
string.Empty; }
    set { base.SetProperty(ref _notificationText, value, "NotificationText"); }
  }
}

事件採購

你的方式家基於大量圍繞事件採購的概念 (bit.ly/3SpC9h)。 這個主意是發佈到應用程式的所有狀態更改,存儲為一系列的事件。 在這方面,事件不是指由 C# 事件關鍵字定義的 (雖然這個想法是相同) 的事,而是要具體的類表示單個更改到系統。 這些都通過了所謂的事件的集合,然後通知做工作來回應事件的一個或多個處理常式發佈。 (有關事件聚合的更多資訊,參閱肖恩 · 維爾德穆特文章,"複合 Web 應用程式與棱鏡,"在 msdn.microsoft.com/magazine/dd943055。)

例如,該事件表示雜貨項添加到購物清單看起來類似所示圖 9

圖 9 項添加的事件

// Published when a grocery item is added to a shopping list
[DataContract]
public class ItemAddedEvent : IEvent
{
  public ItemAddedEvent()
  {
  }
  [DataMember]
  public Guid Id
  {
    get;
    set;
  }
  [DataMember]
  public string Name
  {
    get;
    set;
  }
}

ItemAddedEvent 類包含有關事件的資訊:在此情況下,添加了的雜貨項和已用於唯一地表示內購物雜貨項的 ID 的名稱清單。 事件也標有 [DataContract],這使得他們要序列化到磁片或通過網路發送更容易。

此事件是創建和發佈當使用者按一下添加按鈕上 AddGroceryItemView,如圖所示,在圖 10

圖 10 發佈事件

public class AddGroceryItemViewModel : NavigatableViewModel
{
  private readonly IEventAggregator _eventAggregator;
  [...]
  // Adds an item to the shopping list
  private void Add()
  {
    var e = new ItemAddedEvent();
    e.Id = Guid.NewGuid();
    e.Name = Name;
    _eventAggregator.Publish(e);
    NotificationText = String.Format("{0} was added to the shopping list.", Name);
    Name = string.Empty;
  }
}

請注意此方法不會直接進行任何更改了購物清單 ; 它只是發佈的 ItemAddedEvent 事件聚合器。 它是一個事件處理常式聽此事件做些事情的責任。 在此情況下,一類稱為 ShoppingList 贊同並處理事件,如圖所示,在圖 11

圖 11 ShoppingList 類

public class ShoppingList : IEventHandler<ItemAddedEvent>                                        
{
  public ShoppingList(IEventAggregator eventAggregator)
  {
    Requires.NotNull(eventAggregator, "eventAggregator");
    _eventAggregator = eventAggregator;
    _eventAggregator.Subscribe<ItemAddedEvent>(this);
  }
  [...]
  public ReadOnlyObservableCollection<GroceryItem> GroceryItems
  {
    get { return _groceryItems; }
  }
  public void Handle(ItemAddedEvent e)
  {
    var item = new GroceryItem();
    item.Id = e.Id;
    item.Name = e.Name;
    item.IsInCart = false;
    _groceryItems.Add(item);
  }
}
}

每次發佈 ItemAddedEvent,ShoppingList 創建新的 GroceryItem,使用從事件資料,並將其添加到購物清單。ShoppingListView,而間接地綁定到同一清單中通過其 ShoppingListViewModel,也會更新。這意味著當使用者導航到購物清單頁面,他剛添加到清單中的專案都顯示為預期。從購物清單中移除項的過程中,將專案添加到購物車和簽出該購物車是所有處理使用相同的事件發佈訂閱模式。

它可能最初看起來像很多間接定址的簡單地將項添加到購物清單:AddGroceryItemViewModel.Add 方法發佈到 IEventAggregator,而將其傳遞到的 ShoppingList,將其添加到購物清單上的事件。為什麼不會 AddGroceryItemViewModel.Add 方法只是繞過 IEventAggregator 和 ShoppingList 中直接添加新的 GroceryItem?我很高興你問。作為事件的系統治療所有狀態更改的優點是它鼓勵非常鬆散耦合應用程式的所有單個的部分。因為在發佈伺服器和訂閱伺服器不知道彼此,管道中插入一個新的功能如同步資料,並從雲,是簡單得多。。

同步資料到雲

我已經介紹,在單個設備上運行的應用程式的基本功能,但仍有問題的獲取使用者購物清單,其他設備,以使所做的更改,反之亦然。這是 Windows Azure AppFabric 服務匯流排是哪裡來。

Windows Azure AppFabric 服務匯流排是一種功能,可以輕鬆地談得來,在互聯網上,避免複雜的導航資訊溝通障礙如防火牆和網路位址轉譯 (NAT) 設備的應用程式和服務。它提供了 Windows Azure 主辦的休息和 Windows 通訊基礎 (WCF) HTTP 端點,並坐在發佈伺服器和訂閱伺服器之間。

有三種主要的方法,使用 Windows Azure AppFabric 服務匯流排 ; 通信 然而,我的應用程式而言,我就將只涵蓋的主題。全面概述,簽出"介紹到 Windows Azure AppFabric 服務匯流排"在 bit.ly/uNVaXG

對於出版商,服務匯流排主題是類似于在雲大佇列 (請參閱圖 12)。完全不知道誰正在偵聽,出版商推送消息的主題,在他們被拘留無限直到訂閱伺服器所要求的。要獲取消息從佇列,訂戶拉從發佈到相應主題的郵件篩選的訂閱。訂閱行為像特定佇列,並仍從預訂中移除的消息將會看到從其他訂閱中,如果他們自己的篩檢程式,包括它們。


圖 12 服務匯流排主題

在對你的方式家,AzureServiceEventHandler 類是應用程式和服務匯流排之間的橋樑。類似于 ShoppingList,它還實現 IEventHandler <T>,但的特定的事件,而不是 AzureServiceEventHandlers 可以處理它們所有,如圖所示,在圖 13


圖 13 AzureServiceEventHandler 類

public class AzureServiceBusEventHandler : DisposableObject, IEventHandler<IEvent>, IStartupService
{
  private readonly IAzureServiceBus _serviceBus;
  private readonly IAzureEventSerializer _eventSerializer;
  public AzureServiceBusEventHandler(IEventAggregator eventAggregator,
    IAzureServiceBus serviceBus, IAzureEventSerializer eventSerializer)
  {
    _eventAggregator = eventAggregator;
    _eventAggregator.SubscribeAll(this);
    _serviceBus = serviceBus;
    _serviceBus.MessageReceived += OnMessageReceived;
    _eventSerializer = eventSerializer;
  }
  [...]
  public void Handle(IEvent e)
  {
    BrokeredMessage message = _eventSerializer.Serialize(e);
    _serviceBus.Send(message);
  }
}

每個使用者進行購物清單的狀態的更改是由 AzureServiceBusEventHandler 處理,直接推向雲。 既不是 AddGroceryItemViewModel,出版事件,也不是 ShoppingList,負責處理它在本地設備上,是意識到這種情況發生。

從雲回旅行是其中一種基於事件的體系結構是值得的。 AzureServiceEventHandler 檢測 (通過 IAzureServiceBus.MessageReceived C# 事件) 的服務匯流排上已收到新郵件時,它不會的較早前做反向,回事件,將反序列化所收到的郵件。 從這裡,它獲取發佈回通過事件聚合器,使它被視為好像事件是來自在應用程式中,如中所示圖 14

圖 14 反序列化收到的郵件

public class AzureServiceBusEventHandler : DisposableObject, IEventHandler<IEvent>,
  IStartupService
{
  private readonly IAzureServiceBus _serviceBus;
  private readonly IAzureEventSerializer _eventSerializer;
  [...]
  private void OnMessageReceived(object sender, MessageReceivedEventArgs args)
  {
    IEvent e = _eventSerializer.Deserialize(args.Message);
    _eventAggregator.Publish(e);
  }
}

ShoppingList 不知道 (也不關心的它) 有關的事件和處理那些來自服務匯流排雲,仿佛他們直接來自使用者的輸入源。它更新其清單的食品,並從而引起任何更新以及綁定到該清單的視圖。

如果你特別注意,您可能會注意在工作流的一個小問題:從本地設備獲取發送到雲計算的事件回到該同一設備,並導致資料重複。更糟的是,其他、 無關的購物清單的更改也會對該設備。不了解你,但我敢肯定我不想看到其他人的食物選擇出現在我的購物清單。要防止出現這種情況,每個清單和每個設備,進行偵聽的主題訂閱創建服務匯流排主題。當從設備情況下,消息發佈到主題時,連同訂閱篩選器使用排除來自其自身設備的消息的消息,發送包含設備 ID 屬性。圖 15 顯示此工作流。


圖 15 設備到設備工作流

接近尾聲了

在這篇文章,我很多涵蓋:可擕式類庫簡化我的解決方案,大大減少需要寫入目標兩個平臺的代碼的數量。同時,更改應用程式狀態通過事件作出很容易同步雲計算該狀態。仍有很多,我已經向左愚鈍,不過,你要時發展持續的用戶端中的因數。我沒有談到離線事件緩存和容錯能力 (如果網路不可用時我發佈的事件?),合併衝突 (如果另一個使用者使與煤礦衝突的更改?),播放 (如果我將新設備附加到購物清單,如何不會它獲取更新?),訪問的控制 (我如何防止未經授權的使用者訪問的資料,他們不應該?),最後持久性。在文章的示例代碼中,應用程式不會保存發射之間的購物清單。我將把這個作為練習你 ; 如果你想玩代碼,它可能是一個有趣的挑戰。天真 (或相當傳統) 即將來臨的持久性的方式付諸表決可直接進入 ShoppingList 類鉤、 標記為可序列化的 GroceryItem 物件和關閉將它們保存到一個檔。前走這條路線,不過,停止,並認為這件事:ShoppingList 已經以本機方式處理的事件,已經不在乎他們從哪裡來,同步資料,並從雲像竟然保存和恢復資料從磁片,不是嗎?

David Kean 是一個開發者上。在 Microsoft,他在基類庫 (BCL) 團隊工作網框架團隊。在這之前,他從事經常魅力,但也極大地被誤解的 FxCop 工具和其相關的一個同級 Visual Studio 代碼分析。最初從墨爾本,澳大利亞,他現在設在西雅圖、 華盛頓州,與他的妻子露西和三個孩子,傑克、 冬和本。他可以上找到博客 davesbox.com

多虧了以下的技術專家審查這篇文章:Nicholas Blumhardt 和  Immo Landwerth