Azure移动服务:表的序列化

时间:2017-05-03 17:37:24

标签: c# android azure f# azure-mobile-services

我有一个Azure移动应用服务,PullAsync无声地失败。

在后端,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值显示:

[{"myEntityName@":"Harry","id@":"2","deleted@":true,"createdAt@":"2017-04-26T11:20:46.83Z","updatedAt@":"2017-04-26T11:20:46.83Z","version@":"AAAAAAAAO/Y="},
{"myEntityName@":"Gary","id@":"3","deleted@":false,"createdAt@":"2017-04-26T11:23:05.16Z","updatedAt@":"2017-04-26T11:23:05.16Z","version@":"AAAAAAAAO/c="}]

这个“@”符号后缀发生了什么?它可以防止对象在调试器中反序列化,我怀疑这就是PullAsync方法回收的原因。

这是F#的一些奇怪的副作用吗?如何摆脱这些“@”符号(如果这是导致我的表同步中断的原因)?

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

1 个答案:

答案 0 :(得分:2)

费奥多尔的评论是我需要找到答案的提示。

在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()方法将配置提供程序添加到您的移动应用配置中:

    new MobileAppConfiguration()
        .AddTablesWithEntityFramework()
        .WithTableControllerConfigProvider(new JsonDotNetConfigProvider())
        .ApplyTo(config);
    
  4. 如果您遇到与F#无关的其他通用序列化问题,这也应该有效。我在这个例子中混合了C#和F#,但如果你想编写基于F#的企业级软件,那么你可能需要做的事情。