0

UserReportエンティティに対するCRUD操作を管理するクラスReportConfigurationManagerがあります。対象となる2つの操作は、「Get」と「SaveUpdate」です。どちらの場合も、DbContextがクエリの最後に配置されるように、操作をusingステートメントでラップします。

最終的に、これらのメソッドはWCFサービスの一部を形成しますが、サービス内で呼び出されることもあります。私の現在の問題は、ReportConfigurationManagerを直接呼び出す一連の単体テストを機能させることです。

新しいUserReportを作成して保存できます(エンティティにはデータベースにすでに存在するネストされたオブジェクトがいくつかあるため、解決に時間がかかりました。これらのそれぞれをコンテキストに「アタッチ」してから、Addを呼び出す必要がありました。正しく保存するためのUserReport。

私の問題は現在、アップデートにあります。

持っているにもかかわらず

    context.Configuration.ProxyCreationEnabled = false;
    context.Configuration.AutoDetectChangesEnabled = false;

ReportConfigurationManagerを使用するすべてのメソッドで、UserReportをアタッチするようになったとき、従来の「同じキーを持つオブジェクトがObjectStateManagerに既に存在します」で失敗しました(変更の追跡を無効にすると、これを処理できると思いましたか?)。

だから今私はここで見つけた次のコードを使用するように切り替えました

 public UserReport SaveUpdateUserReport(UserReport userReport)
    {
        using (var context = new ReportDataEF())
        {
            context.Configuration.ProxyCreationEnabled = false;
            context.Configuration.AutoDetectChangesEnabled = false;
            if (userReport.Id > 0)
            {
                {
                    UserReport oldReport = context.UserReports.Where(ur => ur.Id == userReport.Id).FirstOrDefault();
                    context.Entry(oldReport).CurrentValues.SetValues(userReport);
                }                  
            }
            else
            {
                //Need to attach everything to prevent EF trying to create duplicates in the database
                context.ReportTopTypes.Attach(userReport.ReportTopType);
                context.ReportWindows.Attach(userReport.ReportWindow);
                context.ReportSortOptions.Attach(userReport.ReportSortOption);

                foreach (var col in userReport.ReportColumnGroups)
                {
                    context.ReportColumnGroups.Attach(col);
                }

                context.ReportTemplates.Attach(userReport.ReportTemplate);

                //just add the new data
                context.UserReports.Add(userReport);
            }

            context.SaveChanges();
        }

        return userReport;
    }

私の懸念は、私のコードが面倒に見えることです-更新されたコピーを保存する前に、古いオブジェクトのコピーを取得する必要がありますか?また、SaveNewロジックにも納得できません。

それで、このアプローチは正しいですか、それとも上記を書くためのより良い方法がありますか?

進行中の他のものの詳細:

オブジェクトグラフをWCF経由で送信するためです。Eager Loadingを実装しました:

    public static DbQuery<ReportTemplate> IncludeAll(this DbQuery<ReportTemplate> self)
    {
        return self
            .Include("ReportColumnGroups.ReportColumns.ReportColumnType")
            .Include("ReportColumnGroups.ReportColumnType")
            .Include("ReportSortOptions.ReportSortColumns.ReportColumn.ReportColumnType")
            .Include("ReportSortOptions.ReportSortColumns.ReportSortType");
    }

    public static DbQuery<UserReport> IncludeAll(this DbQuery<UserReport> self)
    {
        return self
            .Include("ReportTemplate")
            .Include("ReportTopType")
            .Include("ReportWindow")
            .Include("ReportSortOption.ReportSortColumns.ReportColumn.ReportColumnType")
            .Include("ReportSortOption.ReportSortColumns.ReportSortType")
            .Include("ReportColumnGroups.ReportColumns.ReportColumnType")
            .Include("ReportColumnGroups.ReportColumnType");

    }


    public static DbQuery<ReportSortOption> IncludeAll(this DbQuery<ReportSortOption> self)
    {
        return self
            .Include("ReportSortColumns.ReportColumn.ReportColumnType")
            .Include("ReportSortColumns.ReportSortType");
    }

    public static DbQuery<ReportColumnGroup> IncludeAll(this DbQuery<ReportColumnGroup> self)
    {
        return self
            .Include("ReportColumn.ReportColumnType")
            .Include("ReportColumnType");
    }

    public static DbQuery<ReportColumn> IncludeAll(this DbQuery<ReportColumn> self)
    {
        return self
            .Include("ReportColumnType");
    }

    public static DbQuery<ReportSortColumn> IncludeAll(this DbQuery<ReportSortColumn> self)
    {
        return self
            .Include("ReportColumn.ReportColumnType")
            .Include("ReportSortType");
    }

次のように取得した静的なキャッシュデータのセットがあります。

using (var context = new ReportDataEF())
        {
            context.Configuration.ProxyCreationEnabled = false;
            context.Configuration.AutoDetectChangesEnabled = false;
            reportConfigurationData = new ReportingMetaData()
                                          {
                                              WatchTypes = context.WatchTypes.ToList(),
                                              ReportTemplates = context.ReportTemplates.IncludeAll().ToList(),
                                              ReportTopTypes = context.ReportTopTypes.ToList(),
                                              ReportWindows = context.ReportWindows.ToList(),
                                              ReportSortOptions =
                                                  context.ReportSortOptions.IncludeAll().ToList()
                                          };
        }

次のようにUserReportsを取得します。

public UserReport GetUserReport(int userReportId)
    {
        using (var context = new ReportDataEF())
        {
            context.Configuration.ProxyCreationEnabled = false;
            context.Configuration.AutoDetectChangesEnabled = false;
            var visibleReports =
                context.UserReports.IncludeAll().Where(ur => ur.Id == userReportId).FirstOrDefault();
            return visibleReports;
        }
    }

私が関わっているテストは、DBから既存のUserReportを取得し、そのReportTemplateプロパティとReportColumnGroupsプロパティを静的データクラスのオブジェクトで更新してから、更新されたUserReportを保存しようとします。

Ladislavの回答のコードを使用すると、UserReportを添付しようとすると失敗します。おそらく、添付したオブジェクトの1つがデータベースにすでに存在しているためです。

4

1 に答える 1

1

はい、別の方法があります。まず、EFは部分的にアタッチされたオブジェクトグラフをサポートしていないためAttachAddコンテキストによってまだ追跡されていないグラフ内のすべてのエンティティをアタッチまたは追加するという副作用があります。これにより、挿入コードが大幅に簡素化されます。

public UserReport SaveUpdateUserReport(UserReport userReport)
{
    using (var context = new ReportDataEF())
    {
        context.Configuration.ProxyCreationEnabled = false;
        context.Configuration.AutoDetectChangesEnabled = false;

        // Now all entities in the graph are attached in unchanged state
        context.ReportTopTypes.Attach(userReport);

        if (userReport.Id > 0 && 
            context.UserReports.Any(ur => ur.Id == userReport.Id))
        {
            context.Entry(userReport).State = EntityState.Modified;
        }
        else
        {
            context.Entry(userReport).State = EntityState.Added;
        }

        context.SaveChanges();
    }

    return userReport;
}

これは、元のコードと同等です。ユーザーレポートを再度ロードする必要はありません。DBに存在するかどうかを確認するだけです。このコードには多くの問題があります。たとえば、他の関連オブジェクトを変更した場合、現在の状態は。であるため、データベースに永続化されませんUnchanged。関係を変更する必要がある場合は、さらに複雑になる可能性があります。

于 2012-06-20T12:11:28.503 に答える