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

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

问题描述

我正在努力更新数据库中现有讲师的数据.
每个讲师都有自己教授的姓名学术学位课程(课程==课程).

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.Namelecturer.AcademicDegreelecturer.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 属性可以由用户在 TextBoxes 和 复选框).

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).

不幸的是这种方法:

    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_LecturerNameAcademic_Degree_IdAcademicDegree)除了 Coursesdb.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?

类似的问题:

-- 编辑--

我也尝试过这种方式(想法来自 这篇文章):

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>

LecturersLecturerListViewModel 构造函数中以这种方式从数据库中获取:

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

推荐答案

将状态设置为 ModifiedSetValues 只会更新 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屋!

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