C# 在Azure Mobile App中使用移动客户端更新行时出错

C# 在Azure Mobile App中使用移动客户端更新行时出错,c#,xamarin.android,azure-mobile-services,C#,Xamarin.android,Azure Mobile Services,需要在我们创建的移动应用程序中添加编辑功能 我们使用此版本中的代码来解决版本冲突(即始终使用客户端的版本) 但是,编辑功能有时会起作用,并且在大多数情况下也会导致错误。有一次服务器中的数据已更新,但移动客户端中仍保留了即将进行的操作 我们已经查看了客户端中的异常,消息只是“错误已发生”。还查看了服务器分析,结果代码是500 我有三个问题: 为什么更新操作会导致错误 在客户端或服务器中调试错误是否有其他方法?错误500是非常普遍的,“错误已经发生”不是很有帮助 即使服务器中的销售模型与相应的sql

需要在我们创建的移动应用程序中添加编辑功能

我们使用此版本中的代码来解决版本冲突(即始终使用客户端的版本)

但是,编辑功能有时会起作用,并且在大多数情况下也会导致错误。有一次服务器中的数据已更新,但移动客户端中仍保留了即将进行的操作

我们已经查看了客户端中的异常,消息只是“错误已发生”。还查看了服务器分析,结果代码是500

我有三个问题:

  • 为什么更新操作会导致错误
  • 在客户端或服务器中调试错误是否有其他方法?错误500是非常普遍的,“错误已经发生”不是很有帮助
  • 即使服务器中的销售模型与相应的sql数据库之间存在差异,客户端是否可以创建销售并将其上载到服务器
  • 更新

    public async Task<Sale> UpdateSaleAsync(Sale sale)
    {
      await saleTable.UpdateAsync(sale);
      return sale;
    }
    
    public async Task<bool> SyncSalesAsync()
    {
        bool wasPushed = true;
    
        try
        {
            // Sync data with cloud
            await MobileService.SyncContext.PushAsync();
            await saleTable.PullAsync("allSales", saleTable.CreateQuery());
        }
        catch (MobileServicePreconditionFailedException<Sale> conflict)
        {
            Console.WriteLine($"taskTitle_Changed - Conflict Resolution for item ${conflict.Item.Id}");
        }
        catch (MobileServicePushFailedException exc)
        {
            Console.WriteLine("Sync Sales MSPFE Exception: ");
            Console.WriteLine("/////////////////////");
            Console.WriteLine("Message:");
            Console.WriteLine(exc.Message);
            Console.WriteLine("HelpLink:");
            Console.WriteLine(exc.HelpLink);
            Console.WriteLine("Source:");
            Console.WriteLine(exc.Source);
            Console.WriteLine("Stack Trace:");
            Console.WriteLine(exc.StackTrace);
            Console.WriteLine("/////////////////////");
    
            if (exc.PushResult != null)
            {
                var c = 1;
    
                foreach (var i in exc.PushResult.Errors)
                {
                    Console.WriteLine("Inside push Details: " + c);
                    Console.WriteLine("Handled: ");
                    Console.WriteLine(i.Handled);
                    Console.WriteLine("Item");
                    Console.WriteLine(i.Item);
                    Console.WriteLine("O Kind");
                    Console.WriteLine(i.OperationKind);
                    Console.WriteLine("Status");
                    Console.WriteLine(i.Status);
                    Console.WriteLine("Table Name");
                    Console.WriteLine(i.TableName);
                    Console.WriteLine("Raw Result");
                    Console.WriteLine(i.RawResult);
                    Console.WriteLine("Result");
                    Console.WriteLine(i.Result);
                    Console.WriteLine("Item");
                    Console.WriteLine(i.Item);
                    c++;
    
                    Console.WriteLine("Cast Result to Sale");
                    var serverItem = i.Result.ToObject<Sale>();
                    Console.WriteLine("Cast Item to Sale");
                    var localItem = i.Item.ToObject<Sale>();
    
                    if (serverItem.Equals(localItem))
                    {
                        Console.WriteLine("server item equals");
                        // Items are the same, so ignore the conflict
                        await i.CancelAndDiscardItemAsync();
                    }
                    else
                    {
                        Console.WriteLine("else");
                        Console.WriteLine("localitem version: " + localItem.Version);
                        Console.WriteLine("serveritem version: " + serverItem.Version);
                        // Always take the client
                        localItem.Version = serverItem.Version ?? localItem.Version;
    
                        var item = JObject.FromObject(localItem);
                        Console.WriteLine("item from jobject");
                        Console.WriteLine(item);
                        try
                        {
                            await i.UpdateOperationAsync(item);
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine("Else Message Error");
                            Console.WriteLine(e.Message);
                            Console.WriteLine("Else Stack Trace");
                            Console.WriteLine(e.StackTrace);
    
                        } 
                    }
                }
            }
            return false;
        }
    
        catch (MobileServiceInvalidOperationException msioe)
        {
            Console.WriteLine("Sync Sales MSIOE Exception: ");
            Console.WriteLine("/////////////////////");
            Console.WriteLine(msioe.Message);
            Console.WriteLine("----");
            Console.WriteLine(msioe.HelpLink);
            Console.WriteLine("----");
            Console.WriteLine(msioe.Source);
            Console.WriteLine("----");
            Console.WriteLine(msioe.StackTrace);
            return false;
        }
        catch (Exception e)
        {
            Console.WriteLine("Sync Sales General Exception: ");
            Console.WriteLine("/////////////////////");
            Console.WriteLine(e.Message);
            Console.WriteLine("----");
            Console.WriteLine(e.HelpLink);
            Console.WriteLine("----");
            Console.WriteLine(e.Source);
            Console.WriteLine("----");
            Console.WriteLine(e.StackTrace);
            return false;
        }
    
        return wasPushed;
    }
    
    打开服务器中的日志记录,将PatchSale更改为async,以便我们可以等待UpdateAsync(id,patch),并在调用await UpdateAsync的位置放置一个try catch

    以下是捕获区中记录的内容:

    CATCH:
    Helplink
    
    Message
    Processing of the HTTP request resulted in an exception. Please see the HTTP response returned by the 'Response' property of this exception for details.
    StackTrace
    at Microsoft.Azure.Mobile.Server.Tables.EntityUtils.<SubmitChangesAsync>d__0.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at Microsoft.Azure.Mobile.Server.EntityDomainManager`1.<UpdateAsync>d__10.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at Microsoft.Azure.Mobile.Server.EntityDomainManager`1.<UpdateAsync>d__3.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at Microsoft.Azure.Mobile.Server.TableController`1.<PatchAsync>d__12.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at SynthesisServer.Controllers.SaleController.<PatchSale>d__3.MoveNext()
    Source
    Microsoft.Azure.Mobile.Server.Entity
    
    SQL Server:Sale列(不知道除了这些列之外还有什么要显示)

    代码500是“无效服务器响应”,通常是“请求导致服务器代码崩溃”。要诊断此问题,您需要进入Azure门户并打开诊断日志记录,然后查看日志流。如果可以,请通过远程调试器从VisualStudio进行连接(有关这方面的一些有用提示,请参阅第8章)


    通过查看代码,我看到了一些问题—例如,使用DateTime而不是DateTimeOffset。但是,这些都不会导致崩溃,因此我怀疑ASP.NET服务器和SQL server在模型定义方面不匹配。但是,您没有提供足够的信息来明确说明这一点。

    在我的情况下,在服务器端使用下面的类似方式使用下面的FK关系似乎不起作用。在我的客户机对象和服务器对象之间,唯一的区别是标记属性。如果将其从服务器对象中删除,则更新工作正常。我不知道Adrian Hall是如何在他的github示例中给出这个示例的


    嗨,阿德里安·霍尔。我已经更新了这个问题,并在.NET服务器和SQL server中的列中发布了销售模型(除了列之外,不知道还要为模型定义显示什么)。我唯一能看到的是您的CreatedAt/UpdatedAt字段需要是DateTimeOffset,而不是DateTime。您可以使用Postman重新创建错误-更新是对表的补丁调用(/tables/sale)。我看到一堆外键。这些是必需的还是可选的?如果SQL server返回错误,自然会导致代码出错。做标准的EF SQL日志记录(我在书中也展示了如何做)并看看它说了些什么。@AdrianHall你能看看我上面的答案吗?这是答案,也是问题。谢谢你你有没有找到解决办法?我有完全相同的问题,我不知道问题是什么。请给我一些提示,可能是什么问题
    public async Task<bool> SyncSalesAsync()
    {
        bool wasPushed = true;
    
        try
        {
            // Sync data with cloud
            await MobileService.SyncContext.PushAsync();
            await saleTable.PullAsync("allSales", saleTable.CreateQuery());
        }
        catch (MobileServicePreconditionFailedException<Sale> conflict)
        {
            Console.WriteLine($"taskTitle_Changed - Conflict Resolution for item ${conflict.Item.Id}");
        }
        catch (MobileServicePushFailedException exc)
        {
            Console.WriteLine("Sync Sales MSPFE Exception: ");
            Console.WriteLine("/////////////////////");
            Console.WriteLine("Message:");
            Console.WriteLine(exc.Message);
            Console.WriteLine("HelpLink:");
            Console.WriteLine(exc.HelpLink);
            Console.WriteLine("Source:");
            Console.WriteLine(exc.Source);
            Console.WriteLine("Stack Trace:");
            Console.WriteLine(exc.StackTrace);
            Console.WriteLine("/////////////////////");
    
            if (exc.PushResult != null)
            {
                var c = 1;
    
                foreach (var i in exc.PushResult.Errors)
                {
                    Console.WriteLine("Inside push Details: " + c);
                    Console.WriteLine("Handled: ");
                    Console.WriteLine(i.Handled);
                    Console.WriteLine("Item");
                    Console.WriteLine(i.Item);
                    Console.WriteLine("O Kind");
                    Console.WriteLine(i.OperationKind);
                    Console.WriteLine("Status");
                    Console.WriteLine(i.Status);
                    Console.WriteLine("Table Name");
                    Console.WriteLine(i.TableName);
                    Console.WriteLine("Raw Result");
                    Console.WriteLine(i.RawResult);
                    Console.WriteLine("Result");
                    Console.WriteLine(i.Result);
                    Console.WriteLine("Item");
                    Console.WriteLine(i.Item);
                    c++;
    
                    Console.WriteLine("Cast Result to Sale");
                    var serverItem = i.Result.ToObject<Sale>();
                    Console.WriteLine("Cast Item to Sale");
                    var localItem = i.Item.ToObject<Sale>();
    
                    if (serverItem.Equals(localItem))
                    {
                        Console.WriteLine("server item equals");
                        // Items are the same, so ignore the conflict
                        await i.CancelAndDiscardItemAsync();
                    }
                    else
                    {
                        Console.WriteLine("else");
                        Console.WriteLine("localitem version: " + localItem.Version);
                        Console.WriteLine("serveritem version: " + serverItem.Version);
                        // Always take the client
                        localItem.Version = serverItem.Version ?? localItem.Version;
    
                        var item = JObject.FromObject(localItem);
                        Console.WriteLine("item from jobject");
                        Console.WriteLine(item);
                        try
                        {
                            await i.UpdateOperationAsync(item);
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine("Else Message Error");
                            Console.WriteLine(e.Message);
                            Console.WriteLine("Else Stack Trace");
                            Console.WriteLine(e.StackTrace);
    
                        } 
                    }
                }
            }
            return false;
        }
    
        catch (MobileServiceInvalidOperationException msioe)
        {
            Console.WriteLine("Sync Sales MSIOE Exception: ");
            Console.WriteLine("/////////////////////");
            Console.WriteLine(msioe.Message);
            Console.WriteLine("----");
            Console.WriteLine(msioe.HelpLink);
            Console.WriteLine("----");
            Console.WriteLine(msioe.Source);
            Console.WriteLine("----");
            Console.WriteLine(msioe.StackTrace);
            return false;
        }
        catch (Exception e)
        {
            Console.WriteLine("Sync Sales General Exception: ");
            Console.WriteLine("/////////////////////");
            Console.WriteLine(e.Message);
            Console.WriteLine("----");
            Console.WriteLine(e.HelpLink);
            Console.WriteLine("----");
            Console.WriteLine(e.Source);
            Console.WriteLine("----");
            Console.WriteLine(e.StackTrace);
            return false;
        }
    
        return wasPushed;
    }
    
    public class Sale
        {
            [JsonProperty(PropertyName = "id")]
            public string Id { get; set; }
    
            [JsonProperty(PropertyName = "productId")]
            public string ProductId { get; set; }
    
            [JsonProperty(PropertyName = "promoterId")]
            public string PromoterId { get; set; }
    
            [JsonProperty(PropertyName = "storeId")]
            public string StoreId { get; set; }
    
            [JsonProperty(PropertyName = "paymentMethodId")]
            public string PaymentMethodId { get; set; }
    
            [JsonProperty(PropertyName = "corporateSale")]
            public bool CorporateSale { get; set; }
    
            [JsonProperty(PropertyName = "dateSold")]
            public DateTime? DateSold { get; set; }
    
            [JsonProperty(PropertyName = "priceSold")]
            public double PriceSold { get; set; }
    
            [JsonProperty(PropertyName = "quantitySold")]
            public int QuantitySold { get; set; }
    
            [JsonProperty(PropertyName = "remarks")]
            public string Remarks { get; set; }
    
            [JsonProperty(PropertyName = "deleted")]
            public bool Deleted { get; set; }
    
            [JsonProperty(PropertyName = "createdAt")]
            public DateTime CreatedAt { get; set; }
    
            [JsonProperty(PropertyName = "updatedAt")]
            public DateTime UpdatedAt { get; set; }
    
            [JsonProperty(PropertyName = "version")]
            public string Version { get; set; }
    
            [JsonProperty(PropertyName = "saleTransactionId")]
            public string SaleTransactionId { get; set; }
    
            [JsonIgnore]
            public virtual Dictionary<string, string> Data
            {
                get
                {
                    var data = new Dictionary<string, string>
                    {
                        ["Id"] = Id,
                        ["ProductId"] = ProductId,
                        ["PromoterId"] = PromoterId,
                        ["StoreId"] = StoreId,
                        ["PaymentMethodId"] = StoreId,
                        ["CorporateSale"] = CorporateSale.ToString(),
                        ["DateSold"] = "",
                        ["PriceSold"] = PriceSold.ToString(),
                        ["QuantitySold"] = QuantitySold.ToString(),
                        ["Remarks"] = Remarks,
                        ["SaleTransactionId"] = SaleTransactionId,
                        ["Deleted"] = Deleted.ToString(),
                        ["CreatedAt"] = CreatedAt.ToString(),
                        ["UpdatedAt"] = UpdatedAt.ToString(),
                        ["Version"] = Version
                    };
    
                    if (DateSold != null) data["DateSold"] = ((DateTime)DateSold).ToString();
    
                    return data;
                }
    
            }
    
            [JsonIgnore]
            public bool IsNew
            {
                get
                {
                    return string.IsNullOrEmpty(PromoterId) || UpdatedAt == null || CreatedAt == null || string.IsNullOrEmpty(Version);
                }
            }
    
            public virtual Product Product { get; set;}
            public virtual Store Store { get; set; }
            public virtual PaymentMethod PaymentMethod { get; set; }
    
            // Default constructor
            public Sale() {}
    
            public Sale(Dictionary<String, String> data)
            {
                DateSold = DateTime.Parse(data["DateSold"]);
                CorporateSale = bool.Parse(data["CorporateSale"]);
                ProductId = data["ProductId"];
                PriceSold = Double.Parse(data["PriceSold"]);
                QuantitySold = int.Parse(data["QuantitySold"]);
                StoreId = data["StoreId"];
                PaymentMethodId = data["PaymentMethodId"];
                Remarks = data["Remarks"];
    
                SaleTransactionId = Guid.NewGuid().ToString();
            }
    
            public virtual string TransactionId()
            {
                string value = "Not Synced";
    
                if (!string.IsNullOrEmpty(SaleTransactionId)) value = SaleTransactionId;
    
                return value;
            }
    
            public override string ToString()
            {
                return "I'm a Sale: DateSold " + DateSold + " ProductID " + ProductId + " StoreID " + StoreId + " Corporate Sale " + CorporateSale;
            }
    
            public virtual string FormattedCorporateSale()
            {
                string result = "No";
    
                if (CorporateSale) result = "Yes";
    
                return result;
            }
    
            public virtual string FormattedDateSold ()
            {
                if (DateSold == null) return "DateSold not recorded";
    
                // Convert DateSold from DateTime? to DateTime cos DateTime? doesn't have the ToString with overload for 
                // formatting
                DateTime date = (DateTime)DateSold;
    
                return date.ToString("dd MMM yyyy") + " " + date.ToString("ddd");
            }
    
            public virtual string FormattedPriceSold()
            {
                return string.Format("{0:n}", PriceSold);
            }
    
            public virtual string FormattedPriceSoldForIndex()
            {
                return string.Format("{0:n}", PriceSold);
            }
    
            public virtual string FormattedQuantitySold()
            {
                string formattedQuantitySold = QuantitySold.ToString () + " unit";
    
                if (QuantitySold > 1) formattedQuantitySold = formattedQuantitySold + "s";
    
                return formattedQuantitySold;
            }
    
            public virtual string FormattedQuantitySoldForIndex()
            {
                string formattedQuantitySold = QuantitySold.ToString() + " unit";
    
                if (QuantitySold > 1) formattedQuantitySold = formattedQuantitySold + "s";
    
                return formattedQuantitySold;
            }
    
            public virtual string FormattedRemarks()
            {
                string result = "none";
    
                if (!(String.IsNullOrEmpty(Remarks))) result = Remarks;
    
                return result;
            }
    
            public virtual string FormattedProductSku()
            {
                return "Im a regular sale";
            }
    
            public virtual string FormattedProductSkuForIndex()
            {
                return "Im a regular sale";
            }
    
            public virtual string FormattedProductPartNumber()
            {
                return "I'm a regualr sale";
            }
    
            public virtual string FormattedStoreName()
            {
                return "I'm a regular sale";
            }
    
            public virtual string FormattedPaymentMethodName()
            {
                return "I'm a regular sale";
            }
    
            public virtual bool IsNoSale()
            {
                throw new NotImplementedException();
            }
    
            // Updates only those properties that are on the form
            public virtual void Update(Dictionary<string, string> data)
            {
                DateSold = DateTime.Parse(data["DateSold"]);
                CorporateSale = bool.Parse(data["CorporateSale"]);
                ProductId = data["ProductId"];
                PriceSold = Double.Parse(data["PriceSold"]);
                QuantitySold = int.Parse(data["QuantitySold"]);
                StoreId = data["StoreId"];
                PaymentMethodId = data["PaymentMethodId"];
                Remarks = data["Remarks"];
            }
        }
    
    [Table("sales.Sales")]
    public class Sale : EntityData
    {
        public string PromoterId { get; set; }
        public DateTime DateSold { get; set; }
        [Range(1, Int32.MaxValue, ErrorMessage = "Quantity Sold must be > 0")]
        public int QuantitySold { get; set; }
        [Range (1, Double.MaxValue, ErrorMessage = "Price Sold must be > 0")]
        public double PriceSold { get; set; }
        public bool CorporateSale { get; set; }
    
        [StringLength(255)]
        public string Remarks { get; set; }
    
        public string ProductId { get; set; }
        public string StoreId { get; set; }
        public string PaymentMethodId { get; set; }
        public string SaleTransactionId { get; set; }
    
        public virtual Product Product { get; set; }
        public virtual Store Store { get; set; }
        public virtual PaymentMethod PaymentMethod { get; set; }
        [NotMapped, JsonIgnore]
        public virtual Promoter Promoter { get; set; }
    
        [NotMapped]
        public string DateUploaded
        {
            get
            {
                string date = "";
    
                if (CreatedAt != null)
                {
                    var transformed = CreatedAt.GetValueOrDefault();
                    date = transformed.ToString("yyyy-MMM-dd");
                }
    
                return date;
            }
    
            set
            {
    
            }
        }
    
        [NotMapped]
        public string DateSold_String
        {
            get
            {
                string date = "";
    
                if (DateSold != null)
                {
                    var transformed = DateSold;
                    date = transformed.ToString("yyyy-MMM-dd");
                }
    
                return date;
            }
    
            set
            {
    
            }
        }
    
        public override string ToString()
        {
            var message = "I'm a Sale! DateSold: ";
    
            if (DateSold != null) message = message + DateSold;
            else message = message + "x";
    
            if (String.IsNullOrEmpty(ProductId)) message = message + " ProductID: " + "x";
            else message = message + " ProductID: " + ProductId;
    
            if (String.IsNullOrEmpty(StoreId)) message = message + " StoreID: " + "x"; 
            else message = message + " StoreID: " + StoreId;
    
            if (String.IsNullOrEmpty(PromoterId)) message = message + " PromoterID: " + "x";
            else message = message + " PromoterID: " + PromoterId;
    
            return message;
        }
    }
    
    Id(PK, nvarchar(128), not null)
    PromoterId(nvarchar(max), null)
    DateSold(datetime, not null)
    QuantitySold(int, not null)
    PriceSold(float, not null)
    CorporateSale(bit, not null)
    Remarks(nvarchar(255), null)
    ProductId(FK, nvarchar(128), null)
    StoreId(FK, nvarchar(128), null)
    PaymentMethodId(FK, nvarchar(128), null)
    SaleTransactionId(nvarchar(max), null)
    Version(timestamp, not null)
    CreatedAt(datetimeoffset(7), not null)
    UpdatedAt(datetimeoffset(7), null)
    Deleted(bit, not null)
    
     public class TodoItem : EntityData
        {
            public string UserId { get; set; }
    
            public string Text { get; set; }
    
            public bool Complete { get; set; }
    
            public string TagId { get; set; }
    
            [ForeignKey("TagId")]
            public Tag Tag { get; set; }
        }