5

I have a controller running on Azure App Service - Mobile. The trace shows that the below code runs fine until db.SaveChanges() this fails.

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

The stacktrace from the diagnostic on the appservice (which I unfortunately do not understand) gives:

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

Update

So I will be trying this, which will give more detailed error message:

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

Also

I will go through all the the settings to ensure that the a field that must not be null is null as according to this answer on stackoverflow.

Testing the above

The exception hides that the Id Field is required, is the exception.

The database I have uses EntityData(described here on msdn) where the Id comes from. My use on MobileService was that the Id was created when I executed the line, which fails db.SaveChanges(). Can somebody clarify this? The class looks like this:

 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; }
}
Community
  • 1
  • 1
JTIM
  • 2,774
  • 1
  • 34
  • 74
  • you still have the error after going through the settings you mentioned above? – Rami Sarieddine Apr 10 '16 at 11:38
  • @RamiSarieddine I have now tested everything and wrote an update to the question, it is the `Id` field from `EntityData` which gives the `Exception`. I had the understanding from previous work that this was created when I did `db.SaveChanges()`, which is the line that fails upon execution. Do you know why this is? I have not used `EntityData` before but would like to do so because of the `CreatedAt` and `UpdatedAt` columns which would be updated automatically ? – JTIM Apr 10 '16 at 17:16
  • @AlexanderDerck No, so inferring from you: My class that inherits from `EntityData` should have a variable `[DatabaseGenerated(DatabaseGeneratedOption.Identity)] public string Id {get; set;}` as to be compatible with the type from `EntityData`, or? – JTIM Apr 11 '16 at 12:22

1 Answers1

3

There is no auto generate support for string primary key in EF. That's why you should assign your primary key by manually.

You can initilaize Id,CreatedDate and UpdatedDate with constructor.

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

You can override savechanges method in your context. When you modified an entity ,this method will change updateddate automatically.

 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: The first solution was general solution which is not realeted with any library.

EntityData you need for serilazation. When mobile client and backend server tries to communicate with each other, this class will help you. For Example mobile client doesn't support navigation properties but backend server does. When you serilaize entitydata, it will hide these properties for you.

If you design your database for this app. Follow this document.

First: You should use EntityData Class which is inside of library. Your models should inherit from it not your EntityData Class.

Second: Your context should have this code on override model creating. You need this for automatic update of created/updated at and Id (This is the answer for your question).

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

If you use a database which is already existing follow this document.

Erkan Demirel
  • 4,302
  • 1
  • 25
  • 43
  • Okay, so this is normal practice to override the function? If so where in the solution should I put this, in the controller or? – JTIM Apr 11 '16 at 13:22
  • 1
    In `BCMobileAppContext` – Erkan Demirel Apr 11 '16 at 13:24
  • Okay I will try this tonight ! – JTIM Apr 11 '16 at 13:26
  • I tried the code and it got a bit further before crashing: `System.Data.SqlClient.SqlException: Cannot insert the value NULL into column 'CreatedAt', table 'BCSQL.dbo.Users'; column does not allow nulls. INSERT fails.` Does this mean I should also fill the `UpdatedAt` and `CreatedAt`, how do you do this such that it is optimal? i.e. only fills `CreatedAt` in the start and allways updates `UpdatedAt`? – JTIM Apr 11 '16 at 19:11
  • so this new solution is instead of right? But the `UpdatedAt` will not be updated automatically now? This was what I thought I would get by utilizing `EntityData`? – JTIM Apr 11 '16 at 19:36
  • I will test it again tonight. But before supplying you with all the points :) I just wanted to know if you could clarify why I should use `EntityData`? I could do all the same code for any other class? your code would work if another class just inherited `ITableData`? – JTIM Apr 12 '16 at 07:20
  • Yes first one just a solution for general (string id,automatic modify updated vs). I have changed the answer for based on Mobile library. – Erkan Demirel Apr 12 '16 at 09:15
  • Okay, the links you refer to are to the old service "MobileService" and not the new "App Service - Mobile". In the old Service, I did not use `EntityData` instead classes, and it had no issues on the client. I only accessed the data with different `DTO's`. The initial code you had seemed to work in general. I think I find myself in uncharted territory. I am a bit unsure if the added benefit is there for me to go with `EntityData`, when I could use a normal class and augment it with your initial code. hmm :) I will test later and accept, but if you have any comments I'll happily listen :) – JTIM Apr 12 '16 at 09:47
  • I haven't experienced with mobile app. If you use without EntityData, you will miss benefits of library. But you will have more control. You should implement the things like in first solution. ForExample [DomainManager](https://msdn.microsoft.com/en-us/library/azure/dn643358.aspx) needs `ITableData`. There is UpdateAsync method. I'm sure it uses Version field for concurrency. If you need concurrency you should implement it by yourself without library. I will keep both solution in answer. If it's not enough for your problem, I can delete the answer. – Erkan Demirel Apr 12 '16 at 10:09
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/108918/discussion-between-erkan-demirel-and-jtim). – Erkan Demirel Apr 12 '16 at 10:11