1

ID 列を使用し、pk を生成することに慣れています。ID 列のスコープを設定したり、外部キー内で一意になるように計算したりする方法はありますか?

たとえば、ID 列を使用すると、テーブルは次のようになります。

| UserId | WidgetId | <-- Table Widget, WidgetId is identity column & pk
---------------------
|      1 |        1 |
|      1 |        2 |
|      1 |        3 |
|      2 |        4 |
|      2 |        5 |
|      2 |        6 |

次のようなことを達成したい場合はどうすればよいですか。

| UserId | WidgetId | <-- Table Widget, both UserId and WidgetId compose the pk
---------------------
|      1 |        1 |
|      1 |        2 |
|      1 |        3 |
|      2 |        1 |
|      2 |        2 |
|      2 |        3 |

EF では、id や計算など、データベースで生成された列の値を使用できることを知っています。私の質問は、このような計算列をどのように達成できるでしょうか?

ユーザーが持っている最大の WidgetId を見つけて 1 ずつインクリメントすることで、アプリケーションで実行できることがわかりました。しかし、これは正しい方法ではないように感じます。

トリガーを使用してこれを行うことも可能かもしれないと思いますが、トリガーを から db に追加するDbContextと、RDBMS に依存することになりますよね?

EF が使用しているバックエンドへの依存を少なくする、より良い方法はありますか?

アップデート

より明確にするために、上記のモデルでは、Widget と User の間に意図的に識別関係があります。UserId は User テーブルの主キーであり、Widget の PK の一部として User への FK を持つことで、Widget がユーザー間で共有されることはありません。ユーザーが削除されると、すべてのウィジェットが一緒に削除されます。WidgetId だけでウィジェットをクエリしても意味がありません。特定のウィジェットを照会するには、UserId パラメーターと WidgetId パラメーターの両方が必要です。

Evans DDD で、集約内の非ルート エンティティ ID は集約内で一意である必要があるだけであると読んだことを思い出したので、これを調査しています。上記は、この場合の例です。

ルート以外の ENTITIES にはローカル ID がありますが、その ID は AGGREGATE 内でのみ識別可能である必要があります。これは、外部オブジェクトがルート ENTITY のコンテキストからそれを見ることができないためです。(ドメイン駆動設計、エヴァンス)

更新 2

Gorgan の回答の後、ウィジェット ファクトリ クラスに次のようなものを実装しました。これは、上記の望ましい結果を達成するための正しいアプローチですか?

public class WidgetFactory : BaseFactory
{
    private object _sync = new object();

    internal WidgetFactory(IWriteEntities entityWriter) : base(entityWriter)
    { 
        // my DbContext class implements the IWriteEntities interface
    }

    public Widget CreateOrUpdate(User user, int? widgetId, string prop1, 
        bool prop2, string prop3)
    {
        Widget widget = null;
        if (!widgetId.HasValue)
        {
            // when widgetId is null do a create (construct & hydrate), then
            EntityWriter.Insert(widget); // does DbEntityEntry.State = Added
        }
        else
        {
            // otherwise query the widget from EntityWriter to update it, then
            EntityWriter.Update(widget); // does DbEntityEntry.State = Modified
        }

        // determine the appropriate WidgetId & save
        lock (_sync)
        {
            widget.WidgetId = widgetId.HasValue ? widget.WidgetId
                : EntityWriter.Widgets.Where(w => w.UserId == user.Id)
                    .Max(w => w.WidgetId) + 1;
            EntityWriter.SaveChanges(); // does DbContext.SaveChanges()
        }

        return widget;
    }
}

ウィジェット

この用語を偽装すべきではなかったと思います。実際のエンティティは WorkPlace です。毎年、市税還付申請書と共に旅程表を提出しなければならないので、その年のどこで働いているかを追跡するアプリを作成しています。完了したら、クラウドに公開して、他の人が無料で使用できるようにする予定です。しかしもちろん、私は彼らの WorkPlace を私のものから完全に分離したいと考えています。

ユーザーは、匿名の識別を使用してすべての訪問者に対して自動的に作成されます / Request.AnonymousID(後で登録機能がありますが、デモを試す必要はありません)。Web アプリには、/work-places/1、/work-places/2 などの安静な URL があります。すべての要求にはユーザーが含まれていることが保証されているため、ユーザー ID を URL で識別する必要はありません。Request.AnonymousIDに何もないときからユーザーを識別できますUser.Identity.Name

WorkPlaceId が ID 列である場合、コントローラー アクションは、表示する前にユーザーが職場を所有していることを確認する必要があります。そうしないと、URL をハッキングして、すべてのユーザーがシステムに設定したすべての WorkPlace を確認できます。WorkPlaceId をユーザーに対してのみ一意にすることで、心配する必要はありません。URL /work-places/1 は、2 人の異なるユーザーにまったく異なるデータを表示します。

4

2 に答える 2

1

ID 列の目的は、他の列の値に関係なく一意の列が必要な場合にテーブルに ID を持たせることであり、SQL Server はテーブル内の 1 つの ID 列のみの自動生成をサポートします (複数または複雑なプライマリ自動生成はありません)。キー)。

すべてのウィジェットが異なる場合 (同じウィジェットがより多くのユーザーによって使用されている場合でも)、この主キーは理にかなっていますが、SQL サーバーで計算することはできません (挿入にストアド プロシージャまたは他の db プログラミング機能を使用する場合を除く)。また、自動生成された列は ef でのみ読み取ることができます (「Entity Framework Code First で計算列にアクセスするにはどうすればよいですか?」 を参照してください)。

ただし、より多くのウィジェット タイプがある場合 (この例では、ウィジェット タイプ 1、2、および 3編集: WidgetId は、Id が自動生成されるテーブル WidgetType の FK になる可能性があるため、PK は 2 つの自動生成 ID から構成されます)、作成できます。 WidgetType (または WidgetInstance、WidgetSomething など、ユーザーの影響を受けないウィジェットの一部を記述するエンティティ) テーブルに自動生成された ID があり、このテーブルでそれを FK および主キーの一部として使用します。これにより、UserId と WidgetId の両方を自動生成し、このテーブルへの挿入時に存在させることができます。

于 2012-03-15T22:20:34.863 に答える
0

単純化しすぎて申し訳ありません。この質問が出てくるときはいつでも、データベース設計の問題のような匂いがします。

ウィジェットとユーザーは、どちらもマスター データと見なされます。ウィジェット テーブルにはウィジェットに関する属性のみを含める必要があり、ユーザー テーブルにはユーザーに関する属性のみを含める必要があります。ウィジェットがユーザーに関連している場合、おそらくウィジェット テーブルにユーザー ID を含めても問題ありませんが、ウィジェット ID は ID であり、ユーザー ID は FK である必要があります。

ウィジェットとユーザーのリンクは、これらのマスター テーブルの外で行う必要があります。それらが請求書/請求書に関連している場合は、請求書ヘッダー テーブル (invoiceid、userid、datetime、総コスト、総価格など) と請求書明細テーブル (invoicelineid、invoiceid、widgetid、unit) の組み合わせを使用するのが一般的です。コスト、単価、数量...)

データ量が増えるにつれて、少し正規化することでアプリのスケーリングが保証されます。さらに、これらの強力な関係は、このデータをビジネス インテリジェンスに変換するときのデータ品質に役立ちます。

于 2012-03-16T03:27:53.043 に答える