1

私は、SQL Server 2008/LinqToSQL/カスタムメイドのリポジトリを DAL として使用して、マルチユーザーのインターネット データベース駆動型 Web サイトに取り組んでいます。正しく悪用された場合、一貫性のないデータベース状態につながる可能性がある正規化の問題に遭遇しました。この問題をどのように処理するかを考えています。

問題: 複数の異なる会社が私の Web サイトにアクセスしています。彼らは私のウェブサイトで彼らのプロジェクトとクライアントを追跡できるはずです. プロジェクトの一部 (すべてではない) をクライアントに割り当てる必要があります。

これにより、次のデータベース スキーマが生成されます。

**Companies:**
ID
CompanyName

**Clients:** 
ID 
CompanyID (not nullable)
FirstName
LastName


**Projects:**
ID
CompanyID (not nullable)
ClientID (nullable)
ProjectName

これにより、次の関係が生まれます。

Companies-Clients (1:n)
Companies-Projects (1:n)
Clients-Projects(1:n)

ここで、悪意のあるユーザーは、たとえば、自分の CompanyID を持つプロジェクトを挿入する可能性がありますが、別のユーザーに属する ClientID を使用して、データベースを矛盾した状態のままにする可能性があります。

この問題は、データベース スキーマ全体で同様の方法で発生するため、可能であれば一般的な方法で解決したいと考えています。次の2つのアイデアがありました。

  • DAL の不整合につながる可能性のあるデータベース書き込みを確認します。これは一般的な方法ですが、更新クエリと作成クエリを実行する前に追加のデータベース クエリが必要になるため、パフォーマンスが低下します。

  • クライアントとプロジェクトの関係用に追加のテーブルを作成し、この方法で作成された関係が一貫していることを確認します。これには追加の選択クエリも必要ですが、最初のケースよりもはるかに少なくなります。一方で、それは一般的ではないため、特にデータベースにテーブルや依存関係を追加する場合は、長期的に見逃す可能性が高くなります。

あなたならどうしますか?私が見逃したより良い解決策はありますか?

編集:なぜ Projects テーブルに CompanyID があるのか​​不思議に思うかもしれません。これは、ユーザーがクライアントの有無にかかわらずプロジェクトを追加できるようにするためです。クライアントレス プロジェクトがどの会社 (したがって、どの Web サイト ユーザー) に属しているかを追跡する必要があります。これが、プロジェクトに CompanyID が必要な理由です。

4

5 に答える 5

2

私は後者を使用し、エンティティ間の許容可能な関係を定義する1つ以上のテーブルを用意します。

于 2009-08-05T08:24:37.447 に答える
1

あなたが持っている参照には循環性がないので、タイトルは誤解を招くことに注意してください.

あなたが持っているのは、データが競合する可能性です。それは異なります。


プロジェクト テーブルに「CompanyID」があるのはなぜですか? 関連する会社の ID は、リンク先のクライアントによって暗黙的に付与されます。あなたはそれを必要としません。

その列を削除すると、問題が解決しました。

さらに、クライアント テーブルの「名前」列の目的は何ですか? 会社の名前とは異なる 1 つの名前を持つクライアントを持つことはできますか?

それとも「クライアント」はその会社の人ですか?


編集:会社のないプロジェクトについての説明でわかりました。参照を分離しますが、複数の参照が行われないようにする制約なしでは、説明している問題を取り除くことはできません。

既存のテーブルに対する単純な制約は、プロジェクト行の CompanyID フィールドと ClientID フィールドの両方を同時に非 null にすることはできないということです。

于 2009-08-05T11:37:32.037 に答える
0

このようなテーブルを使用し、すべての新しいクエリを回避したい場合は、トリガーをテーブルに配置し、ユーザーが間違ったデータを含む行を挿入しようとすると、トリガーを停止します。よろしく、ヨルダン

于 2009-08-05T08:23:32.770 に答える
0

私の最初の考えは、「クライアントなし」という名前の会社ごとに特別なクライアントレコードを作成することでした。次に、ProjectテーブルからCompanyIdを削除し、プロジェクトにクライアントがない場合は、「通常の」クライアントレコードではなく「クライアントなし」レコードを使用します。このようなクライアントなしの処理が特殊な場合は、クライアントなしレコードにフラグを追加して、明示的に識別します。(名前が「クライアントなし」などであることに依存するのは嫌です-あいまいすぎます。)

そうすれば、一貫性のないデータを保存する方法がなくなるため、問題は解決します。

于 2009-08-06T16:41:38.710 に答える
0

最後に、実行時のオーバーヘッドが少なく、データベースを変更する必要なく、問題を解決する完全に汎用的なソリューションを実装しました。他の誰かが同じ問題を抱えている場合に備えて、ここで説明します。

まず、他のテーブルが複数のパスを介して参照している唯一のテーブルが Companies テーブルであるため、このアプローチは機能します。これは私のデータベースの場合であるため、作成/更新/削除される各エンティティの n:1 参照エンティティがすべて同じ会社を参照しているかどうか (またはまったく会社を参照していないかどうか) を確認するだけで済みます。

次のタイプのいずれかからすべての Linq エンティティを派生させることで、これを強制しています。

  1. SingleReferenceEntityBase - 標準。Companies テーブルへの参照が (推移的か非推移的かに関係なく) 1 つしかないかどうかのみを (リフレクションを介して) チェックします。この場合、会社テーブルへの参照が矛盾することはありません。

  2. MultiReferenceEntityBase - 上記の Projects テーブルなどの特殊なケース用。直接参照されているすべてのエンティティに、参照している会社 ID を尋ねます。矛盾がある場合は例外を発生させます。これにより、CRUD 操作ごとにいくつかの選択クエリが発生しますが、MultiReferenceEntities は SingleReferenceEntities よりもはるかにまれであるため、これは無視できます。

これらのタイプはどちらも「CheckReferences」を実装しており、すべての Linq エンティティに対して自動的に生成される OnValidate(System.Data.Linq.ChangeAction action) メソッドを部分的に実装することで、linq エンティティがデータベースに書き込まれるたびにそれを呼び出しています。

于 2009-08-07T09:56:02.160 に答える