Add related database entry in Azure Mobile Services controller - entity-framework

In my Azure Mobile Service I have a controller class UserController : TableController<User> and in it is a get method:
// GET tables/User/48D68C86-6EA6-4C25-AA33-223FC9A27959
public SingleResult<User> GetUser(string id)
{
return Lookup(id);
}
I want to record each time a user is accessed and so I add a simple type to the model:
public class UserVisit : Microsoft.WindowsAzure.Mobile.Service.EntityData
{
public string VisitingUser { get; set; }
public DateTime TimeOfVisit { get; set; }
}
and include the property public DbSet<UserVisit> UserVisits { get; set; } in my VCollectAPIContext : DbContext class (and update the database with a code-first migration).
To add a UserVisit to the database when a user id is queried I change my controller method to
// GET tables/User/48D68C86-6EA6-4C25-AA33-223FC9A27959
public async Task<SingleResult<User>> GetUser(string id)
{
var userVisit = new UserVisit { VisitingUser = id, TimeOfVisit = DateTime.UtcNow };
context.UserVisits.Add(userVisit);
await context.SaveChangesAsync();
return Lookup(id);
}
But the SaveChangesAsync fails with a System.Data.Entity.Validation.DbEntityValidationException. Digging around in the exception's EntityValidationErrors property I find that the problem is "The Id field is required."
That's a little odd. The Id field is one of the properties in the base-class Microsoft.WindowsAzure.Mobile.Service.EntityData that I would expect to be added automatically on insert. No matter, I can add it and several of the other base-class's properties thus:
// GET tables/User/48D68C86-6EA6-4C25-AA33-223FC9A27959
public async Task<SingleResult<User>> GetUser(string id)
{
var userVisit = new UserVisit { Id = Guid.NewGuid().ToString(), Deleted = false, VisitingUser = id, TimeOfVisit = DateTime.UtcNow, CreatedAt = DateTimeOffset.Now };
context.UserVisits.Add(userVisit);
await context.SaveChangesAsync();
return Lookup(id);
}
This time I get a System.Data.Entity.Infrastructure.DbUpdateException because we "Cannot insert the value NULL into column 'CreatedAt'". It was not null in the call to Add. So CreatedAt has been set to null somewhere outside my code and then the insert fails as a result!
I also tried setting up an EntityDomainManager<UserVisit> userVisitDomainManager; instance variable in the controller's initializer, and then rewriting my controller get method as
// GET tables/User/48D68C86-6EA6-4C25-AA33-223FC9A27959
public async Task<SingleResult<User>> GetUser(string id)
{
var userVisit = new UserVisit { VisitingUser = id, TimeOfVisit = DateTime.UtcNow };
await userVisitDomainManager.InsertAsync(userVisit);
return Lookup(id);
}
That fails with the same message, "Cannot insert the value NULL into column 'CreatedAt'"
How should I perform the seemingly simple task of inserting a related data item within my controller method?

The solution is likely similar to this answer. I'm guessing that your migration is not using the Mobile Services SqlGenerator so some of the custom SQL settings aren't getting applied. What that means is that:
Id doesn't get a default value of NEWID() -- this explains your "Id field is required" error.
CreatedAt doesn't get a default value of SYSUTCDATETIME() -- this, combined with the [DatabaseGenerated] attribute on EntityData.CreatedAt, explains the "NULL CreatedAt" error.
Try updating your migration according to the link above and see if that works for you.

To fix the problem of "The Id field is required" following brettsam's instructions.
Add this in your model:
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[TableColumn(TableColumnType.Id)]
public new string Id { get; set; }
It will auto generate a GUID when you add an entity.

Related

Entity Framework - Update only sending values

