1

データベース内の既存の講師のデータを更新するのに苦労しています。
すべての講師にはNameAcademicDegree、および彼/彼女が教えるコース ( Courses ==Lessons ) があります。

実際には、より多くのプロパティがありますがclass Lecturer、それらは関連していません。簡単にするために、 POCOクラスが次のように定義されていると仮定します。

// 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_Lecturerlecturer.Id_Lecturerlecturer.Namelecturer.AcademicDegreelecturer.Courses

ViewModelで呼び出すことができるため、非常に便利なメソッドです_dataAccess.UpdateLecturer(SelectedLecturer)(SelectedLecturerXAMLにバインドされています。プロパティはes およびSelectedLecturerでユーザーが設定できます)。TextBoxCheckbox

残念ながら、この方法:

    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_IdおよびAcademicDegree)を更新します。 Coursesdb.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();
    }
}

ただし、コースは古い値を上書きしません。


-- 編集 2 --

@Slauma の質問に応えて、どのようにロードしたか (メソッドに引数としてSelectedLecturer渡される)について説明します。UpdateLecturer(Lecturer lecturer)

私はMVVMの概念を実装しているので、ソリューション内にプロジェクトを表示し、に設定しています。ビューには、データベースから取得したすべての講師のリストがあります。は次のようにバインドされます。DataContextLecturerListViewModelDataGridDataGrid

<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次の方法で、コンストラクターでデータベースからフェッチされます。

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

1 に答える 1

2

状態を に設定するModifiedと、エンティティSetValuesのスカラー プロパティのみが更新されます。コレクション (スカラー プロパティではない) をLecturer更新するには、さらに作業が必要です。Coursesコースがコレクションから削除された可能性がある、コースが追加された可能性がある、またはコースのスカラー プロパティが変更された可能性がある場合を処理する必要があります。

また、コレクションを更新する方法は、コースが講師に依存しているかどうかによって異なります。コースがコレクションから削除された場合、コースをデータベースから削除する必要がありますか?それとも、講師とコースの間の関係のみを削除する必要がありますか? コレクションに追加されたときに新しいコースを作成する必要がありますか、それとも関係を確立するだけでよいですか?

コースを削除する必要がなく、新しいコースを作成しない場合、Update メソッドは次のようになります。

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コレクション内のコースにへの参照がある場合lecturer(Lecturerおそらくナビゲーション プロパティがある場合)、このコレクションからコースをコンテキストにアタッチするlecturerInDbと、 が既にアタッチされていて同じキーを持っているため、問題が発生する可能性があります。elseうまくいけば問題を解決するために、最後のブロックを次のように変更することができます:

            else
            {
                // Add courses relationships
                var courseToAttach = new Course { Id_Course = course.Id_Course };
                db.Courses.Attach(courseToAttach);
                lecturerInDb.Courses.Add(courseToAttach);
            }
于 2012-11-14T08:52:33.770 に答える