使用Entity Framework更新数据库记录(延迟加载和虚拟属性) [英] Updating database record using Entity Framework (lazy loading and virtual properties)

查看:146
本文介绍了使用Entity Framework更新数据库记录(延迟加载和虚拟属性)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在努力更新现有讲师的数据库中的。

每位讲师都有他/她所教授的名称 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 中,我可以调用 _dataAccess.UpdateLecturer(SelectedLecturer)(其中 SelectedLecturer 被绑定在 XAML SelectedLecturer 属性可以由用户在 TextBox es和复选框

不幸的是这个方法:

  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();
}
}

但仍然课程不要重写旧值。






- 编辑2 -



为了回应@Slauma问题,我将介绍如何加载 SelectedLecturer (传递给 UpdateLecturer(讲师讲师)方法作为参数)。



我正在实施 MVVM 概念,所以我在解决方案中的视图项目与 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 =Horizo​​ntal>
< 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();
}

根据场景的细节,正确的解决方案可能会略有不同。 / p>

修改



如果讲师的课程。课程集合引用讲师(拥有讲师导航属性)当您将此集合的课程附加到上下文时,可能会出现问题,因为 lecturerInDb 已经附加并具有相同的键。您可以尝试更改最后一个 else 块,以便有希望解决问题:

  else 
{
//添加课程关系
var courseToAttach = new Course {Id_Course = course.Id_Course};
db.Courses.Attach(courseToAttach);
lecturerInDb.Courses.Add(courseToAttach);
}


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 TextBoxes 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:


-- 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();
    }
}

解决方案

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?

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.

Edit

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);
            }

这篇关于使用Entity Framework更新数据库记录(延迟加载和虚拟属性)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