I want to update my entity for just sending values.
public HttpResponseMessage UpdateDepartment(Department department)
{
var ok = _departmentDAL.Update(department);
return Request.CreateResponse(HttpStatusCode.OK, ok);
}
Im sending just 2 value with postman to my api.
In my generic repository base, my update function like.
public int Update(TEntity entity)
{
var updatedEntity = _context.Entry(entity);
updatedEntity.State = EntityState.Modified;
return _context.SaveChanges();
}
And I got entity validation error. Simply I just want to modify only not null values of entity.
Is it possible or should I take all entity with my Id property from database and then after changing the properties send to the entity framework ?
Cleanest solution is to not provide a general interface that can update any desired field of Department. Instead, provide an API that is tailored to the actual use cases you want to support. This API should receive commands that only contain the data allowed for the specific use case. Commands can also validate their data (here I use System.ComponentModel.DataAnnotations for validation). Also, you can handle authorization in a more granular way if the use cases are well defined and separated.
public class UpdateDepartmentDescriptionCommand {
[Required, Range(1, long.MaxValue)]
public long DepartmentId { get; set; }
[Required, StringLength(256)]
public string Description { get; set; }
}
public HttpResponseMessage UpdateDepartmentDescription(UpdateDepartmentDescriptionCommand cmd) {
// validate command
var validationResults = new List<ValidationResult>();
var isValid = Validator.TryValidateObject(cmd, new ValidationContext(cmd, null, null), validationResults, true);
if (!isValid) {
return Request.CreateResponse(HttpStatusCode.BadRequest, validationResults);
}
// retrieve Department from DB using the given ID
var department = _departmentDAL.Find(cmd.DepartmentId);
// only update values defined by the usecase
department.Description = cmd.Description;
var ok = _departmentDAL.Update(department);
return Request.CreateResponse(HttpStatusCode.OK, ok);
}

Adding a new entity to collection in attached entity causes ConcurrencyException

