2009-05-23 10 views
2

私のデータベースには、ページ(PageId、他のデータ)とPageTag(PageId、Tag)の2つのテーブルが外部キーで接続されています。私はLINQを使用して、ページを親として、タグを子コレクションとして(1対多の関係)これらの表のクラスを生成しました。ページクラスからデータベースからPageTagレコードを削除するようにマークする方法はありますか?LINQ to SQLの子コレクションからレコードを削除するにはどうすればよいですか?

クイックClearification:

私は親のDataContextはないの前に、()でSubmitChangesを呼び出したときに子オブジェクトを削除することにしたいです。 TagStringは、Pageオブジェクトの他のプロパティとまったく同じように動作します。

私は、次のようなコードを有効にしたいと思います:ここで

Page page = mDataContext.Pages.Where(page => page.pageId = 1); 
page.TagString = "new set of tags"; 

//Changes have not been written to the database at this point. 

mDataContext.SubmitChanges(); 

//All changes should now be saved to the database. 

は詳細に私の状況です:
簡単にタグのコレクションを操作できるようにするために、私はにプロパティを追加しました文字列としてタグのコレクションを扱うPageオブジェクト:

public string TagString { 
    get { 
     StringBuilder output = new StringBuilder(); 
     foreach (PageTag tag in PageTags) { 
      output.Append(tag.Tag + " "); 
     } 

     if (output.Length > 0) { 
      output.Remove(output.Length - 1, 1); 
     } 

     return output.ToString(); 
    } 
    set { 
     string[] tags = value.Split(' '); 
     PageTags.Clear(); 
     foreach (string tag in tags) { 
      PageTag pageTag = new PageTag(); 
      pageTag.Tag = tag; 
      PageTags.Add(pageTag); 
     } 
    } 
} 

基本的には、アイデアは、タグの文字列が、このプロパティに送信されたときに、オブジェクトの現在のタグが削除され、新しいセットを内に生成されていることです彼らの場所。

私が遭遇してる問題は、この行ということです:

PageTags.Clear(); 

が実際に変更が提出されているデータベースから古いタグを削除しません。

周囲を見渡すと、データコンテキストクラスのDeleteOnSubmitメソッドを呼び出すことが正しいと思われます。しかし、私はPageクラスからDataContextクラスにアクセスすることはできません。

ページクラス内からデータベースから削除する子要素をマークする方法について知っている人はいますか?

答えて

5

もう少し研究を重ねるうちに、私は解決策を見つけることができたと信じています。コレクションから削除されたオブジェクトを削除マーク付けするには、Association属性のDeleteOnNullパラメーターを使用します。

このパラメータは、2つのテーブル間の関係がOnDelete Cascadeでマークされている場合にtrueに設定されます。

残念ながら、この属性をデザイナ内から設定する方法はなく、* DataContext.csファイルの部分クラス内からこの属性を設定する方法はありません。カスケード削除を有効にしないで設定する唯一の方法は、* DataContext.designer.csファイルを手動で編集することです。

私の場合、これはページの関連性を発見し、DeleteOnNullプロパティを追加する意味:

[Association(Name="Page_PageTag", Storage="_Page", ThisKey="PageId", OtherKey="iPageId", IsForeignKey=true)] 
public Page Page 
{ 
    ... 
} 

そしてDeleteOnNull属性を追加:必要な属性が追加されることを

[Association(Name="Page_PageTag", Storage="_Page", ThisKey="PageId", OtherKey="iPageId", IsForeignKey=true, DeleteOnNull = true)] 
public Page Page 
{ 
    ... 
} 

注意をPageTagクラスのPageプロパティ。これ以外の方法ではありません。
Beth Massi -- LINQ to SQL and One-To-Many Relationships
Dave Brace -- LINQ to SQL: DeleteOnNull

+0

Aaron、洞察力のおかげです。しかし、リポジトリに2行のコードを書いて削除の記録をマークするほうが簡単ではないでしょうか? –

+0

リポジトリに2行のコードが残ることはありません。 UpdateTags(Page page、string tagString)メソッドをリポジトリに追加する必要がありますが、モデルバインドではうまく動作しません。 – AaronSieb

+0

これは設定がとても簡単で、On Delete Cascadeにリンクされていることがわかったので、デザイナーファイルを手で編集するのを避けることができます。 – AaronSieb

0

LinqからSQLへのエンティティダイアグラムに、PageとPageTagsテーブルをリンクする関係はありますか?そうしないと、ページクラスからPageTagsクラスを見ることができないのです。

PageTagsデータベーステーブルの外部キーがNULLを許可に設定されている場合、SQL Serverでリレーションシップを作成した場合でも、テーブルをデザイナにドラッグするとリンクが作成されません。

+0

協会が整備されている、と私はプログラム的に子コレクションにアクセスすることができます

も参照してください。問題は、コレクションに対する変更(特にオブジェクトの削除)がデータベースに保持されないことです。 – AaronSieb

+0

愚かな質問ですが、変更をコミットしていますか? –

+0

はい。変更をコミットすると、新しいタグが古いタグと重複するため、重複したキーエラーが発生します。 – AaronSieb

0

これは、ORマッピングが多少毛むくじゃらするような領域の1つです。このTagStringプロパティを提供すると、物が少し便利になりますが、長期的には誰かがTagStringプロパティを利用するときに本当に起こっていることがわかりません。実行中のデータ変更を隠すことで、DataContextの範囲内でPageエンティティを使用せずに誰かがTagStringを簡単に見つけて設定できるようになり、バグを見つけにくくなる可能性があります。

