2015-09-04 58 views
4

我正在使用Table Storage創建一個基本的(我的第一個)Azure移動服務來控制一個簡單的事件應用程序。我的數據對象由2種對象類型:CoordinatorEvent,我想協調員是一個單獨的表來存儲,我不希望它非規範化的活動中的特定信息,但事件也有一個內部對象的位置來存儲該事件的詳細信息地點,但我想存儲非規範化的,因爲我不想將這些細節與事件分開保存。Azure移動服務TableController不返回內部對象

這裏我到目前爲止的對象: DataObjests:

public class Coordinator : EntityData { 
    public string Name { get; set; } 
    public int Points { get; set; } 
    public bool IsActive { get; set; } 
} 
public class Event: EntityData { 
     public Coordinator Coordinator { get; set; } 
     public DateTime EventDate { get; set; } 
     public int Attendees { get; set; } 
     public Location Location { get; set; } 
} 
public class Location { 
     public string Name { get; set; } 
     public string Address1 { get; set; } 
     public string Address2 { get; set; } 
     public string City { get; set; } 
     public string County { get; set; } 
     public string PostCode { get; set; } 
} 

該控制器由VS的腳手架生成的基本TableController,我做出的唯一改變是暴露MobileServiceContext的事件控制器,使郵政從客戶端

public class EventController : TableController<Event> { 
     private MobileServiceContext context; 
     protected override void Initialize(HttpControllerContext controllerContext) { 
      base.Initialize(controllerContext); 
      context = new MobileServiceContext(); 
      DomainManager = new EntityDomainManager<Event>(context, Request, Services); 
     } 
     protected override void Dispose(bool disposing) { 
      context?.Dispose(); 
      base.Dispose(disposing); 
     } 

     public IQueryable<Event> GetAllEvent() { 
      return Query(); 
     } 

     public async Task<IHttpActionResult> PostEvent(Event item) { 
      var coordinator = context.Coordinators.Find(item.Coordinator.Id); 
      item.Coordinator = coordinator; 
      Event current = await InsertAsync(item); 
      return CreatedAtRoute("Tables", new { id = current.Id }, current); 
     } 

} 

發佈數據正常工作,我有一個協調員:方法來保存爲客戶端將只發布協調員的ID時,發現現有的協調員

ID Name Points IsActive Version CreatedAt UpdatedAt Deleted 
cdf96b0f93644f1e945bd16d63aa96e0 John Smith 10 True 0x00000000000007D2 04/09/2015 09:15:02 +00:00 04/09/2015 09:15:02 +00:00 False 
f216406eb9ad4cdcb7b8866fdf126727 Rebecca Jones 10 True 0x00000000000007D4 04/09/2015 09:15:30 +00:00 04/09/2015 09:15:30 +00:00 False 

和相關的第一協調員事件:用正確的數據表

Id EventDate Attendees Location_Name Location_Address1 Location_Address2 Location_City Location_County Location_PostCode Version CreatedAt UpdatedAt Deleted Coordinator_Id 
961abbf839bf4e3481ff43a214372b7f 04/11/2015 09:00:00 0 O2 Arena Peninsula Square  London  SE10 0DX 0x00000000000007D6 04/09/2015 09:18:11 +00:00 04/09/2015 09:18:11 +00:00 False cdf96b0f93644f1e945bd16d63aa96e0 

此時一切看起來不錯,我的2個問題都與事件的獲取,其中兩個CoordinatorLocation對象不回來,我EventController我們得到的JSON是簡單:

[{"id":"961abbf839bf4e3481ff43a214372b7f","attendees":0,"eventDate":"2015-11-04T09:00:00Z"}] 

所以我的2個問題是:

1)定位對象由EventController在服務器上正確加載,我可以看到,如果我回國之前打破正確加載的特性,什麼我看起來像一個JSON序列化的問題,但我已經試圖改變串行的配置(在WebApiConfig)沒有太大的影響,我試過的最後一個選項是MaxDepth,但仍然沒有返回Location對象。

2)協調器對象我根本沒有加載到服務器上,甚至沒有Id(它正確存儲在表上),所以我不能強制整個對象的加載,當然也不會返回給客戶。

關於我在做什麼錯誤的任何想法?

在此先感謝

克萊頓洛瓦託

回答

4

這是TableController默認行爲。 爲了達到您的要求,以時尚的方式,您應該在您的控制器中實施OData $ expand

本文提供了一個良好的步行整個的解決方案
Retrieving data from 1:n relationship using .NET backend Azure Mobile Services