I have simplified the code below to show the root of the problem. My real code is using GenericRepository and UnitOfWork pattern but I get the same exception with this simplified code too.
I am using Entity Framework 6, Code First
It uses the following POCO entities
public class Order
{
public int Id {get;set;}
public virtual List<OrderProducts> OrderProducts {get;set;}
...
}
public class Product
{
public int Id {get;set;}
...
}
public class OrderProduct
{
public int OrderId {get;set;}
public int ProductId {get;set;}
public int Quantity
public virtual Order Order { get; set; }
public virtual Product Product{ get; set; }
}
The user is able to create a new product and add it to the order products on the same screen.
//Pull an order from the database:
var existingOrder = db.Orders.FirstOrDefault(x => x.Id == inputModel.OrderId);
//Iterate the OrderProductInputModels (IMs) in the Inputmodel
foreach (var orderProductIM in inputModel.OrderProductIMs )
{
var orderProduct = existingOrder.OrderProducts.SingleOrDefault(o => o.Id == orderProductIM.Id);
//if its an existing order product (already in db)
if (orderProduct != null)
{
//just update its property values
}
//if it has been added
else
{
//we need to create a new product first
var newProduct= new Product() { <set some properties> };
orderProduct= new OrderProduct()
{
Product=newProduct,
Order=existingOrder
}
//Add the OrderProduct to the order
existingOrder.OrderProducts.Add(orderProduct);
}
db.SaveChanges();
On save changes, I get the following error.
[System.Data.Entity.Infrastructure.DbUpdateConcurrencyException] = {"Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries."}
Why is this?
I expected entity framework to see that the existingOrders nested properties were newly added and unattached, update the order and create the new OrderProduct and Product.
Should it not be other way around in your if clause as you are checking for null ( then only it is a new order product else update. Issue is here:
//if its an existing order product (already in db)
if (orderProduct == null)
{
//just update its property values
}
//if it has been added
else
{
When you are looping around all the OrderProducts, you are constantly updating the database but the existingOrder object is not getting refreshed. Update that or add all the objects first and then update the database.
Finally solved it by creating a test project and reverse code first engineering the database. Noticed that OrderProduct entity was not generated. On inspecting the database, the primary key was not set. Once I set the primary key in the database, the issue was resolved. Thanks for all the suggestions.

ASP.NET MVC 4 error updating entity framework models with related entities

I feel like this should be a pretty common thing to do. I have a model with a related object on it. Let's say it's a User and a user has one Role.
public class User
{
public int Id { get; set; }
public virtual Role Role { get; set; }
/* other stuff that saves fine */
}
public class Role
{
public int Id {get;set;}
public string Name { get;set;}
}
So if I save a new user, or if I edit a user (but don't change his Role), I have no issues. If I have a user without a role, and add a role to him, again no problem (though I manually lookup the role and assign it). If I try and change a role, I get a modelstate error on the Role property that the ID is part of the object's key and can't be changed. So how do folks go about making updates like this? Whitelist the simple values and then manually update the Role?
My controller code in question is here:
[HttpPost]
public ActionResult Save(int id, FormCollection form)
{
var user = data.Users.FirstOrDefault(d=> d.Id == id);
if (user != null)
{
TryUpdateModel(user, form.ToValueProvider());
if (!ModelState.IsValid)
{
var messages = ModelState.Values.Where(m => m.Errors.Count() > 0).SelectMany(m=>m.Errors).Select(e => e.ErrorMessage);
if (Request.IsAjaxRequest())
return Json(new { message = "Error!", errors = messages });
return RedirectToAction("index"); // TODO: more robust Flash messaging
}
updateDependencies(user);
/* negotiate response */
}
}
I'll probably just do it manually for now, but it seems like a scenario that I would have expected to work out of the box, at least to some degree.
Your User model should have a foreign key:
public int? RoleId { get; set; }
public virtual Role Role { get; set; }
You can assign a Role.Id to this value, or make it null when the user does not have a role.
I'm also not sure if your Save function is correct. I'm always using this pattern (not sure if it is correct either...), but of course it depends on the data you post to the server:
[HttpPost]
public ActionResult Save(User model)
{
if (ModelState.IsValid)
{
// Save logic here, for updating an existing entry it is something like:
context.Entry(model).State = EntityState.Modified;
context.SaveChanges();
return View("Success");
}
return View("Edit", model);
}

Cant see changes in database after SaveChanges() called entity framework

I have a table Device with only one column UID nvarchar(128) and here is my entity:
[Table( Name="Device" )]
public class Device
{
[Key]
public string UID { get; set; }
}
When I trying to insert entity and commit changes to database all is ok. But in database there are no new rows added. If I try to repeat this operation with the same UID - I get en eror
Violation of PRIMARY KEY constraint 'PK_dbo.Devices'. Cannot insert duplicate key in object 'dbo.Devices'. The duplicate key value is ...
What's wrong?
EDIT:
Here is my context:
public class DeviceContext : BaseDbContext, IDbContext
{
public DbSet<Entity.Device> Device { get; set; }
public new IDbSet<T> Set<T>() where T : class
{
return base.Set<T>();
}
public int SaveChanges()
{
return base.SaveChanges();
}
public void Dispose()
{
base.Dispose();
}
}
Fails SaveChanges() method. BaseDbContext is only "connectionstring layer".
DeviceContext context = new DeviceContext();
context.Device.Add(new Device() { UID = id });
context.SaveChanges();
Error says that data is already saved. So I think you are looking at wrong database. Verify connection string which is used by your DbContext.
BTW you can see which connection is used by watching at context.Database.Connection.ConnectionString property in debugger.

Entity Framework, unmapped property and Dynamic Data

I'm using an Entity Framework data model to drive a Dynamic Data website for use by users to update data.
One of the entities contains a non-nullable string property (Description). In the database, one of the rows has an empty Description (not null but an empty string). When I try to update the Description I get the following validation error: "This property cannot be set to a null value".
If I manually update the Description in the database and then edit the property, it works as expected. But as soon as I change the Description in the database back to an empty string, the validation error occurs. The error happens on Description's setter.
So I've tried adding an additional string property called CustomDescription which basically wraps Description, made Description a ScaffoldColumn(false) in the entity's metadata and added the new property to the entity's metadata.
[ScaffoldColumn(true)]
public string CustomDescription
{
get { return this.Description; }
set {
if (value == null)
{
value = string.Empty;
}
this.Description = value;
}
}
However what do I need to add to this property in order to get it to display on the dynamic data site?
Problem is that old value was empty string in Non-Nullable field.
By default framework is converting it to null.
To fix the error just add the following attribute to your field:
[DisplayFormat(ConvertEmptyStringToNull = false)]
public object Description { get; set; }
In the corresponding Metadata class, just refernce it as you would an actual field:
[MetadataType(typeof(MyClassMetadata))]
public partial class MyClass
{
[ScaffoldColumn(true)]
public string CustomString
{
return "foo";
}
}
public class MyClassMetadata
{
[Display(Name = "Custom")]
public object CustomString { get; set; }
}