2017-05-03 110 views
2

我有一个Azure移动应用程序服务,并且PullAsync正在悄悄失败。Azure移动服务:表序列化

在后端,DTO的是这样的:

type TablesDto() = 
    [<JsonProperty("id")>] member val Id = String.Empty with get, set 
    [<JsonProperty("deleted")>] member val Deleted = false with get, set 
    [<JsonProperty("createdAt")>] member val CreatedAt = Nullable<DateTimeOffset>() with get, set 
    [<JsonProperty("updatedAt")>] member val UpdatedAt = Nullable<DateTimeOffset>() with get, set 
    [<JsonProperty("version")>] member val Version = [||] with get, set 
    interface ITableData with 
     member this.Id with get() = this.Id and set(value) = this.Id <- value 
     member this.Deleted with get() = this.Deleted and set(value) = this.Deleted <- value 
     member this.CreatedAt with get() = this.CreatedAt and set(value) = this.CreatedAt <- value 
     member this.UpdatedAt with get() = this.CreatedAt and set(value) = this.UpdatedAt <- value 
     member this.Version with get() = this.Version and set(value) = this.Version <- value 

type MyEntityDto() = 
    inherit TablesDto() 
    [<JsonProperty("entityName")>] member val EntityName = String.Empty with get, set 

我使用的是DomainManager从我的数据库架构对象映射到这个DTO形式,但我不知道这是相关的。

在前端(在Xamarin Android应用)时,DTO被定义为

type MyEntityDto() = 
    [<JsonProperty("id")>] member val Id = String.Empty with get, set 
    [<JsonProperty("version"); AzureVersion>] member val Version = Unchecked.defaultof<String> with get, set 
    [<JsonProperty("createdAt")>] member val CreatedUtc = DateTime.MinValue with get, set 
    [<JsonProperty("updatedAt")>] member val UpdatedUtc = DateTime.MinValue with get, set 
    [<JsonProperty("entityName")>] member val EntityName = String.Empty with get, set 

出于调试目的,我已经使用了下列处理程序来检查从服务器回来的JSON:

#if DEBUG 
type DebuggingHandler() = 
    inherit DelegatingHandler() 
    let deserialise content = 
     try 
      let deserialised = JsonConvert.DeserializeObject<MyEntitypDto[]>(content) 
      deserialised |> ignore 
     with 
      | ex -> 
       ex |> ignore 
    override this.SendAsync(message, cancellationToken) = 
     let sendAsync = base.SendAsync(message, cancellationToken) 
     async { 
      let! response = sendAsync |> Async.AwaitTask 
      let! content = response.Content.ReadAsStringAsync() |> Async.AwaitTask 
      deserialise content 
      return response 
     } |> Async.StartAsTask 
#endif 

检查在调试器中content值显示这一点:

[{"[email protected]":"Harry","[email protected]":"2","[email protected]":true,"[email protected]":"2017-04-26T11:20:46.83Z","[email protected]":"2017-04-26T11:20:46.83Z","[email protected]":"AAAAAAAAO/Y="}, 
{"[email protected]":"Gary","[email protected]":"3","[email protected]":false,"[email protected]":"2017-04-26T11:23:05.16Z","[email protected]":"2017-04-26T11:23:05.16Z","[email protected]":"AAAAAAAAO/c="}] 

这个“@”符号后缀是怎么回事?它可以防止对象在调试器中解串行,我怀疑这就是为什么PullAsync方法正在回收。

这是F#的一些奇怪的副作用吗?我怎样才能摆脱这些“@”符号(如果这是什么导致我的表同步中断)?

编辑我已经添加了C#标记,因为这可能与典型企业环境中F#和C#之间的区别有关。

+2

这些是您的属性的内部后备字段的名称。出于某种原因,即使您的JSON序列化程序使用属性进行了修饰,但它们仍可以使用字段而不是属性。 –

+0

感谢Fyodor。这使我能够找到答案。 TableSync现在正在正常工作。 –

回答

2

Fyodor的评论是我需要找到答案的提示。

在Azure移动应用程序中,带有表控制器的JSON格式器无法被信任,无法正确地对F#对象进行序列化。所以,你需要以下的定制件:

  1. 创建使用JSON.Net格式化:

    type JsonDotNetFormatter() as this = 
        inherit MediaTypeFormatter() 
        do this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json")) 
        let settings = new JsonSerializerSettings(ReferenceLoopHandling = ReferenceLoopHandling.Ignore) 
        do settings.ContractResolver <- new CamelCasePropertyNamesContractResolver() 
        override __.CanReadType _ = true 
        override __.CanWriteType _ = true 
        override __.ReadFromStreamAsync(t, readStream, _, _) = 
         async { 
          use reader = new StreamReader(readStream) 
          let! text = reader.ReadToEndAsync() |> Async.AwaitTask 
          return JsonConvert.DeserializeObject(text, t) 
         } |> Async.StartAsTask 
        override __.WriteToStreamAsync(_, value, writeStream, _, _) = 
         async { 
          match box value with 
          | null -> value |> ignore 
          | _ -> 
           let text = JsonConvert.SerializeObject(value, settings) 
           use writer = new StreamWriter(writeStream) 
           do! writer.WriteAsync(text) |> Async.AwaitTask 
         } |> Async.StartAsTask :> Task 
    
  2. 创建一个配置提供商,在Startup.MobileApp.cs使用:

    public class JsonDotNetConfigProvider : TableControllerConfigProvider 
    { 
        public override void Configure(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor) 
        { 
         base.Configure(controllerSettings, controllerDescriptor); 
         controllerSettings.Formatters.Insert(0, new JsonDotNetFormatter()); 
        } 
    } 
    
  3. 将配置提供程序添加到Startup.Configure()方法中的移动应用程序配置中:

    如果碰上无关的F#等通用序列化问题
    new MobileAppConfiguration() 
        .AddTablesWithEntityFramework() 
        .WithTableControllerConfigProvider(new JsonDotNetConfigProvider()) 
        .ApplyTo(config); 
    

这也应该工作。在这个例子中,我混合使用了C#和F#,但如果您想编写基于F#的企业级软件,那么您可能需要做某些事情。