使用Entity Framework更新数据库记录(延迟加载和虚拟属性) [英] Updating database record using Entity Framework (lazy loading and virtual properties)
问题描述
我正在努力更新现有讲师的数据库中的。
每位讲师都有他/她所教授的名称, AcademicDegree 和课程(课程= =教训)。
事实上,在 class Lecturer
中有更多的属性,但它们不相关。为了简单起见,我们假定 POCO 课程的定义如下:
// POCO类(Entity Framework Reverse Engineering Code First)
public class讲师
{
public Lecturer()
{
this.Courses = new List< Course>();
}
public int Id_Lecturer {get;组; } //主键
public string Name {get;组; }
public int? Academic_Degree_Id {get;组; }
public virtual AcademicDegree AcademicDegree {get;组; }
public virtual ICollection< Course>课程{get;组; }
}
在数据访问层 code> void UpdateLecturer(讲师讲师)应该更新 Id_Lecturer
等于 lecturer.Id_Lecturer
(使用 lecturer.Name
, lecturer.AcademicDegree
和讲师。课程
)。
这是非常方便的方法,因为在 ViewModel 中,我可以调用 不幸的是这个方法: 更新所有内容(即 为什么?如何解决? 类似问题: - 编辑 - 我也试过这样(想法来自这篇文章): 但仍然课程不要重写旧值。 - 编辑2 - 为了回应@Slauma问题,我将介绍如何加载 我正在实施 MVVM 概念,所以我在解决方案中的视图项目与 设置状态为 另外更新收藏的方式取决于课程取决于讲师。课程从系统中删除后,是否需要从数据库中删除,或者只需要删除讲师和课程之间的关系?是否需要创建一个新课程,当它被添加到集合或只需要建立关系? 如果没有课程必须删除,没有新的更新方法可能如下所示: 根据场景的细节,正确的解决方案可能会略有不同。 / p> 修改 如果 I'm struggling with updating existing lecturer's data in database. In fact there are more properties in In Data Access Layer I have method It's very handy method because in ViewModel I can call Unfortunatelly this method: updates everything (i.e. Why? How can I fix it? Similar problems: -- Edit -- I also tried this way (idea came from this post): but still Courses don't overwrite old values. -- Edit 2 -- In response to the @Slauma question I'll describe how I loaded I'm implementing MVVM concept so I have View project within my Solution with
Setting the state to Also the way to update the collection depends on a course being dependent on the lecturer or not. Does the course need to be deleted from the database when it has been removed from the collection or does only the relationship between lecturer and course need to be removed? Does a new course need to be created when it has been added to the collection or does only a relationship need to be established? If no courses must be deleted and no new courses be created the Update method could look like this: Depending on the details of your scenario the correct solution might be slightly different. Edit If the courses in the
这篇关于使用Entity Framework更新数据库记录(延迟加载和虚拟属性)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋! _dataAccess.UpdateLecturer(SelectedLecturer)
(其中 SelectedLecturer
被绑定在 XAML SelectedLecturer
属性可以由用户在 TextBox
es和复选框$ c中设置$ c $)
public void UpdateLecturer(Lecturer lecturer)
{
using(var db = new AcademicTimetableDbContext())
{
//查找主键设置为' lecturer.Id_Lecturer':
var lect = db.Lecturers.Find(lecturer.Id_Lecturer);
//如果没有找到:
if(lect == null)
return;
//复制所有可能的属性:
db.Entry(lect).CurrentValues.SetValues(lecturer);
//除课程之外,所有内容都被复制。为什么?!
//我试图添加这个,但它没有帮助:
// var stateMgr =(db as IObjectContextAdapter).ObjectContext.ObjectStateManager;
// var stateEntry = stateMgr.GetObjectStateEntry(lect);
// stateEntry.SetModified();
db.SaveChanges();
}
}
Id_Lecturer
,姓名
, Academic_Degree_Id
和 AcademicDegree
)除 课程
在 db.SaveChanges()
之后不变。
public void UpdateLecturer(Lecturer lecturer)
{
using(var db = new AcademicTimetableDbContext ())
{
if(lecturer == null)
return;
DbEntityEntry<讲师> entry = db.Entry(lecturer);
if(entry.State == EntityState.Detached)
{
讲师attachEntity = db.Set< Lecturer>()。Find(lecturer.Id_Lecturer);
if(attachedEntity == null)
entry.State = EntityState.Modified;
else
db.Entry(attachedEntity).CurrentValues.SetValues(lecturer);
}
db.SaveChanges();
}
}
SelectedLecturer
(传递给 UpdateLecturer(讲师讲师)
方法作为参数)。
DataContext
设置为 LecturerListViewModel
。在视图中有 DataGrid
,其中包含从数据库中提取的所有讲师的列表。 DataGrid
以这种方式绑定:
< DataGrid AutoGenerateColumns =FalseName =LecturersDataGridHeadersVisibility =ColumnIsReadOnly =TrueItemsSource ={Binding Lecturers,Mode = TwoWay}SelectedItem ={Binding SelectedLecturer,Mode = TwoWay}>
< DataGrid.Columns>
< DataGridTextColumn Header =NameBinding ={Binding Name}/>
< DataGridTextColumn Header =学术学位Binding ={Binding AcademicDegree.Degree_Name}/>
< DataGridTemplateColumn>
< DataGridTemplateColumn.CellTemplate>
< DataTemplate>
< StackPanel Orientation =Horizontal>
< Button Content =EditClick =EditButtonClick/>
< Button Content =DeleteCommand ={Binding DataContext.RemoveLecturer,ElementName = LecturersDataGrid}/>
< / StackPanel>
< / DataTemplate>
< /DataGridTemplateColumn.CellTemplate>
< / DataGridTemplateColumn>
< /DataGrid.Columns>
< / DataGrid>
讲师
从 LecturerListViewModel
这样构造函数:
// /
/// LecturerListViewModel类中的代码:
///
//数据库中的所有讲师。
public ObservableCollection<讲师>讲师
//构造函数
public LecturerListViewModel()
{
//调用数据访问层:
Lecturers = new ObservableCollection< Lecturer>(_ dataAccess .GetAllLecturers());
//其他一些东西在这里...
}
私人讲师_selectedLecturer;
//目前选择的行与讲师。
public Lecturer SelectedLecturer
{
get {return _selectedLecturer; }
set {SetProperty(out _selectedLecturer,value,x => x.SelectedLecturer);
}
///
///数据访问层(DataAccess类内):
///
public IEnumerable< ;讲师> 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();
}
}
修改
和 SetValues
只更新讲师的标量属性
实体。更新课程
集合(不是标量属性)需要更多的工作。你必须处理一个课程可以从集合中删除的情况,一个课程可能已被添加,或一个课程的标量属性可能被修改。
public void UpdateLecturer(讲师讲师)
{
使用(var db = new AcademicTimetableDbContext())
{
if(lecturer == null)
return;
var lecturerInDb = db.Lecturers
.Include(l => l.Courses)
.Single(l => l.Id_Lecturer == lecturer.Id_Lecturer);
//更新讲师
db.Entry(lecturerInDb).CurrentValues.SetValues(lecturer);
//删除课程关系
foreach(var courseInDb in lecturerInDb.Courses.ToList())
if(!lecturer.Courses.Any(c => c.Id_Course == courseInDb.Id_Course))
lecturerInDb.Courses.Remove(courseInDb);
foreach(var course in lecturer.Courses)
{
var courseInDb = lecturerInDb.Courses.SingleOrDefault(
c => c.Id_Course == course.Id_Course );
if(courseInDb!= null)
//更新课程
db.Entry(courseInDb).CurrentValues.SetValues(course);
else
{
//添加课程关系
db.Courses.Attach(course);
lecturerInDb.Courses.Add(course);
}
}
}
db.SaveChanges();
}
讲师的课程。课程
集合引用讲师
(拥有讲师
导航属性)当您将此集合的课程附加到上下文时,可能会出现问题,因为 lecturerInDb
已经附加并具有相同的键。您可以尝试更改最后一个 else
块,以便有希望解决问题:
else
{
//添加课程关系
var courseToAttach = new Course {Id_Course = course.Id_Course};
db.Courses.Attach(courseToAttach);
lecturerInDb.Courses.Add(courseToAttach);
}
Every lecturer has Name, AcademicDegree and Courses which are taught by him/her (Courses==Lessons). 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; }
}
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
). _dataAccess.UpdateLecturer(SelectedLecturer)
(where SelectedLecturer
is binded in XAML; SelectedLecturer
properties can be set by user in TextBox
es and Checkbox
). 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();
}
}
Id_Lecturer
, Name
, Academic_Degree_Id
and AcademicDegree
) except Courses
which are unchanged after db.SaveChanges()
.
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();
}
}
SelectedLecturer
(which is passed to UpdateLecturer(Lecturer lecturer)
method as an argument). 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();
}
}
Modified
and SetValues
only updates scalar properties of the Lecturer
entity. Updating the Courses
collection (which is not a scalar property) needs more work. You must handle the cases that a course could have been removed from the collection, a course could have been added or the scalar properties of a course could have been modified.public void UpdateLecturer(Lecturer lecturer)
{
using(var db = new AcademicTimetableDbContext())
{
if (lecturer == null)
return;
var lecturerInDb = db.Lecturers
.Include(l => l.Courses)
.Single(l => l.Id_Lecturer == lecturer.Id_Lecturer);
// Update lecturer
db.Entry(lecturerInDb).CurrentValues.SetValues(lecturer);
// Remove courses relationships
foreach (var courseInDb in lecturerInDb.Courses.ToList())
if (!lecturer.Courses.Any(c => c.Id_Course == courseInDb.Id_Course))
lecturerInDb.Courses.Remove(courseInDb);
foreach (var course in lecturer.Courses)
{
var courseInDb = lecturerInDb.Courses.SingleOrDefault(
c => c.Id_Course == course.Id_Course);
if (courseInDb != null)
// Update courses
db.Entry(courseInDb).CurrentValues.SetValues(course);
else
{
// Add courses relationships
db.Courses.Attach(course);
lecturerInDb.Courses.Add(course);
}
}
}
db.SaveChanges();
}
lecturer.Courses
collection have a reference to the lecturer
(having a Lecturer
navigation property maybe) you could have problems when you attach a course from this collection to the context because lecturerInDb
is already attached and has the same key. You can try to change the last else
block like so to solve the problem hopefully: else
{
// Add courses relationships
var courseToAttach = new Course { Id_Course = course.Id_Course };
db.Courses.Attach(courseToAttach);
lecturerInDb.Courses.Add(courseToAttach);
}