I have an unusual problem. One field in the database (FK_ItemType) will not update itself. I went into the vs debug, and checked that int ItemType was assigned, that it found a record in the database, that it assigned that record to item.FK_ItemType, and that that record stayed attached all the way to db.SaveChanges() (I used a breakpoint to check the value of item.FK_ItemType at db.Entry(item).State = EntityState.Modified;
To be extra sure, immediately after I commented out my redirect and pulled the record from the database and checked what the value was, returning that instead of the redirect. It returns the correct value.
However, when I look in the database, or go to my listing page, the newly updated ItemType is not there (still NULL in the database). Even more puzzling, everything works perfectly fine when adding a record.
Here are the relevant sections of my controller and model
[HttpPost]
public ActionResult Edit(Item item, int Company = 0, int Service = 0, int ItemType = 0)
{
if (Request.Form["doDelete"] == "true")
return Delete(item);
//Get, assign foreign keys
Company c = db.Companies.Find(Company);
Service s = db.Services.Find(Service);
ItemType i = db.ItemTypes.Find(ItemType);
if (c != null)
item.FK_Company = c;
if (s != null)
item.FK_Service = s;
if (i != null)
item.FK_ItemType = i;
//Force revalidate
ModelState.Clear();
//TryUpdateModel(item);
TryValidateModel(item);
if (ModelState.IsValid)
{
if (item.ItemID == 0) //add
db.Items.Add(item);
else
{
db.Entry(item).State = EntityState.Modified;
}
db.SaveChanges();
Item i2 = db.Items.Find(item.ItemID);
Response.Write(i2.FK_ItemType.Name);
return null;
// Return to the listing page, and show the user the filters they were looking at before editing.
return Redirect("~/" + Request.RequestContext.RouteData.Values["Controller"].ToString() + "/Index/" + Request.Form["ref"]);
}
// There was a validation error
if (item.ItemID != 0)
return Update(item);
else
return Add(item);
}
Model
public class Item
{
public int ItemID { get; set; }
...
[Required]
[Display(Name="Company")]
public virtual Company FK_Company { get; set; }
[Required]
[Display(Name="Service")]
public virtual Service FK_Service { get; set; }
[Display(Name="Item Type")]
public virtual ItemType FK_ItemType { get; set; }
}
Despite the name FK_ItemType this property is not a FK (foreign key) property. It is a navigation property and relationships represented by navigation properties are not updated when you set the state to Modified. (The same applies to FK_Company and FK_Service.)
You can either introduduce "real" foreign key properties...
[ForeignKey("FK_ItemType")]
public int RealFK_ItemType { get; set; }
public virtual ItemType FK_ItemType { get; set; }
// please give those properties better names..., like "ItemTypeID" and "ItemType"
...and then use the FK RealFK_ItemType directly to change relationships.
Or you must load the original Item from the database first (including related entities) to change the relationships by setting the navigation properties. The code for the UPDATE would be something like this:
var itemIdDb = db.Items
.Include(i => i.Company)
.Include(i => i.Service)
.Include(i => i.ItemType)
.Single(i => i.ItemID == item.ItemID);
Company c = db.Companies.Find(Company);
Service s = db.Services.Find(Service);
ItemType i = db.ItemTypes.Find(ItemType);
if (c != null)
itemInDb.FK_Company = c;
if (s != null)
itemInDb.FK_Service = s;
if (i != null)
itemInDb.FK_ItemType = i;
db.Entry(itemInDb).CurrentValues.SetValues(item);
db.SaveChanges();
Related
Recently I have faced with an issue in mvc. My problem is about how increment a value of field which is unique,primary and int. For example , I have a customerCode as int and I want to increment a value of that after saving new customer . I get that mvc first check id and if its equal to zero ,then increment it to max+1. But the issue is here: if I want to increment it like Max+4, how can I handle it?
this is my code:
public ActionResult Save(Customers customer)
{
if (!ModelState.IsValid)
{
var _Customer = new CreatnewCustomerViewModel()
{
Customer = customer,
membershiptype = _context.membershiptype.ToList()
};
return View("CustomerForm", _Customer);
}
if (customer.CustomerID==0)
{
//int id = _context.customers.Max(m => m.CustomerID);
//customer.CustomerID = id + 1;
_context.customers.Add(customer);
}
else
{
var CustomerIndb = _context.customers.Single(c => c.CustomerID == customer.CustomerID);
{
CustomerIndb.Name = customer.Name;
CustomerIndb.birthday = customer.birthday;
CustomerIndb.IsSubscribedToNewsletter = customer.IsSubscribedToNewsletter;
// CustomerIndb.MembershipType = customer.MembershipTypeID;
CustomerIndb.MembershipTypeID = customer.MembershipTypeID;
}
}
_context.SaveChanges();
return RedirectToAction("index", "Customer");
}
Write your Customers model class as follows:
public class Customers
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CustomerID { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CustomerCode { get; set; }
public string Name { get; set; }
// Other properties
}
Now generate a new migration and update the database accordingly!
Now if you want to set Identity Increment more than 1, then go to the database with SQL Server Management Studio, open the the Customers table. Right click on CustomerCode column and then select properties. Now set the Identity Increment value (default is 1) to your desired value.
you can easily solve that problem with the help of DatabaseGeneratedOption.Identity and Sql() method in the Up() .
If you new to both of above concepts , Here is the links with clear instructions
https://forums.asp.net/t/2062551.aspx?Entity+Framework+How+to+set+a+start+value+for+an+Auto+Incremented+Column+
Addtional:
How to set initial value for auto incremented property (DatabaseGeneratedOption.Identity)
Hope it will help to get rid of the problem, let me know your status.
Thanks
I am getting error in adding the value to IEnumerable type entity. Here is the restaurant entity
public virtual ICollection<BusinessUser> BusinessUsers { get; set; }
Here is the businessUser entity
public virtual ICollection<Restaurant> Restaurants { get; set; }
Here is the action method to add business user to the restaurant
public ActionResult Add(Restaurant restaurant)
{
if (ModelState.IsValid)
{
restaurant.DateAdded = DateTime.Now;
restaurant.BusinessUsers.Select(c => c.PersonID) = 2; // i want to assign the user with id 2 to this restaurant
db.Restaurants.Add(restaurant);
db.SaveChanges();
TempData["Success"] = "New Restaurant has been added successfully";
return RedirectToAction("Index");
}
return View(restaurant);
}
I see the following error in my editor:
Only assignment call increment decrement await and new object expressions can be used as a statement
You must create a BusinessUser entity with PersonID = 2 and attach it to the context (to avoid that it gets duplicated in the database). Then add this entity to the restaurant.BusinessUsers collection:
public ActionResult Add(Restaurant restaurant)
{
if (ModelState.IsValid)
{
var businessUser = new BusinessUser { PersonID = 2 };
db.BusinessUsers.Attach(businessUser);
restaurant.DateAdded = DateTime.Now;
restaurant.BusinessUsers.Add(businessUser);
db.Restaurants.Add(restaurant);
db.SaveChanges();
TempData["Success"] = "New Restaurant has been added successfully";
return RedirectToAction("Index");
}
return View(restaurant);
}
I assume that restaurant.BusinessUsers is instantiated, otherwise add the line restaurant.BusinessUsers = new List<BusinessUser>(); before adding the user to the collection.
I suspect your syntax error is coming from this line:
restaurant.BusinessUsers.Select(c => c.PersonID) == 2;
Which should probably be something like:
restaurant.BusinessUsers.Add(db.Persons.Where(c => c.PersonID == 2).First());
Let's say I have two models:
public class User
{
[Key]
public int UserId { get; set; }
public string Name { get; set; }
}
and
public class Friend
{
[Key]
public int FriendId { get; set; }
public User A { get; set; }
public User B { get; set; }
}
Let's say I only have 2 users in my database (ids: 1 (Jon) and 2 (Sam)). Now I insert into table friend like this:
db.Friends.Add(new Friend()
{
A = db.Users.Find(1),
B = db.Users.Where(u => u.UserId == 2).First()
});
db.SaveChanges();
Suddenly, I find a user (3, Sam) in a table user. What is the reasoning behind this? Not completely sure if relevant or not, but note that even if I make A and B fields virtual, nothing changes.
UPDATE
Finally found how to reproduce my problem. Apparently the problem isn't exactly the same as I described.
User a, b;
using (var db = new DbConnection())
{
a = db.Users.First(u => u.UserId == 1);
b = db.Users.First(u => u.UserId == 2);
}
using (var db = new DbConnection())
{
db.Friends.Add(new Friend()
{
A = a,
B = b
});
db.SaveChanges();
}
Now users will have 4 users. Does it mean that if I step out of transaction, I can no longer access the entities as if they were exactly the same items in the current transaction? Or maybe there is a way to make the program know that I am referring to the same item (because the ID is the same)?
Honestly tried the same steps as you described and everything work well.. Anyway my steps
Created a db context class derived from `DbContext'
public class EFContext : DbContext
{
public DbSet<Friend> Friends { get; set; }
public DbSet<User> Users { get; set; }
public EFContext(string connectionString)
: base(connectionString)
{
}
}
I use MSQL2008 Express with win auth so I created the Users table
using (var db = new EFContext(#"Data Source=yourMachineName\SQLEXPRESS2008;Initial Catalog=DBName;Integrated Security=True;MultipleActiveResultSets=True"))
{
db.Users.Add(new User()
{
UserId = 1,
Name = "John"
});
db.Users.Add(new User()
{
UserId = 2,
Name = "Sam"
});
db.SaveChanges();
}
I checked my db and found 2 records
After I created the Friends table
using(var db = new EFContext(#"Data Source=yourMachineName\SQLEXPRESS2008;Initial Catalog=DBName;Integrated Security=True;MultipleActiveResultSets=True"))
{
db.Friends.Add(new Friend()
{
A = db.Users.Find(1),
B = db.Users.Where(u => u.UserId == 2).First()
});
db.SaveChanges();
}
Again I got 1 record in the Friends table with columns FriendId=1, A_UserId=1, B_UserId=2.
I checked the Users table and I still have 2 records.
If I were you I would try my code in a separate app. If it works then please post here all steps which led you to this problem.
Ok, this is probably a concept that i've got wrong, but anyways...
(MVC3) I have an entity with a list property on it. My CRUD views work sending a JSon representation to the controller, via an Ajax post. Everything is working great, except that when i'm posting an update of that entity, the list property is not being updated at all. All the simple properties of the entity are updated, but (as I imagine) the update tree is not including the List property. How can I make the EF aware of those changes on the list?
Here's some of the code so far:
[HttpPost]
public JsonResult Edit(Lote lote)
{
//Given the IDs present in lote.Documentos, load the List of Documentos
if (lote.Documentos != null)
{
List<Documento> ldoc = new List<Documento>();
foreach (var d in lote.Documentos)
{
ldoc.Add(db.Documentos.Find(d.IDDocumento));
}
lote.Documentos.Clear();
foreach (var d in ldoc)
{
lote.Documentos.Add(d);
}
}
//Now, clear all the previous errors
foreach (var modelValue in ModelState.Values)
{
modelValue.Errors.Clear();
}
//And re-validate the model
ValidateModel(lote);
if (ModelState.IsValid)
{
if (lote.IDLote > 0)
{
//Updating
db.Entry(lote).State = EntityState.Modified;
}
else
{
//Inserting
db.Lotes.Add(lote);
}
db.SaveChanges();
CustomMessages.Sucesso(TempData, "Informações salvas com sucesso.", 10000);
return Json(new { Success = 1, IDProprietario = lote.IDLote, ex = "" });
}
else
{
return Json(new { Success = 0, ex = "Falha na rotina de armazenamento das informações"});
}
And those are the classes themselves:
public class Lote
{
[Key]
public int IDLote { get; set; }
(... lots of properties ...)
[Display(Name = "Documentos")]
public List<Documento> Documentos { get; set; }
}
public class Documento
{
//---=== ATRIBUTOS ===---
[Key]
public int IDDocumento { get; set; }
[Required]
[MaxLength(60)]
public string Nome { get; set; }
public List<Lote> Lotes { get; set; }
}
As this is a Many-to-Many relationship, i also got this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove
<System.Data.Entity.ModelConfiguration.Conventions.PluralizingTableNameConvention>();
modelBuilder.Entity<Lote>()
.HasMany(t => t.Documentos)
.WithMany(t => t.Lotes)
.Map(m =>
{
m.ToTable("LoteDocumento");
m.MapLeftKey("IDLote");
m.MapRightKey("IDDocumento");
});
(... and some other stuff)
Any help on this?
Try changing this line:
ldoc.Add(db.Documentos.Find(d.IDDocumento));
to
ldoc.Add(db.Documentos.Include("Lotes").FirstOrDefault(x => x.IDDocumento == d.IDDocumento));
You need to make sure that the relatioship/objects that you are changing are in fact attached to your current DB context. Otherwise entity framework wont be able to track changes made to them.
This link explains it in terms of object context, but I think the same rules apply to DBcontext.
If that doesn't work, let me know cause I am really trying to get better at understanding the way EF works.
Okay, found some more link that will help:
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key
Entity Framework and Connection Pooling
When you execute Include("Lotes") that adds your lotes related to your Documentos to the DBContext. Entity framework is now tracking these objects. Further down in your code you are re-adding them to the context with this line db.Entry(lote).State = EntityState.Modified; That's my guess anyway.
based on the links above i would try and re-write what you have like this (not compiled):
[HttpPost]
public JsonResult Edit(Lote lote)
{
//get old Lote from DB
var oldLote = db.Lotes.include("Documentos").FirstOrDefault(x => x.IDLote == lote.IDLote);
//update
if(oldLote != null)
{
//refresh any other properties that you may have changed on it.
db.Entry(oldLote).CurrentValues.SetValues(lote);
//not sure if you will even need this section any more but then you can...
oldLote.Documentos.Clear();
foreach (var d in lote.Documentos)
{
oldLote.Documentos.Add(db.Documentos.include("Lotes").FirstOrDefault(x => x.IDDocumento == d.IDDocumento));
}
}
else //add
{
//not sure if this will work
foreach (var d in lote.Documentos)
{
db.Entry(d).State = EntityState.Modified;
}
db.Lotes.Add(lote);
}
//then just save changes. EF is already tracking all your objects and changes.
db.SaveChanges();
....
I would like to use Linq and strongly typed views in the right way. at the moment I do the following:
Make a Model to verify agianst:
public class Menu
{
public int Id { get; private set; }
public string Text { get; private set; }
public string Action { get; private set; }
public string Controller { get; private set; }
public string Parameter { get; private set; }
public string Langue { get; private set; }
public Menu(int id, string controller, string action, string parameter, string text)
{
Id = id;
Controller = controller;
Action = action;
Text = text;
Parameter = parameter;
}
Use Linq to get the data from the database into the model:
public static List<Menu> GetTabListForMenu(string langue)
{
Page_dbEntities entity = new Page_dbEntities();
var tabList = (from ml in entity.wpmenulangue
where ml.Langue == langue
from m in entity.wpmenu
where ml.Menu == m.Id
from l in entity.wplangue
where ml.Langue == l.Langue
from p in entity.wppage
where p.Id == m.Page
select new { m.Id, p.Controller, p.Action, p.Parameter, ml.Text}).ToList();
List<Menu> menu = new List<Menu>();
foreach (var item in tabList)
{
menu.Add(new Menu(item.Id, item.Controller, item.Action, item.Parameter, item.Text));
}
return menu;
}
I am pretty convinced that this is not the optimal way to do this and have 2 questions:
When I get the data from the database I first use a var and then have to move it to the object with a foreach. this seems like a waste of both my time and less effeicent then getting it with sql.
I have been told that I can just verify up agianst the entitymodel. Even if i use multiple entities in a view. is this true? (the one telling me this wes not able to get it to work and I have not been able to find anything about it online).
I will try to look back on this post in the next couple of hours, but might have to wait 24 hours.
public static List<Menu> GetTabListForMenu(string langue)
{
Page_dbEntities entity = new Page_dbEntities();
return (from ml in entity.wpmenulangue
where ml.Langue == langue
from m in entity.wpmenu
where ml.Menu == m.Id
from l in entity.wplangue
where ml.Langue == l.Langue
from p in entity.wppage
where p.Id == m.Page
select new Menu(m.Id, p.Controller, p.Action, p.Parameter, ml.Text)
).ToList();
}
As for the validation is concerned you shouldn't use multiple entities in the view. You should use a single entity which is called ViewModel. This ViewModel is a class that represents the data on the view. If you are using DataAnnotations for validation you could decorate this view model properties with attributes that indicate how to be validated.