【问题标题】:Db context on azure app service (mobile) fails天蓝色应用服务(移动)上的数据库上下文失败
【发布时间】:2016-04-07 17:39:05
【问题描述】:

我有一个在 Azure 应用服务 - 移动设备上运行的控制器。跟踪显示以下代码运行良好,直到 db.SaveChanges() 失败。

var telemetry = new Microsoft.ApplicationInsights.TelemetryClient();
telemetry.TrackTrace("Create User");
using (BCMobileAppContext db = new BCMobileAppContext())
{
     string Username_NoSpaces = username.Username.Replace(" ", "");
     var user = db.Users.FirstOrDefault(u => u.Username_NoSpaces == Username_NoSpaces || u.MicrosoftToken == this.User.Identity.Name);
     telemetry.TrackTrace("1");
     if (user == null && !Username_NoSpaces.Contains(","))
     {
          telemetry.TrackTrace("2");
           DateTime now = DateTime.UtcNow;
           telemetry.TrackTrace("3");
           string username_noSpaces = username.Username.Replace(" ", "");
           DataObjects.User userItem = new DataObjects.User() { Created = now, UserId = this.User.Identity.Name, MicrosoftToken = this.User.Identity.Name, Username_NoSpaces = username_noSpaces, Update = now, Username = username.Username, Gold = 1, Level = 1, Title = "Sir", InGameCrest = "", ReceiveNotifications = true };
           telemetry.TrackTrace("4");
           UserDTO returnObject1 = new UserDTO() { Created = userItem.Created, isCreated = true, MicrosoftId = userItem.MicrosoftToken, Username = userItem.Username };
            telemetry.TrackTrace("5");
            db.Users.Add(userItem);
            telemetry.TrackTrace("6");
            db.SaveChanges();         //Trace and code fails
            telemetry.TrackTrace("7");
            UserDTO returnObject = new UserDTO() { Created = userItem.Created, isCreated = true, MicrosoftId = userItem.MicrosoftToken, Username = userItem.Username };
            telemetry.TrackTrace("8");
            return Ok(returnObject);
       }
}

来自 appservice 诊断的堆栈跟踪(遗憾的是我不明白)给出:

2016-04-07T17:29:19  PID[5008] Error       Operation=ReflectedHttpActionDescriptor.ExecuteAsync, Exception=System.Data.Entity.Validation.DbEntityValidationException: Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
   at System.Data.Entity.Internal.InternalContext.SaveChanges()
   at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
   at System.Data.Entity.DbContext.SaveChanges()
   at BCMobileAppService.Controllers.Test2Controller.Post(UserDTO username) in C:\Users\johann\Desktop\BCMobileApp_Runtime\BCMobileAppService\Controllers\TestController.cs:line 78
   at lambda_method(Closure , Object , Object[] )
   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass10.<GetExecutor>b__9(Object instance, Object[] methodParameters)
   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)
   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary 2 arguments, CancellationToken cancellationToken)
--- 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 System.Web.Http.Tracing.ITraceWriterExtensions.<TraceBeginEndAsyncCore>d__18 1.MoveNext()
2016-04-07T17:29:19  PID[5008] Error       Operation=ApiControllerActionInvoker.InvokeActionAsync, Exception=System.Data.Entity.Validation.DbEntityValidationException: Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
   at System.Data.Entity.Internal.InternalContext.SaveChanges()
   at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
   at System.Data.Entity.DbContext.SaveChanges()
   at BCMobileAppService.Controllers.Test2Controller.Post(UserDTO username) in C:\Users\johann\Desktop\BCMobileApp_Runtime\BCMobileAppService\Controllers\TestController.cs:line 78
   at lambda_method(Closure , Object , Object[] )
   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass10.<GetExecutor>b__9(Object instance, Object[] methodParameters)
   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)
   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary 2 arguments, CancellationToken cancellationToken)
--- 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 System.Web.Http.Tracing.ITraceWriterExtensions.<TraceBeginEndAsyncCore>d__18 1.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 System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>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 System.Web.Http.Tracing.ITraceWriterExtensions.<TraceBeginEndAsyncCore>d__18 1.MoveNext()
2016-04-07T17:29:19  PID[5008] Error       Operation=Test2Controller.ExecuteAsync, Exception=System.Data.Entity.Validation.DbEntityValidationException: Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
   at System.Data.Entity.Internal.InternalContext.SaveChanges()
   at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
   at System.Data.Entity.DbContext.SaveChanges()
   at BCMobileAppService.Controllers.Test2Controller.Post(UserDTO username) in C:\Users\johann\Desktop\BCMobileApp_Runtime\BCMobileAppService\Controllers\TestController.cs:line 78
   at lambda_method(Closure , Object , Object[] )
   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.< >c__DisplayClass10.<GetExecutor>b__9(Object instance, Object[] methodParameters)
   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)
   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary 2 arguments, CancellationToken cancellationToken)
--- 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 System.Web.Http.Tracing.ITraceWriterExtensions.<TraceBeginEndAsyncCore>d__18 1.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 System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>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 System.Web.Http.Tracing.ITraceWriterExtensions.<TraceBeginEndAsyncCore>d__18 1.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 System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__5.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 System.Web.Http.Filters.ActionFilterAttribute.<ExecuteActionFilterAsyncCore>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 System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.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 System.Web.Http.Tracing.Tracers.HttpControllerTracer.<ExecuteAsyncCore>d__5.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 System.Web.Http.Tracing.ITraceWriterExtensions.<TraceBeginEndAsyncCore>d__18 1.MoveNext()

更新

所以我会尝试this,它会给出更详细的错误信息:

