5

Ho un controller in esecuzione su Servizio app di Azure - Mobile. La traccia mostra che il codice sottostante funziona bene fino al db.SaveChanges().Contesto Db su servizio app blu (mobile) non riuscito

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

Lo StackTrace dalla diagnostica sul appservice (che io purtroppo non capisco) dà:

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() 

Aggiornamento

Quindi cercherò this, che darà un messaggio di errore più dettagliato:

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

anche

farò passare attraverso tutte le impostazioni per garantire che il un campo che non deve essere null è null come in base alla this answer on stackoverflow.

Test del sopra

L'eccezione si nasconde che è necessario il campo Id, è l'eccezione.

Il database I utilizza EntityData (descritto here on msdn) da cui proviene il numero Id. Il mio utilizzo su MobileService è stato che il Id è stato creato quando ho eseguito la riga, che non riesce db.SaveChanges(). Qualcuno può chiarirlo? La classe si presenta così:

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

hai ancora l'errore dopo aver attraversato le impostazioni che hai menzionato sopra? –

+0

@RamiSarieddine Ora ho provato tutto e ho scritto un aggiornamento alla domanda, è il campo 'Id' da' EntityData' che dà l ''Eccezione'. Ho avuto la comprensione dal lavoro precedente che questo è stato creato quando ho fatto 'db.SaveChanges()', che è la linea che non riesce all'esecuzione. Sai perché è questo? Non ho usato 'EntityData' prima, ma mi piacerebbe farlo a causa delle colonne' CreatedAt' e 'UpdatedAt' che verrebbero aggiornate automaticamente? – JTIM

+0

@AlexanderDerck No, quindi deduco da te: la mia classe che eredita da 'EntityData' dovrebbe avere una variabile' [DatabaseGenerated (DatabaseGeneratedOption.Identity)] stringa pubblica Id {get; set;} 'come compatibile con il tipo da 'EntityData', o? – JTIM

risposta

3

Non c'è auto generare il supporto per la stringa chiave primaria in EF. Ecco perché dovresti assegnare la tua chiave primaria manualmente.

È possibile eseguire initilaize Id, CreatedDate e UpdatedDate con il costruttore.

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

È possibile sovrascrivere il metodo di salvataggi nel proprio contesto. Quando hai modificato un'entità, questo metodo cambierà automaticamente aggiornato.

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

Edit: La prima soluzione è stata soluzione generale che non è realeted con qualsiasi libreria.

EntityData necessario per serilazation. Quando il client mobile e il server di back-end cercano di comunicare tra loro, questa classe ti aiuterà. Ad esempio, il client mobile non supporta le proprietà di navigazione ma il server di backend lo fa. Quando serisci i dati dell'entità, nasconderà queste proprietà per te.

Se si progetta il database per questa app. Segui questo document.

Primo: È necessario utilizzare la classe EntityData che si trova all'interno della libreria. I tuoi modelli dovrebbero ereditare da esso non la tua classe EntityData.

Secondo: il proprio contesto deve avere questo codice sul modello di creazione sovrascrittura. È necessario per l'aggiornamento automatico di creato/aggiornato a e Id (Questa è la risposta alla tua domanda).

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

Se si utilizza un database che è già esistente seguire questo document.

+0

Ok, quindi questa è una pratica normale per sovrascrivere la funzione? Se sì, dove nella soluzione dovrei metterlo, nel controller o? – JTIM

+1

In 'BCMobileAppContext' –

+0

Okay ci proverò stasera! – JTIM