使用Azure移动应用程序中的移动客户端更新行时出错

问题描述 投票:0回答:2

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

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

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

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

我有三个问题:

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

更新

打开服务器中的日志记录,将PatchSale更改为异步,以便我们可以等待UpdateAsync(id,patch),并在调用等待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

AdrianHall怀疑.NET服务器中的Sale模型与其对应的SQL服务器之间可能存在差异。我已经比较了两者,似乎没有发现任何差异(我假设在扩展EntityData时包含了Id,Version,CreatedAt,UpdatedAt和Deleted)。此外,如果.NET服务器和SQL服务器中的模型之间存在差异,我们可以创建销售并将其上载到服务器吗?

此外,在下面的服务器中发布了销售模型,在SQL服务器中发布了列。

这是参考的代码:

服务器:控制器中的更新方法

public Task<Sale> PatchSale(string id, Delta<Sale> patch)
{
    System.Diagnostics.Trace.TraceInformation("INSIDE PATCH SALE!!!");

    return UpdateAsync(id, patch);
}

移动客户端:更新销售

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;
}

移动客户:销售模式

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;
    }
}

SQL Server:销售列(不知道除了列之外还有什么其他内容)

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)
c# xamarin.android azure-mobile-services
2个回答
1
投票

代码500是“无效的服务器响应”,通常是“请求导致服务器代码崩溃”。要诊断这一点,您需要进入Azure门户并打开诊断日志记录,然后查看日志流。如果你能够,可以通过远程调试器从Visual Studio连接(查看http://aka.ms/zumobook - 第8章,了解一些有用的提示)。

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


0
投票

在我的情况下,似乎在使用FK关系的服务器端下面使用如下所示不起作用。我的客户端对象和服务器对象之间只有区别是Tag属性。如果我从服务器对象中删除它,更新工作正常。我不知道Adrian Hall如何在他的github样本上给出这个例子,它可以在here上运行。

 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; }
    }
© www.soinside.com 2019 - 2024. All rights reserved.