I currently have this method which updates my university object and then saves the changes.
public static int Update(Models.University uni)
{
using (var ctx = new ApplicationDbContext())
{
var x = (from y in ctx.Universities
where y.Id == uni.Id
select y).FirstOrDefault();
x.PrincipleName = uni.PrincipleName;
return ctx.SaveChanges();
}
}
Is there a way to save the whole object instead of setting each property?
For example something like(I tried this but the update fails):
public static int Update(Models.University uni)
{
using (var ctx = new ApplicationDbContext())
{
var x = (from y in ctx.Universities
where y.Id == uni.Id
select y).FirstOrDefault();
x = uni;
return ctx.SaveChanges();
}
}
If uni is also an entity then you can just attach it to context
ctx.Universities.Attach(uni);
// set the state of the entity to modified so changes are saved
ctx.Entry(uni).State = EntityState.Modified;
ctx.SaveChanges();
Related
I have to update one field in the row of the table after fetching two records from the same row. As an easiest practice I have fetched two records individually, created a new value and then updating that particular property through Entity framework. I think there is a better way to do the same thing with less code. If any body can suggest please.
if (objModel.amountpaid==0)
{
using (estatebranchEntities db=new estatebranchEntities())
{
int rentVar = Convert.ToInt32(db.PropertyDetails.Where(m => m.propertyid == objVM.propertyid).Select(m => m.rent).SingleOrDefault());
int balanceVar = Convert.ToInt32(db.PropertyDetails.Where(m => m.propertyid == objVM.propertyid).Select(m => m.balance).SingleOrDefault());
int balanceUpdateVar = (rentVar + balanceVar);
var propInfo = new PropertyDetail() { balance = balanceUpdateVar };
//var result = (from a in db.PropertyDetails
// where a.propertyid == objVM.propertyid
// select new PropertyDetail
// {
// rent = a.rent,
// balance = a.balance
// }).ToList();
db.PropertyDetails.Attach(propInfo);
db.Entry(propInfo).Property(z => z.balance).IsModified = true;
db.SaveChanges();
}
}
Here is what I think you can do.
Fetch the data once and update once.
using (estatebranchEntities db=new estatebranchEntities())
{
var propDetails = db.PropertyDetails.FirstOrDefault(m => m.propertyid == objVM.propertyid);
if (propDetails != null)
{
int rentVar = Convert.ToInt32(propDetails.rent);
int balanceVar = Convert.ToInt32(propDetails.balance);
int balanceUpdateVar = rentVar + balanceVar;
//now do the update
propDetails.balance = balanceUpdateVar;
db.Entry(proDetails).State = EntityState.Modified;
db.SaveChanges();
}
}
if you need to use the rentVar,balanceVar or the balanceUpdateVar, outside of the using statement then declare them outside it.
I'm somewhat new to EF 6.0 so I'm pretty sure I'm doing something wrong here.
there are two questions related to the problem
what am I doing wrong here
what's the best practice to achieve this
I'm using a code first model, and used the edmx designer to design the model and relationships, the system needs to pull information periodically from a webservice and save it to a local database (SQL Lite) in a desktop application
so I get an order list from the API, when I populate and try to save Ticket, I get a duplicate key exception when trying to insert TicketSeatType -
how do I insert the ticket to dbContext, so that It doesn't try and re-insert insert TicketSeatType and TicketPriceType, I have tried setting the child object states to unchanged but it seems to be inserting
secondly, what would be the best practice to achieve this using EF ? it just looks very inefficient loading each object into memory and comparing if it exists or not
since I need to update the listing periodically, I have to check against each object in the database if it exists, then update, else insert
code:
//read session from db
if (logger.IsDebugEnabled) logger.Debug("reading session from db");
dbSession = dbContext.SessionSet.Where(x => x.Id == sessionId).FirstOrDefault();
//populate orders
List<Order> orders = (from e in ordersList
select new Order {
Id = e.OrderId,
CallCentreNotes = e.CallCentreNotes,
DoorEntryCount = e.DoorEntryCount,
DoorEntryTime = e.DoorEntryTime,
OrderDate = e.OrderDate,
SpecialInstructions = e.SpecialInstructions,
TotalValue = e.TotalValue,
//populate parent refernece
Session = dbSession
}).ToList();
//check and save order
foreach (var o in orders) {
dbOrder = dbContext.OrderSet.Where(x => x.Id == o.Id).FirstOrDefault();
if (dbOrder != null) {
dbContext.Entry(dbOrder).CurrentValues.SetValues(o);
dbContext.Entry(dbOrder).State = EntityState.Modified;
}
else {
dbContext.OrderSet.Add(o);
dbContext.Entry(o.Session).State = EntityState.Unchanged;
}
}
dbContext.SaveChanges();
//check and add ticket seat type
foreach (var o in ordersList) {
foreach (var t in o.Tickets) {
var ticketSeatType = new TicketSeatType {
Id = t.TicketSeatType.TicketSeatTypeId,
Description = t.TicketSeatType.Description
};
dbTicketSeatType = dbContext.TicketSeatTypeSet.Where(x => x.Id == ticketSeatType.Id).FirstOrDefault();
if (dbTicketSeatType != null) {
dbContext.Entry(dbTicketSeatType).CurrentValues.SetValues(ticketSeatType);
dbContext.Entry(dbTicketSeatType).State = EntityState.Modified;
}
else {
if (!dbContext.ChangeTracker.Entries<TicketSeatType>().Any(x => x.Entity.Id == ticketSeatType.Id)) {
dbContext.TicketSeatTypeSet.Add(ticketSeatType);
}
}
}
}
dbContext.SaveChanges();
//check and add ticket price type
foreach (var o in ordersList) {
foreach (var t in o.Tickets) {
var ticketPriceType = new TicketPriceType {
Id = t.TicketPriceType.TicketPriceTypeId,
SeatCount = t.TicketPriceType.SeatCount,
Description = t.TicketPriceType.Description
};
dbTicketPriceType = dbContext.TicketPriceTypeSet.Where(x => x.Id == ticketPriceType.Id).FirstOrDefault();
if (dbTicketPriceType != null) {
dbContext.Entry(dbTicketPriceType).CurrentValues.SetValues(ticketPriceType);
dbContext.Entry(dbTicketPriceType).State = EntityState.Modified;
}
else {
if (!dbContext.ChangeTracker.Entries<TicketPriceType>().Any(x => x.Entity.Id == ticketPriceType.Id)) {
dbContext.TicketPriceTypeSet.Add(ticketPriceType);
}
}
}
}
dbContext.SaveChanges();
//check and add tickets
foreach (var o in ordersList) {
dbOrder = dbContext.OrderSet.Where(x => x.Id == o.OrderId).FirstOrDefault();
foreach (var t in o.Tickets) {
var ticket = new Ticket {
Id = t.TicketId,
Quantity = t.Quantity,
TicketPrice = t.TicketPrice,
TicketPriceType = new TicketPriceType {
Id = t.TicketPriceType.TicketPriceTypeId,
Description = t.TicketPriceType.Description,
SeatCount = t.TicketPriceType.SeatCount,
},
TicketSeatType = new TicketSeatType {
Id = t.TicketSeatType.TicketSeatTypeId,
Description = t.TicketSeatType.Description
},
Order = dbOrder
};
//check from db
dbTicket = dbContext.TicketSet.Where(x => x.Id == t.TicketId).FirstOrDefault();
dbTicketSeatType = dbContext.TicketSeatTypeSet.Where(x => x.Id == t.TicketSeatType.TicketSeatTypeId).FirstOrDefault();
dbTicketPriceType = dbContext.TicketPriceTypeSet.Where(x => x.Id == t.TicketPriceType.TicketPriceTypeId).FirstOrDefault();
if (dbTicket != null) {
dbContext.Entry(dbTicket).CurrentValues.SetValues(t);
dbContext.Entry(dbTicket).State = EntityState.Modified;
dbContext.Entry(dbTicket.Order).State = EntityState.Unchanged;
dbContext.Entry(dbTicketSeatType).State = EntityState.Unchanged;
dbContext.Entry(dbTicketPriceType).State = EntityState.Unchanged;
}
else {
dbContext.TicketSet.Add(ticket);
dbContext.Entry(ticket.Order).State = EntityState.Unchanged;
dbContext.Entry(ticket.TicketSeatType).State = EntityState.Unchanged;
dbContext.Entry(ticket.TicketPriceType).State = EntityState.Unchanged;
}
}
}
dbContext.SaveChanges();
UPDATE:
Found the answer, it has to do with how EF tracks references to objects, in the above code, I was creating new entity types from the list for TicketPriceType and TicketSeatType:
foreach (var o in ordersList) {
dbOrder = dbContext.OrderSet.Where(x => x.Id == o.OrderId).FirstOrDefault();
foreach (var t in o.Tickets) {
var ticket = new Ticket {
Id = t.TicketId,
Quantity = t.Quantity,
TicketPrice = t.TicketPrice,
TicketPriceType = new TicketPriceType {
Id = t.TicketPriceType.TicketPriceTypeId,
Description = t.TicketPriceType.Description,
SeatCount = t.TicketPriceType.SeatCount,
},
TicketSeatType = new TicketSeatType {
Id = t.TicketSeatType.TicketSeatTypeId,
Description = t.TicketSeatType.Description
},
Order = dbOrder
};
....
in this case the EF wouldn't know which objects they were and try to insert them.
the solution is to read the entities from database and allocate those, so it's referencing the same entities and doesn't add new ones
foreach (var t in o.Tickets) {
//check from db
dbTicket = dbContext.TicketSet.Where(x => x.Id == t.TicketId).FirstOrDefault();
dbTicketSeatType = dbContext.TicketSeatTypeSet.Where(x => x.Id == t.TicketSeatType.TicketSeatTypeId).FirstOrDefault();
dbTicketPriceType = dbContext.TicketPriceTypeSet.Where(x => x.Id == t.TicketPriceType.TicketPriceTypeId).FirstOrDefault();
var ticket = new Ticket {
Id = t.TicketId,
Quantity = t.Quantity,
TicketPrice = t.TicketPrice,
TicketPriceType = dbTicketPriceType,
TicketSeatType = dbTicketSeatType,
Order = dbOrder
};
...}
Don't you think that you are trying to write very similar codes for defining the state of each entity?
We can handle all of these operations with a single command.
You can easily achieve this with the newly released EntityGraphOperations for Entity Framework Code First. I am the author of this product. And I have published it in the github, code-project (includes a step-by-step demonstration and a sample project is ready for downloading) and nuget. With the help of InsertOrUpdateGraph method, it will automatically set your entities as Added or Modified. And with the help of DeleteMissingEntities method, you can delete those entities which exists in the database, but not in the current collection.
// This will set the state of the main entity and all of it's navigational
// properties as `Added` or `Modified`.
context.InsertOrUpdateGraph(ticket);
By the way, I feel the need to mention that this wouldn't be the most efficient way of course. The general idea is to get the desired entity from the database and define the state of the entity. It would be as efficient as possible.
I have the following View defined in my Context class, Entity Framework: I have added .Include() here, with the hopes that this will eliminate calls to database later.
public IQueryable<GeneralReportModel> vwGeneralReportItems
{
get
{
return from p in this.Patients.AsNoTracking().Include(p => p.Age) //Multiple includes added to include all properties on model
join fac in this.Facilities.AsNoTracking() on p.FacilityID equals fac.ID into fJoin
from f in fJoin.DefaultIfEmpty()
join userFac in this.UserFacilities.AsNoTracking() on p.FacilityID equals userFac.FacilityID into ufJoin
from uf in ufJoin.DefaultIfEmpty()
select new GeneralReportModel()
{
Patient = p,
//ReportIndex = ri,
Facility = f,
UserFacility = uf
};
}
}
I also have this function, which does some filtering on my select from the View:
private IQueryable<GeneralReportModel> generalReportQuery(ApplicationTypes.ReportParameterObject repParam)
{
var reportQuery = ReportHelper.GeneralReport_Source(this.DbContext, repParam, false);
reportQuery = ReportHelper.GeneralReport_FilterCriteria(this.DbContext, reportQuery, repParam);
// Lab confirmed - Extra Pulmonary
reportQuery = reportQuery.Where(rq =>
rq.Patient.TypeOfResistantTBConfirmation.Value == ApplicationTypes.ResistantTBConfirmationTypes.LABCONFIRMED.ToString() &&
rq.Patient.SiteOfDiseaseID != repParam.DatabaseSettingObject.SiteOfDiseaseID_ExtraPulmonary &&
rq.Patient.PatientCategoryID != ApplicationGlobal.GLBDATABASESETTINGOBJECT.PatientCategoryID_TransferIn);
return reportQuery;
}
The code below
var reportQueryMdr = reportQuery.Where(rq =>
rq.Patient.TypeOfResistantTB.Value == ApplicationTypes.ResistantTBTypes.MDR.ToString());
var list = reportQueryMdr.ToList();
foreach (var obj in list)
{
obj.ReportIndex = obj.Patient.getRecalculatedReportIndexFields(ApplicationGlobal.GLBDATABASESETTINGOBJECT);
}
reportQueryMdr = list.AsQueryable();
Still calls the Database for each iteration made over the list. How can I make this list entirely reside in memory, with all the Patient properties.
using the following code:
using (GagaShaggyContext db = new GagaShaggyContext())
{
ItemModel itemToChange = null;
itemToChange = (from i in db.Items
where i.ItemID == checkoutItem.Item.ItemID
select i).FirstOrDefault();
itemToChange.FrontFeature = false;
db.SaveChanges();
}
The model is saving back to the database with a brand new ID, which I want to save changes to the original database entry. Is there any reason for this to happen?
Thanks
Edit
On breakpoint analysis adding the receipt item before hand is adding a different ItemID not that I can see why:
using (var db = new GagaShaggyContext())
{
db.Receipts.Add(rec);
db.SaveChanges();
}
using (var db = new GagaShaggyContext())
{
var ItemToUse = (from i in db.ItemModels
where i.ItemModelID == checkoutItem.Item.ItemModelID
select i).FirstOrDefault();
rec.ItemModel = ItemToUse;
db.Receipts.Add(rec);
db.SaveChanges();
}
This fixed it. We need to actually retrieve the relevant Item and put it inside the Receipt (rec) then we could add it, now that Entity Framework recognizes the relationship between these two.
I guess you are trying to Insert into DB
using (GagaShaggyContext db = new GagaShaggyContext())
{
ItemModel itemToChange = new ItemModel();
itemToChange = (from i in db.Items
where i.ItemID == checkoutItem.Item.ItemID
select i).FirstOrDefault();
if(itemToChange !=null)
{
itemToChange.FrontFeature = false;
db .Items.Add(itemToChange);
db.SaveChanges();
}
}
If you are trying to Update the record than
using (GagaShaggyContext db = new GagaShaggyContext())
{
ItemModel itemToChange = new ItemModel();
itemToChange = (from i in db.Items
where i.ItemID == checkoutItem.Item.ItemID
select i).FirstOrDefault();
if(itemToChange !=null)
{
itemToChange.FrontFeature = false;
objDBContext.Entry(itemToChange).State = EntityState.Modified;
objDBContext.SaveChanges();
}
}
I am inserting child records that are associated with an existing parent record. How would I refresh the parent record to show all, including the newly inserted, child records?
context.Refresh(RefreshMode.OverwriteCurrentValues, entity) isn't working.
A more complete example of my attempt:
Location newLocation = Json.deserialize<Location>(json);
if (newLocation != null) {
IEnumerable<string> zipCodes = Regex.Split(newLocation.zipCodes, #"[\s,;]+");
// this verifies the new zipcodes against a table of all US zipcodes and returns matches
var newLocationZipCodes = _zipCodeRepository.match(zipCodes).Select(item => new LocationZipCode { idLocation = newLocation.id, state = item.state, zipcode = item.zipcode });
// get the parent entity
var domainLocation = _unitOfWork.locationRepository.getFirst(l => l.id == newLocation.id);
// insert child entities
if (newLocationZipCodes.Any()) {
_unitOfWork.locationZipCodeRepository.insertAll(newLocationZipCodes);
_unitOfWork.saveChanges(ConflictMode.ContinueOnConflict);
}
// this isn't working
_unitOfWork.refresh(RefreshMode.OverwriteCurrentValues, domainLocation);
return domainLocation;
}
Here is a basic representation of the LocationZipCode class created by linq-to-sql:
public class LocationZipCode {
int idLocation;
string zipcode;
string state
EntityRef<Location> location;
}
And here is my refresh method in my UnitOfWork:
public void refresh(RefreshMode refreshMode, object entity) {
_context.Refresh(refreshMode, entity);
}
Instead of refreshing the context I changed the way I was inserting my child records into the database. So instead of...
_unitOfWork.locationZipCodeRepository.insertAll(newLocationZipCodes);
I'm doing this...
domainLocation.LocationZipCodes.AddRange(newLocationZipCodes);
So the updated code looks like so...
Location newLocation = Json.deserialize<Location>(json);
if (newLocation != null) {
IEnumerable<string> zipCodes = Regex.Split(newLocation.zipCodes, #"[\s,;]+");
var newLocationZipCodes = _zipCodeRepository.match(zipCodes).Select(item => new LocationZipCode { idLocation = newLocation.id, state = item.state, zipcode = item.zipcode });
var domainLocation = _unitOfWork..locationRepository.getFirst(l => l.id == newLocation.id);
if (newLocationZipCodes.Any()) {
domainLocation.LocationZipCodes.AddRange(newLocationZipCodes);
_unitOfWork.saveChanges(ConflictMode.ContinueOnConflict);
}
return new Mapper<DomainLocation, Location>(new LocationMapTemplate()).map(domainLocation);
}