catch (DbEntityValidationException dbEx)
{
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
    foreach (var validationError in validationErrors.ValidationErrors)
    {
        Trace.TraceInformation("Property: {0} Error: {1}", 
                                validationError.PropertyName, 
                                validationError.ErrorMessage);
    }
}
}

还有

根据this answer on stackoverflow,我将检查所有设置以确保不能为null 的字段为null

测试上述内容

异常隐藏Id字段是必填的,是异常。

我使用的数据库使用EntityData(描述为here on msdnId 的来源。我在 MobileService 上的使用是 Id 是在我执行该行时创建的,它失败了 db.SaveChanges()。有人可以澄清一下吗? 该类如下所示:

 public abstract class EntityData : ITableData
{
    protected EntityData();

    [Index(IsClustered = true)]
    [TableColumn(TableColumnType.CreatedAt)]
    public DateTimeOffset? CreatedAt { get; set; }
    [TableColumn(TableColumnType.Deleted)]
    public bool Deleted { get; set; }
    [TableColumn(TableColumnType.Id)]
    public string Id { get; set; }
    [TableColumn(TableColumnType.UpdatedAt)]
    public DateTimeOffset? UpdatedAt { get; set; }
    [TableColumn(TableColumnType.Version)]
    public byte[] Version { get; set; }
}

【问题讨论】:

  • 按照你上面说的设置,你还有这个错误吗?
  • @RamiSarieddine 我现在已经测试了所有内容并对问题进行了更新,它是来自EntityDataId 字段,它给出了Exception。我从之前的工作中了解到,这是在我执行 db.SaveChanges() 时创建的,这是执行时失败的行。你知道这是为什么吗?我以前没有使用过EntityData,但我想这样做是因为CreatedAtUpdatedAt 列会自动更新?
  • @AlexanderDerck 不,所以从你那里推断:我的继承自 EntityData 的类应该有一个变量 [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public string Id {get; set;} 以与 EntityData 的类型兼容,或者?

标签: c# asp.net entity-framework azure-api-apps azure-mobile-services


【解决方案1】:

EF 中不支持自动生成字符串主键。这就是为什么您应该手动分配主键的原因。

您可以使用构造函数初始化IdCreatedDateUpdatedDate

    public abstract class EntityData : ITableData
    {
     //Change the constructor to initilaize required filled with meaningful data.
        protected EntityData()
        {
          Id=Guid.NewGuid().ToString();
          CreatedDate=DateTime.UtcNow;
          UpdatedDate=DateTime.UtcNow;
        }

        [Index(IsClustered = true)]
        [TableColumn(TableColumnType.CreatedAt)]
        public DateTimeOffset? CreatedAt { get; set; }
        [TableColumn(TableColumnType.Deleted)]
        public bool Deleted { get; set; }
        [TableColumn(TableColumnType.Id)]
        public string Id { get; set; }
        [TableColumn(TableColumnType.UpdatedAt)]
        public DateTimeOffset? UpdatedAt { get; set; }
        [TableColumn(TableColumnType.Version)]
        public byte[] Version { get; set; }
    }

您可以在您的上下文中覆盖 savechanges 方法。当您修改实体时,此方法会自动更改更新日期。

 public override int SaveChanges()
    {
        //Get Modified Entities
        var modifiedEntries = ChangeTracker.Entries()
             .Where(x => x.Entity is ITableData
                 && (x.State == EntityState.EntityState.Modified));
        foreach (var entry in modifiedEntries)
         {
           var entity = entry.Entity as ITableData;
           //Modify updateddate
           if (entity != null)
            {
              entity.UpdatedDate = DateTime.UtcNow;
            }
         }
        return base.SaveChanges();
    }

编辑:第一个解决方案是通用解决方案,未与任何库一起使用。

EntityData 你需要进行序列化。当移动客户端和后端服务器尝试相互通信时,本课程将为您提供帮助。例如,移动客户端不支持导航属性,但后端服务器支持。当您对实体数据进行序列化时,它会为您隐藏这些属性。

如果您为此应用程序设计数据库。关注这个document

首先:您应该使用库内的EntityData 类。您的模型应该继承自它而不是您的 EntityData 类。

第二:您的上下文应该在覆盖模型创建时包含此代码。您需要这个来自动更新 created/updated at 和 Id(这是您问题的答案)。

   protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            string schema = ServiceSettingsDictionary.GetSchemaName();
            if (!string.IsNullOrEmpty(schema))
            {
                modelBuilder.HasDefaultSchema(schema);
            }

            modelBuilder.Conventions.Add(
                new AttributeToColumnAnnotationConvention<TableColumnAttribute, string>(
                    "ServiceTableColumn", (property, attributes) => attributes.Single().ColumnType.ToString()));
        }

如果您使用已经存在的数据库,请关注document

【讨论】:

  • 好的,所以这是覆盖函数的正常做法?如果是这样,我应该把它放在解决方案的哪个位置,在控制器中还是?
  • BCMobileAppContext
  • 好的,我今晚试试这个!
  • 我尝试了代码,它在崩溃之前得到了一点:System.Data.SqlClient.SqlException: Cannot insert the value NULL into column 'CreatedAt', table 'BCSQL.dbo.Users'; column does not allow nulls. INSERT fails.这是否意味着我还应该填写UpdatedAtCreatedAt,你如何做到这一点,这样它是最佳的?即只在开头填写CreatedAt,并且总是更新UpdatedAt?
  • 所以这个新的解决方案是不是对的?但是UpdatedAt现在不会自动更新了吗?这就是我想通过使用EntityData 得到的?
猜你喜欢
  • 1970-01-01
  • 2017-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-07
  • 1970-01-01
  • 1970-01-01
  • 2014-10-04
相关资源
最近更新 更多