より良い解決策は、L2SモデルデザイナでPageクラスを追加し、PageTagをDataContextの範囲内のTagsプロパティで直接編集することです。 TagStringプロパティを読み込み専用にしておくと、作成することができます(それでもやはり便利です)。しかし、そのプロパティの設定に関する混乱や困難を解消します。この種の変更は、意図を明確にし、起こっていることと、Pageオブジェクトの消費者が必要とすることを明確にします。

タグはDataContextに添付されている限り、Pageオブジェクトのプロパティであるため、そのコレクションの変更によって、RemoveまたはAddの呼び出しに応じてデータベースの削除または挿入が正しくトリガーされます。

+0

PageTagをコレクションから削除した直後に削除するつもりはありません。親DataContextがSubmitChanges()を呼び出すときに削除されるようにしています。 PageTagStringプロパティの更新セマンティクスは、オブジェクトの他のデータプロパティと異なるものではありません。 – AaronSieb

0

アーロン、

はどうやらあなたはそれぞれについてDeleteOnSubmitを呼び出して、あなたのPageTagレコードを通してループする必要があります。 Linq to SQLは、SubmitChangesを呼び出すときに一度にすべてのレコードを削除する集約クエリを作成する必要があるため、オーバーヘッドは最小限に抑える必要があります。あなたのPageTag部分クラスにDataContextのメンバーを追加

foreach (PageTag tag in PageTags) 
    myDataContext.DeleteOnSubmit(tag); 
+0

PageクラスからDataContextにアクセスするにはどうすればよいですか? – AaronSieb

+0

PageTagパーシャルクラスにDataContextメンバを追加します。 部分クラスPageTag {DataClassesDataContext myDataContext = 新しいDataClassesDataContext()。 公開ストリングタグ文字列{ など。 –

0

アーロンと

PageTags.Clear(); 

を交換してください。

partial class PageTag 
{ 
    DataClassesDataContext myDataContext = new DataClassesDataContext(); 

    public string TagString { 

など.etc。ロバート・ハーヴェイの要請に掲載

+0

これは、PageTagを取得するために使用される親DataContextからの独立したDataContextではないでしょうか?私は*親* DataContextでSubmitChangesを呼び出すと削除が起きるとは思わない。 – AaronSieb

+0

それは動作します。 DataContextをデータベースへの別の接続と見なしてください。 これは奇妙に見える理由は、アーキテクチャーの観点からは、PageTag部分クラスの外部ではなく、PageTagオブジェクトを操作する必要があるからです。 –

+0

「添付されていないエンティティを削除できません」という例外が表示されます。私は2番目のDataConnectionからそれらを削除しようとします。これは、元のDataContextが送信されるまで変更を延期するという要件を満たすようには見えません。 – AaronSieb

0

大きなコードサンプル:

DataContext.csファイル:

namespace MyProject.Library.Model 
{ 
    using Tome.Library.Parsing; 
    using System.Text; 

    partial class Page 
    { 
     //Part of Robert Harvey's proposed solution. 
     MyDataContext mDataContext = new TomeDataContext(); 

     public string TagString { 
      get { 
       StringBuilder output = new StringBuilder(); 
       foreach (PageTag tag in PageTags) { 
        output.Append(tag.Tag + " "); 
       } 

       if (output.Length > 0) { 
        output.Remove(output.Length - 1, 1); 
       } 

       return output.ToString(); 
      } 
      set { 
       string[] tags = value.Split(' '); 
       //Original code, fails to mark for deletion. 
       //PageTags.Clear(); 

       //Robert Harvey's suggestion, thorws exception "Cannot remove an entity that has not been attached." 
       foreach (PageTag tag in PageTags) { 
        mDataContext.PageTags.DeleteOnSubmit(tag); 
       } 

       foreach (string tag in tags) { 
        PageTag PageTag = new PageTag(); 
        PageTag.Tag = tag; 
        PageTags.Add(PageTag); 
       } 
      } 
     } 

     private bool mIsNew; 
     public bool IsNew { 
      get { 
       return mIsNew; 
      } 
     } 

     partial void OnCreated() { 
      mIsNew = true; 
     } 

     partial void OnLoaded() { 
      mIsNew = false; 
     } 
    } 
} 

リポジトリ方法:

public void Save() { 
    mDataContext.SubmitChanges(); 
} 

public Page GetPage(string pageName) { 
    Page page = 
     (from p in mDataContext.Pages 
     where p.FileName == pageName 
     select p).SingleOrDefault(); 

    return page; 
} 

使用法:

[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult Edit(string pageName, FormCollection formValues) { 
    Page updatedPage = mRepository.GetPage(pageName); 

    //TagString is a Form value, and is set via UpdateModel. 
    UpdateModel(updatedPage, formValues.ToValueProvider()); 
    updatedPage.FileName = pageName; 

    //At this point NO changes should have been written to the database. 

    mRepository.Save(); 

    //All changes should NOW be saved to the database. 

    return RedirectToAction("Index", "Pages", new { PageName = pageName }); 
} 
1

申し訳ありませんが、私の悪い。それは動作しません。

ページクラスではなく、リポジトリでこれを行う必要があるようです。そこで、元のデータコンテキストにアクセスできます。

元のデータコンテキストに「添付」する方法がありますが、それを行うまでにはかなりのコードの匂いになっています。

+0

ありがとうございます。 LINQはこのような構造に対応していないようです:/ – AaronSieb

関連する問題