隨着進一步的延伸,我已經實現了一個自定義屬性,你可以在你的控制器方法使用指定客戶端可以請求的屬性擴大。您可能不希望總是返回的所有子關係(擴大對象)

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] 
public class ExpandablePropertyAttribute : ActionFilterAttribute 
{ 
    #region [ Constants ] 
    private const string ODATA_EXPAND = "$expand="; 
    #endregion 

    #region [ Fields ] 
    private string _propertyName; 
    private bool _alwaysExpand; 
    #endregion 

    #region [ Ctor ] 
    public ExpandablePropertyAttribute(string propertyName, bool alwaysExpand = false) 
    { 
     this._propertyName = propertyName; 
     this._alwaysExpand = alwaysExpand; 
    } 
    #endregion 

    #region [ Public Methods - OnActionExecuting ] 
    public override void OnActionExecuting(HttpActionContext actionContext) 
    { 
     base.OnActionExecuting(actionContext); 
     var uriBuilder = new UriBuilder(actionContext.Request.RequestUri); 
     var queryParams = uriBuilder.Query.TrimStart('?').Split(new char[1] { '&' }, StringSplitOptions.RemoveEmptyEntries).ToList(); 
     int expandIndex = -1; 

     for (var i = 0; i < queryParams.Count; i++) 
     { 
      if (queryParams[i].StartsWith(ODATA_EXPAND, StringComparison.Ordinal)) 
      { 
       expandIndex = i; 
       break; 
      } 
     } 

     if (expandIndex >= 0 || this._alwaysExpand) 
     { 
      if (expandIndex < 0) 
      { 
       queryParams.Add(string.Concat(ODATA_EXPAND, this._propertyName)); 
      } 
      else 
      { 
       queryParams[expandIndex] = queryParams[expandIndex] + "," + this._propertyName; 
      } 

      uriBuilder.Query = string.Join("&", queryParams); 
      actionContext.Request.RequestUri = uriBuilder.Uri; 
     } 

    } 
    #endregion 
} 

我在我的控制器以這種方式使用:

[ExpandableProperty("Documents", false)] 
    public IQueryable<ClientActivity> GetAllClientActivities() 
    { 
     return Query(); 
    } 
+0

馬西莫嗨,非常感謝,這很有道理,現在我可以將協調員項目發送給客戶端,不幸的是,這不適用於我的「位置」對象。我仍然很懷疑序列化與$展開內部'協調員'對象是序列化,但'位置'不是... –

+0

怪異的,不知道到底爲什麼...我看到你的類只是使用簡單類型,所以這兩個實體應該以相同的方式工作(序列化)。我已經添加了一些更多的代碼來使用自定義屬性以更高級的方式管理它。希望對你有所幫助 –

+2

您好Massimo,非常感謝您的幫助,它確實幫助我確定了我做錯了什麼,並且Coordinator對象現在正在使用您的代碼正確返回。 Location對象仍然沒有返回,但看起來這是SDK上的OData實現的一個錯誤。 (https://social.msdn.microsoft.com/Forums/azure/en-US/1106f041-46b7-4772-8da7-ff399a43fd86/azure-mobile-services-unable-to-retrieve-complextype-from-net-backend ?forum = azuremobile)與MSDN上的相同問題。我現在將嘗試爲此找到解決方法,因爲我不想創建一個新實體來存儲位置 –

1

我已成功地使複雜的屬性被序列化併發送回給客戶。也許這個解決方案不是最乾淨的,但它對我來說很有用。希望別人也會覺得它有用。

首先,從EnableQueryAttribute創建一個繼承一個類並重寫GetModel方法是這樣的:

public class CustomEnableQueryAttribute : EnableQueryAttribute 
{ 
    public override IEdmModel GetModel(Type elementClrType, HttpRequestMessage request, HttpActionDescriptor actionDescriptor) 
    { 
     var modelBuilder = new ODataConventionModelBuilder(); 
     modelBuilder.EntitySet<Event>("Events"); 

     return modelBuilder.GetEdmModel(); 
    } 
} 

然後,在你的控制器的Initialize方法,添加這些行:

protected override void Initialize(HttpControllerContext controllerContext) 
    { 
     base.Initialize(controllerContext); 
     context = new MobileServiceContext(); 
     DomainManager = new EntityDomainManager<Event>(context, Request, Services); 

     //Add these lines 
     var service = Configuration.Services.GetServices(typeof(IFilterProvider)).ToList(); 
     service.Remove(service.FirstOrDefault(f => f.GetType() == typeof(TableFilterProvider))); 
     service.Add(new TableFilterProvider(new CustomEnableQueryAttribute())); 
     Configuration.Services.ReplaceRange(typeof(IFilterProvider), service.ToList().AsEnumerable()); 

     Request.Properties.Add("MS_IsQueryableAction", true); 
    }