使用实体框架更新数据库记录(延迟加载和虚拟属性) [英] Updating database record using Entity Framework (lazy loading and virtual properties)
问题描述
我正在努力更新数据库中现有讲师的数据.
每个讲师都有自己教授的姓名、学术学位和课程(课程==课程).
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).
实际上class Lecturer
中还有更多属性,但它们并不相关.为简单起见,我们假设 POCO 类是这样定义的:
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; }
}
在 数据访问层 我有方法 void UpdateLecturer(Lecturer Lecturer)
应该更新 Id_Lecturer
等于 lecturer 的讲师.Id_Lecturer
(使用 lecturer.Name
、lecturer.AcademicDegree
和 lecturer.Courses
).
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
).
这是非常方便的方法,因为在 ViewModel 我可以调用 _dataAccess.UpdateLecturer(SelectedLecturer)
(其中 SelectedLecturer
绑定在 XAML; SelectedLecturer
属性可以由用户在 TextBox
es 和 复选框
).
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
).
不幸的是这种方法:
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
和 AcademicDegree
)除了 Courses
在 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?
类似的问题:
- 更新-an-entity-in-entity-framework李>
- 如何-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
-- 编辑--
我也尝试过这种方式(想法来自 这篇文章):
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();
}
}
但仍然课程不会覆盖旧值.
-- 编辑2--
在回答@Slauma 问题时,我将描述我是如何加载 SelectedLecturer
的(它作为参数传递给 UpdateLecturer(Lecturer Lecturer)
方法).
In response to the @Slauma question I'll describe how I loaded SelectedLecturer
(which is passed to UpdateLecturer(Lecturer lecturer)
method as an argument).
我正在实施 MVVM 概念,所以我有 View我的 Solution 中的项目,其中 DataContext
设置为 LecturerListViewModel
.在 View 中有 DataGrid
,其中包含从数据库中获取的所有讲师的列表.DataGrid
是这样绑定的:
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
在 LecturerListViewModel
构造函数中以这种方式从数据库中获取:
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
和 SetValues
只会更新 Lecturer的标量属性代码>实体.更新
Courses
集合(它不是一个标量属性)需要做更多的工作.您必须处理课程可能已从集合中删除、课程可能已添加或课程的标量属性可能已修改的情况.
Setting the state to 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.
此外,更新集合的方式取决于课程是否依赖于讲师.课程从集合中删除后是否需要从数据库中删除,还是只需要删除讲师和课程之间的关系?新课程添加到集合时是否需要创建新课程,还是只需要建立关系?
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?
如果不需要删除任何课程并且没有创建新课程,则 Update 方法可能如下所示:
If no courses must be deleted and no new courses be created the Update method could look like this:
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();
}
根据您的场景细节,正确的解决方案可能会略有不同.
Depending on the details of your scenario the correct solution might be slightly different.
编辑
如果 lecturer.Courses
集合中的课程有对 lecturer
的引用(可能具有 Lecturer
导航属性),您可以当您将此集合中的课程附加到上下文时会出现问题,因为 lecturerInDb
已经附加并且具有相同的键.您可以尝试像这样更改最后一个 else
块以希望解决问题:
If the courses in the 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);
}
这篇关于使用实体框架更新数据库记录(延迟加载和虚拟属性)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!