Using Entity Framework Code First's Seed method, I have been making calls like this to add new objects to a table.
private void SeedDatabaseActivities(SiteDbContext context)
{
context.Activities.AddOrUpdate(new ActivityDefinition
{
Id = 605,
Name = "bicycling, >20 mph, racing, not drafting",
UseSteps = false,
UseDistance = false,
Category = "bicycling",
Mets = 16
});
}
This was working great for months. Recently, this no longer sets the ID, correctly. I have been searching for why this would change, but haven't been able to find the issue.
What would cause this behavior?
Related
I want to log new and modified records. This code works just fine for Modified Records.
But with Added records, there is an issue. Since it is new to the Database, there is not yet a primary key for it. So there is no way to log which record was added.
However, if I try to log the records after the save, the EntityState is no longer Added. So I don't know what was added.
The only solution I have been able to come up with is to save a list of the new records, and then after the save, then Log the changes. But that seems like a workaround.
Is there some way to resolve this?
private List<Event> LogChanges(EntityEntry entityEntry, Enums.TableNames tableName)
{
List<Event> result = new List<Event>();
var databaseValues = entityEntry.GetDatabaseValues();
foreach (var property in entityEntry.CurrentValues.Properties.Where(a=> a.Name !="TenantId"))
{
string original = databaseValues[property]?.ToString();
string current = entityEntry.CurrentValues[property]?.ToString();
if(!object.Equals(original,current))
{
result.Add(
new Event()
{
AppUserId = this._appUserProvider.CurrentAppUserId,
EventDate = DateTimeOffset.UtcNow,
EventTypeId = (int)Enums.EventTypes.Modified,
TenantId = databaseValues.GetValue<int>("TenantId"),
RecordId = databaseValues.GetValue<int>("Id"),
ColumnName = property.Name,
OriginalValue = original,
NewValue = current,
TableId = (int)tableName
});
}
}
return result;
}
This library adds triggers to EntityFrameworkCore. Using the Triggers it provides is a much cleaner way to accomplish the above.
I have an object with several really large string properties. In addition, it has a simple timestamp property.
What I trying to achieve is to update only timestamp property without getting the whole huge object to the server.
Eventually, I would like to use EF and to do in the most performant way something equivalent to this:
update [...]
set [...] = [...]
where [...]
Using the following, you can update a single column:
var yourEntity = new YourEntity() { Id = id, DateProp = dateTime };
using (var db = new MyEfContextName())
{
db.YourEntities.Attach(yourEntity);
db.Entry(yourEntity).Property(x => x.DateProp).IsModified = true;
db.SaveChanges();
}
OK, I managed to handle this. The solution is the same as proposed by Seany84, with the only addition of disabling validation, in order to overcome issue with required fields. Basically, I had to add the following line just before 'SaveChanges():
db.Configuration.ValidateOnSaveEnabled = false;
So, the complete solution is:
var yourEntity = new YourEntity() { Id = id, DateProp = dateTime };
using (var db = new MyEfContextName())
{
db.YourEntities.Attach(yourEntity);
db.Entry(yourEntity).Property(x => x.DateProp).IsModified = true;
db.Configuration.ValidateOnSaveEnabled = false;
db.SaveChanges();
}
Ok, I must be working too hard because I can't get my head around what it takes to use the Entity Framework correctly.
Here is what I am trying to do:
I have two tables: HeaderTable and DetailTable. The DetailTable will have 1 to Many records for each row in HeaderTable. In my EDM I set up a Relationship between these two tables to reflect this.
Since there is now a relationship setup between these tables, I thought that by quering all the records in HeaderTable, I would be able to access the DetailTable collection created by the EDM (I can see the property when quering, but it's null).
Here is my query (this is a Silverlight app, so I am using the DomainContext on the client):
// myContext is instatiated with class scope
EntityQuery<Project> query = _myContext.GetHeadersQuery();
_myContext.Load<Project>(query);
Since these calls are asynchronous, I check the values after the callback has completed. When checking the value of _myContext.HeaderTable I have all the rows expected. However, the DetailsTable property within _myContext.HeaderTable is empty.
foreach (var h in _myContext.HeaderTable) // Has records
{
foreach (var d in h.DetailTable) // No records
{
string test = d.Description;
}
I'm assuming my query to return all HeaderTable objects needs to be modified to somehow return all the HeaderDetail collectoins for each HeaderTable row. I just don't understand how this non-logical modeling stuff works yet.
What am I doing wrong? Any help is greatly appriciated. If you need more information, just let me know. I will be happy to provide anything you need.
Thanks,
-Scott
What you're probably missing is the Include(), which I think is out of scope of the code you provided.
Check out this cool video; it explained everything about EDM and Linq-to-Entities to me:
http://msdn.microsoft.com/en-us/data/ff628210.aspx
In case you can't view video now, check out this piece of code I have based on those videos (sorry it's not in Silverlight, but it's the same basic idea, I hope).
The retrieval:
public List<Story> GetAllStories()
{
return context.Stories.Include("User").Include("StoryComments").Where(s => s.HostID == CurrentHost.ID).ToList();
}
Loading the the data:
private void LoadAllStories()
{
lvwStories.DataSource = TEContext.GetAllStories();
lvwStories.DataBind();
}
Using the data:
protected void lvwStories_ItemDataBound(object sender, ListViewItemEventArgs e)
{
if (e.Item.ItemType == ListViewItemType.DataItem)
{
Story story = e.Item.DataItem as Story;
// blah blah blah....
hlStory.Text = story.Title;
hlStory.NavigateUrl = "StoryView.aspx?id=" + story.ID;
lblStoryCommentCount.Text = "(" + story.StoryComments.Count.ToString() + " comment" + (story.StoryComments.Count > 1 ? "s" : "") + ")";
lblStoryBody.Text = story.Body;
lblStoryUser.Text = story.User.Username;
lblStoryDTS.Text = story.AddedDTS.ToShortTimeString();
}
}
I'm currently inserting/updating fields like this (if there's a better way, please say so - we're always learning)
public void UpdateChallengeAnswers(List<ChallengeAnswerInfo> model, Decimal field_id, Decimal loggedUserId)
{
JK_ChallengeAnswers o;
foreach (ChallengeAnswerInfo a in model)
{
o = this.FindChallengeAnswerById(a.ChallengeAnswerId);
if (o == null) o = new JK_ChallengeAnswers();
o.answer = FilterString(a.Answer);
o.correct = a.Correct;
o.link_text = "";
o.link_url = "";
o.position = FilterInt(a.Position);
o.updated_user = loggedUserId;
o.updated_date = DateTime.UtcNow;
if (o.challenge_id == 0)
{
// New record
o.challenge_id = field_id; // FK
o.created_user = loggedUserId;
o.created_date = DateTime.UtcNow;
db.JK_ChallengeAnswers.AddObject(o);
}
else
{
// Update record
this.Save();
}
}
this.Save(); // Commit changes
}
As you can see there is 2 times this.Save() (witch invokes db.SaveChanges();)
when Adding we place the new object into a Place Holder with the AddObject method, in other words, the new object is not committed right away and we can place as many objects we want.
But when it's an update, I need to Save first before moving on to the next object, is there a method that I can use in order to, let's say:
if (o.challenge_id == 0)
{
// New record
o.challenge_id = field_id;
o.created_user = loggedUserId;
o.created_date = DateTime.UtcNow;
db.JK_ChallengeAnswers.AddObject(o);
}
else
{
// Update record
db.JK_ChallengeAnswers.RetainObject(o);
}
}
this.Save(); // Only save once when all objects are ready to commit
}
So if there are 5 updates, I don't need to save into the database 5 times, but only once at the end.
Thank you.
Well if you have an object which is attached to the graph, if you modify values of this object, then the entity is marked as Modified.
If you simply do .AddObject, then the entity is marked as Added.
Nothing has happened yet - only staging of the graph.
Then, when you execute SaveChanges(), EF will translate the entries in the OSM to relevant store queries.
Your code looks a bit strange. Have you debugged through (and ran a SQL trace) to see what is actually getting executed? Because i can't see why you need that first .Save, because inline with my above points, since your modifying the entities in the first few lines of the method, an UPDATE statement will most likely always get executed, regardless of the ID.
I suggest you refactor your code to handle new/modified in seperate method. (ideally via a Repository)
Taken from Employee Info Starter Kit, you can consider the code snippet as below:
public void UpdateEmployee(Employee updatedEmployee)
{
//attaching and making ready for parsistance
if (updatedEmployee.EntityState == EntityState.Detached)
_DatabaseContext.Employees.Attach(updatedEmployee);
_DatabaseContext.ObjectStateManager.ChangeObjectState(updatedEmployee, System.Data.EntityState.Modified);
_DatabaseContext.SaveChanges();
}
I am having a real issue with the EF v1. I have quite a big EDMX with maybe 50 entities mapped, but this one entity is causing me grief.
The entity has mappings to other entities which in effect are reference tables, but for some reason it is trying to do an insert and not just update itself.
Here is a fragment of my code:
using (var context = new someEntities()) {
var studentCourseJoin =
context.StudentCourseJoinSet.Where(o => o.Code == scjCode).First();
studentCourseJoin.EntryStatus = new EntryStatus { Code = viewModel.StudentDetails.EntryStatusCode };
studentCourseJoin.ParentalInHigherEducation = new ParentalInHigherEducation { Code = viewModel.StudentDetails.ParentalInHigherEducationCode };
studentCourseJoin.School = new School { Code = viewModel.StudentDetails.SchoolCode };
studentCourseJoin.Institution = new Institution { Code = viewModel.StudentDetails.InstitutionCode };
studentCourseJoin.LastSchoolEndYear = viewModel.StudentDetails.LastSchoolEndYear;
studentCourseJoin.LastInstitutionEndYear = viewModel.StudentDetails.LastInstitutionEndYear;
// Blows up here trying to do an insert on the studentCourseJoin.Institution.
// But if I removed this one, then it will blow up on another one.
context.SaveChanges(true);
}
If anyone has ANY ideas please, they would help a lot.
Try adding those lines before calling SaveChanges:
ObjectStateEntry entry = context.ObjectStateManager.GetObjectStateEntry(studentCourseJoin);
entry.ChangeState(EntityState.Modified);
Update:
Try this for Institution instead:
studentCourseJoin.Institution = context.Institutions.FirstOrDefault(i => i.Code == viewModel.StudentDetails.InstitutionCode);