I have the following code. When I trace the code I see that City is not null, it's values changes then SaveChanges is called but changes are not saved. there are about hundreds of similar code in this project and all of them works. what the problem can be here?
using (Entities db = new Entities())
{
long id = (long)((string)data.data[i].id).ParseLong();
City city = db.Cities.FirstOrDefault(c => c.Id == id);
if (city != null)
{
city.FromLat = data.data[i].fromlat;
city.ToLat = data.data[i].tolat;
city.FromLng = data.data[i].fromlng;
city.ToLng = data.data[i].tolng;
if (city.FromLat > city.ToLat) More.Swap(ref city.ToLat, ref city.FromLat);
if (city.FromLng > city.ToLng) More.Swap(ref city.FromLng, ref city.ToLng);
db.SaveChanges();
}
}
You haven't provided your model, but using the ref keyword in these lines:
if (city.FromLat > city.ToLat) More.Swap(ref city.ToLat, ref city.FromLat);
if (city.FromLng > city.ToLng) More.Swap(ref city.FromLng, ref city.ToLng);
clearly indicates that FromLat, ToLat, FromLng and ToLng members are fields, thus not mapped to database columns. Make them properties and use different code for swapping (a bit longer, but working):
if (city.FromLat > city.ToLat) { var temp = city.FromLat; city.FromLat = city.ToLat; city.ToLat = temp; };
if (city.FromLng > city.ToLng) { var temp = city.FromLng; city.FromLng = city.ToLng; city.ToLng = temp; };
db.Entry(city).State = EntityState.Modified;
Should do the trick.
Add this right before savechanges
Try setting the state to modified via
db.Entry(city).State = System.Data.Entity.EntityState.Modified;
Or you can manually set each field that has changed as modified.
You did not add the item to its collection (db.Cities.Add(city)).
If you want to update the entity:
db.Entry(city).State = EntityState.Modified;
db.SaveChanges();
Related
I have an action and 2 SQL tables I know how to enter a record into 1 table but for the second table I want to get the lastly inserted ID from table 1 and insert that into the 2nd table for one of the columns. I have everything setup but my database save doesn’t work without an exception. Also Im not sure how to use scope_identity() or get the last ID and I am using EF for everything.
public ActionResult AddTimeSheet()
{
ViewBag.ProjectsSelect = new SelectList(
context.Projects.ToList(), "ProjectId", "ProjectName");
var uid = User.Identity.GetUserId();
ViewBag.CurrentId = uid;
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddTimeSheet(TimeSheetProjectsModel timeSheetModel)
{
try
{
if(timeSheetModel == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
//if (ModelState.IsValid)
//{
TimeSheetMaster masterModel = new TimeSheetMaster();
masterModel.FromDate = timeSheetModel.Proj1;
masterModel.ToDate = timeSheetModel.Proj1.AddDays(7);
/* NEED HRS 4 WEEK/END MONTH/NEW MONTH */
masterModel.TotalHours = timeSheetModel.ProjTotal1;
masterModel.UserId = User.Identity.GetUserId();
masterModel.DateCreated = DateTime.Now;
/* MONTH SUBMITTED */
masterModel.TimeSheetStatus = 1;
masterModel.Comment = timeSheetModel.ProjDesc1;
context.TimeSheetMaster.Add(masterModel);
var detailsModel = CreateTimeSheetDetails(masterModel, timeSheetModel);
context.TimeSheetDetails.Add(detailsModel);
context.SaveChanges();
TempData["SuccessMaster"] = "TimeSheetMaster Created Successfully";
TempData["SuccessDetails"] = "TimeSheetDetails Created Successfully";
return RedirectToAction("TimeSheetList", "TimeSheet");
//}
// TempData["Error"] = "TimeSheet Create Was Unsuccessful";
// return RedirectToAction("TimeSheetList", "TimeSheet");
}
catch (Exception ex)
{
Console.Write(ex.Message);
TempData["Error"] = "TimeSheet Create Was Unsuccessful";
return RedirectToAction("TimeSheetList", "TimeSheet");
}
}
If the two entities are associated with the same DbContext and they are directly related, (PK/FK) then the best solution is to ensure that the entities have a navigation property set up for the relation and let EF manage the keys and linking.
For instance:
TimeSheetMaster masterModel = new TimeSheetMaster();
masterModel.FromDate = timeSheetModel.Proj1;
masterModel.ToDate = timeSheetModel.Proj1.AddDays(7);
masterModel.TotalHours = timeSheetModel.ProjTotal1;
masterModel.UserId = User.Identity.GetUserId();
masterModel.DateCreated = DateTime.Now;
masterModel.TimeSheetStatus = 1;
masterModel.Comment = timeSheetModel.ProjDesc1;
var detailsModel = CreateTimeSheetDetails(masterModel, timeSheetModel);
// either here, or inside CreateTimeSheetDetails...
// if Master has a collection of Details: (1-to-Many)
masterModel.Details.Add(details);
// or if Master has a 1-to-1 relationship to details.
// masterModel.Details = detailsModel;
context.TimeSheetMaster.Add(masterModel);
context.SaveChanges();
Ideally EF should manage your relationships so that your code simply works with the related entity structure and hands off the key/relationship management to Entity Framework.
If you need IDs cross-context, or to return after performing an insert then you can access the entity's new PK directly after calling SaveChanges:
context.TimeSheetMaster.Add(masterModel);
context.SaveChanges();
var newId = masterModel.TimeSheetMasterId; // Populated automatically after SaveChanges.
I have a working code here.
using (var db = new MyContextDB())
{
var result = db.Books.SingleOrDefault(b => b.BookNumber == bookNumber);
if (result != null)
{
result.MyColumnName= "Some new value";
db.SaveChanges();
}
}
But I have many properties to change. So I was trying for something like
result= newResult;
db.SaveChanges();
But this is not working. Is there any idea to replace a record with a new one?
I think, you can not do this so easily.
You should create a method in your Book class, where you change all of the properties.
result.Update(Book newProperties);
db.SaveChanges();
or
result.Update(string prop1, int prop2, etc.);
db.SaveChanges();
I would recommend to use Automapper. You can use it like this:
var result = db.Books.SingleOrDefault(b => b.BookNumber == bookNumber);
if (result != null)
{
Mapper.Map(newResult, result);
db.SaveChanges();
}
Notice, that probably you will need to properly configure Automapper(e.g. to ignore Id properties when updating)
I Use this code for update one field in my table.( with Entity Framework 6.1.3)
var model = new MyTable { Id = Id, UpdateTime = DateTime.UtcNow };
var dbSet = this.dbContext.Set<MyTable>();
dbSet.Attach(model);
entry = this.dbContext.Entry(model);
entry.State = EntityState.Modified;
this.dbContext.SaveChanges();
but this not work and UpdateTime does not change.and when i change code to this:
var model = this.dbContext.Set<MyTable>().Find(id);
model = new MyTable { Id = Id, UpdateTime = DateTime.UtcNow };
var dbSet = this.dbContext.Set<MyTable>();
entry = this.dbContext.Entry(model);
entry.State = EntityState.Modified;
this.dbContext.SaveChanges();
I found that in my first code , EF look to UpdateTime field that did not change, but is this right when i write:
entry.State = EntityState.Modified;
ef must generate update code , then why it does not?
How must i do for solve this problem?
Finally i found what's the problem?
I have a required string field in my table , and i found that we need to fill reference type fileds in Entity framework , and it can not be empty or whitespace character but not required the right value, and in this state we must don't use
entry.State = EntityState.Modified;
and instead of it we must use
entry.Property("UpdateTime").IsModified = true;
therefore this problem can be solved by this way:
var model = new MyTable { Id = Id, UpdateTime = DateTime.UtcNow , Title = "EveryThing" };
var dbSet = this.dbContext.Set<MyTable>();
dbSet.Attach(model);
entry = this.dbContext.Entry(model);
entry.Property("UpdateTime").IsModified = true;
this.dbContext.SaveChanges();
I'm trying to copy ProductStatisticsTemp table data to ProductStatistics table,
var str = from a in db.ProductStatisticsTemp select a;
ProductStatistics ls = new ProductStatistics();
foreach (var val in str.ToList())
{
ls.Product_ID = val.Product_ID;
ls.ProductNameEn = val.ProductNameEn;
ls.ProductNameAr = val.ProductNameAr;
db.ProductStatistics.Add(ls);
db.SaveChanges();
}
first record can insert but once its try to insert 2nd one getting following error
The property 'Product_ID' is part of the object's key information and
cannot be modified.
It's because you have one instance of an object and try to add already added object twice.
You need to create new object of ProductStatistics in the loop.
Also you can save changes just once after the loop to improve performance by trigger DB communication just once:
var str = from a in db.ProductStatisticsTemp select a;
foreach (var val in str.ToList())
{
ProductStatistics ls = new ProductStatistics
{
Product_ID = val.Product_ID,
ProductNameEn = val.ProductNameEn,
ProductNameAr = val.ProductNameAr
};
db.ProductStatistics.Add(ls);
}
db.SaveChanges();
Here is a slightly different method.
var products = db.ProductStatisticsTemp.Select(t => new ProductStatistics
{
Product_ID = t.Product_ID,
ProductNameEn = t.ProductNameEn,
ProductNameAr = t.ProductNameAr
}).ToList()
db.ProductStatistics.AddRange(products);
db.SaveChanges();
IMHO Inspired from #Vadim Martynov
If the Product_ID is your primary key, and your set to increment
the key from database . Do not do this Product_ID = val.Product_ID.
The key should be generated from the database. You will get the id
after save changes is invoked.
try
{
var str = from a in db.ProductStatisticsTemp select a;
//This will improve some performance
db.Configuration.AutoDetectChangesEnabled = false;
foreach (var val in str.ToList())
{
ProductStatistics ls = new ProductStatistics
{
Product_ID = val.Product_ID,
ProductNameEn = val.ProductNameEn,
ProductNameAr = val.ProductNameAr
};
//use AddRange or Add based on your EF Version.
db.ProductStatistics.Add(ls);
}
db.SaveChanges();
}
finally
{
db.Configuration.AutoDetectChangesEnabled = true;
}
If you are using AddRange you could omit db.Configuration.AutoDetectChangesEnabled = false
For more info about DetectChanges available here
AddRange() method only support from EF6 see documentation
db.ProductStatistics.AddRange(products);
What AddRange will do for you is
if AutoDetectChangesEnabled is set to true (which is the default), then DetectChanges will be called once before adding any entities and will not be called again.
This means that in some situations AddRange may perform significantly
better than calling Add multiple times would do.
Note that entities that are already in the context in some other state will have their state set to Added. AddRange is a no-op for entities that are already in the context in the Added state.
How I can do just this ( a.myFavorits.Add()) without pulling the all object to var a , because a has a lot of data, and I don't want to pull all a object, but I can't find a way do do it.
I want to do the lambada and the linq without return something but linq is always return something
public static void addFavorits(long f,long idUser)
{
using (var db = dataBase())
{
// here i pull object user from users table
var a = db.users.Where(c => c.id == idUser).SingleOrDefault();
// here i adding to the object field myFavorits new value
//myFavorits is also a table of entitys that connected to user object
a.myFavorits.Add(new BE.FavoritsUsersLong { myLong = f });
db.SaveChanges();
}
}
I thought to do something like this but i dont know how to set the field users_TableId that is the key that connect the 2 tables
public static void addFavorits(long favoritId,long idUser)
{
using (var db = dataBase())
{
db.favoritsUsersLong.Add(new BE.FavoritsUsersLong {myLong = favoritId}
/*,users_TableId =idUser*/);
db.SaveChanges();
}
}
Here's a concrete example that does what you want. In this example, only the Name of a Company is modified and saved. Or an item is added to one of its collections.
var cmp = new Company{ CmpId = 1, Name = "Cmp1" }; // CmpId is the primary key
db.Companies.Attach(cmp);
db.Entry(cmp).Property(c => c.Name).IsModified = true;
// Or add an entity to a collection:
cmp.Users = new[] {new User { Name = "a1", PassWord = "a1" } };
try
{
db.Configuration.ValidateOnSaveEnabled = false;
db.SaveChanges();
}
finally
{
db.Configuration.ValidateOnSaveEnabled = true;
}
Result in SQL:
DECLARE #0 VarChar(30) = 'Cmp1'
DECLARE #1 Int = 1
UPDATE [dbo].[Company]
SET [Name] = #0
WHERE ([CmpId] = #1)
There are a few things to note here:
Obviously you need to know the Id of the entity you want to modify.
The object you create is called a stub entity, which is an incomplete entity. When you try to save such an entity, EF is very likely to complain about null values in required properties. That's why almost certain you'd have to disable validation (temporarily, or, better, dispose the context immediately).
If you want to add an item to a collection, you should leave validation enabled, because you'd want to know for sure that the new entity is valid. So you shouldn't mix these two ways to use a stub entity.
If you often need roughly the same small part of your entity you may consider table splitting.
I'm guessing this is what you want? I don't see you 'editting' I only see you adding.
using (var db = dataBase())
{
var a = new user();
....
//set properties etc..
...
a.myFavorits.Add(new BE.FavoritsUsersLong { myLong = f });
db.users.Add(a);
db.SaveChanges();
}