I'm struggling with updating existing lecturer's data in database.
Every lecturer has Name, AcademicDegree and Courses which are taught by him/her (Courses==Lessons).
In fact there are more properties in class Lecturer
but they are not relevant. For simplicity lets assume that POCO class is defined this way:
// POCO class (Entity Framework Reverse Engineering Code First)
public class Lecturer
{
public Lecturer()
{
this.Courses = new List<Course>();
}
public int Id_Lecturer { get; set; } // Primary Key
public string Name { get; set; }
public int? Academic_Degree_Id { get; set; }
public virtual AcademicDegree AcademicDegree { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
In Data Access Layer I have method void UpdateLecturer(Lecturer lecturer)
which should update lecturer whose Id_Lecturer
is equal to lecturer.Id_Lecturer
(using lecturer.Name
, lecturer.AcademicDegree
and lecturer.Courses
).
It's very handy method because in ViewModel I can call _dataAccess.UpdateLecturer(SelectedLecturer)
(where SelectedLecturer
is binded in XAML; SelectedLecturer
properties can be set by user in TextBox
es and Checkbox
).
Unfortunatelly this method:
public void UpdateLecturer(Lecturer lecturer)
{
using(var db = new AcademicTimetableDbContext())
{
// Find lecturer with Primary Key set to 'lecturer.Id_Lecturer':
var lect = db.Lecturers.Find(lecturer.Id_Lecturer);
// If not found:
if (lect == null)
return;
// Copy all possible properties:
db.Entry(lect).CurrentValues.SetValues(lecturer);
// Everything was copied except 'Courses'. Why?!
// I tried to add this, but it doesn't help:
// var stateMgr = (db as IObjectContextAdapter).ObjectContext.ObjectStateManager;
// var stateEntry = stateMgr.GetObjectStateEntry(lect);
// stateEntry.SetModified();
db.SaveChanges();
}
}
updates everything (i.e. Id_Lecturer
, Name
, Academic_Degree_Id
and AcademicDegree
) except Courses
which are unchanged after db.SaveChanges()
.
Why? How can I fix it?
Similar problems:
- updating-an-entity-in-entity-framework
- how-do-can-i-attach-a-entity-framework-object-that-isnt-from-the-database
- using-dbcontext-in-ef-feature-ctp5-part-4-add-attach-and-entity-states.aspx
- entity-framework-updating-with-related-entity
- entity-framework-code-first-no-detach-method-on-dbcontext
-- Edit --
I also tried this way (idea came from this post):
public void UpdateLecturer(Lecturer lecturer)
{
using (var db = new AcademicTimetableDbContext())
{
if (lecturer == null)
return;
DbEntityEntry<Lecturer> entry = db.Entry(lecturer);
if (entry.State == EntityState.Detached)
{
Lecturer attachedEntity = db.Set<Lecturer>().Find(lecturer.Id_Lecturer);
if (attachedEntity == null)
entry.State = EntityState.Modified;
else
db.Entry(attachedEntity).CurrentValues.SetValues(lecturer);
}
db.SaveChanges();
}
}
but still Courses don't overwrite old values.
-- Edit 2 --
In response to the @Slauma question I'll describe how I loaded SelectedLecturer
(which is passed to UpdateLecturer(Lecturer lecturer)
method as an argument).
I'm implementing MVVM concept so I have View project within my Solution with DataContext
set to LecturerListViewModel
. In View there is DataGrid
with list of all lecturers fetched from databse. DataGrid
is binded this way:
<DataGrid AutoGenerateColumns="False" Name="LecturersDataGrid" HeadersVisibility="Column" IsReadOnly="True" ItemsSource="{Binding Lecturers,Mode=TwoWay}" SelectedItem="{Binding SelectedLecturer, Mode=TwoWay}">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTextColumn Header="Academic degree" Binding="{Binding AcademicDegree.Degree_Name}" />
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Content="Edit" Click="EditButtonClick"/>
<Button Content="Delete" Command="{Binding DataContext.RemoveLecturer, ElementName=LecturersDataGrid}" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Lecturers
are fetched from database in LecturerListViewModel
constructor in this way:
///
/// Code within LecturerListViewModel class:
///
// All lecturers from database.
public ObservableCollection<Lecturer> Lecturers
// Constructor
public LecturerListViewModel()
{
// Call to Data Access Layer:
Lecturers = new ObservableCollection<Lecturer>(_dataAccess.GetAllLecturers());
// Some other stuff here...
}
private Lecturer _selectedLecturer;
// Currently selected row with lecturer.
public Lecturer SelectedLecturer
{
get { return _selectedLecturer; }
set { SetProperty(out _selectedLecturer, value, x => x.SelectedLecturer); }
}
///
/// Data Access Layer (within DataAccess class):
///
public IEnumerable<Lecturer> GetAllLecturers()
{
using (var dbb = new AcademicTimetableDbContext())
{
var query = from b
in dbb.Lecturers.Include(l => l.AcademicDegree).Include(l => l.Timetables).Include(l => l.Courses)
select b;
return query.ToList();
}
}
db.Courses.Attach(course);
. Exception: An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key. I tried to remove linedb.Courses.Attach(course);
from code but then I have exception during executingdb.SaveChanges()
. Exception: The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects. – Matthewmatthews