私はこの質問を何日も調査しましたが、私が満足している選択肢を見つけることができないようです。ただし、非常によく似た質問へのリンクは次のとおりです。
最終的には同じ質問がありますが、より良い解決策を望んでいます。
次のDBテーブルを検討してください。
CREATE TABLE [Contact](
[ContactID] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
[ContactName] [varchar](80) NOT NULL,
[Email] [varchar](80) NOT NULL,
[Title] [varchar](120) NOT NULL,
[Address1] [varchar](80) NOT NULL,
[Address2] [varchar](80) NOT NULL,
[City] [varchar](80) NOT NULL,
[State_Province] [varchar](50) NOT NULL,
[ZIP_PostalCode] [varchar](30) NOT NULL,
[Country] [varchar](50) NOT NULL,
[OfficePhone] [varchar](30) NOT NULL,
[MobilePhone] [varchar](30) NOT NULL)
CREATE TABLE [Blog](
[BlogID] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
[BlogName] [varchar](80) NOT NULL,
[CreatedByID] [int] NOT NULL, -- FK to ContactTable
[ModifiedByID] [int] NOT NULL -- FK to ContactTable
)
CREATE TABLE [Post](
[PostID] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
[BlogID] [int] NOT NULL, -- FK to BlogTable
[Entry] [varchar](8000) NOT NULL,
[CreatedByID] [int] NOT NULL, -- FK to ContactTable
[ModifiedByID] [int] NOT NULL -- FK to ContactTable
)
ここで、「一般的な」ルックアップ/計算された情報をロードするためにビューを使用したいと思います。サイトに投稿を表示するたびに、投稿を作成した人と最後に変更した人の名前を知りたいと思います。これらは、postテーブルとは別のテーブルに格納される2つのフィールドです。次の構文を簡単に使用できます(遅延/熱心な読み込みが適用され、CreatedByがCreatedByIDに基づくContactタイプのプロパティであると仮定):currentPost.CreatedBy.Name;
このアプローチの問題は、Db呼び出しの数と、連絡のために取得された大きなレコードですが、この状況ではName 99%しか使用していません。上記のDBスキーマは小さいと思いますが、これは単純化された例であり、実際の連絡先テーブルには約50のフィールドがあります。
過去(EFを使用する前)にこのタイプの状況を管理するために、私は通常、使用するテーブルの「詳細」ビューを作成しました。「詳細」ビューには一般的なルックアップ/計算フィールドが含まれているため、DBを1回呼び出すだけで、必要なすべての情報を効率的に取得できます(注:SQLビューのインデックスを使用して、これを非常に効率的に読み取ることができます)。私が一般的に使用するビューのリスト(関連するテーブルからの「ルックアップ」フィールドが含まれるため):
ALTER VIEW [icoprod].[BlogDetail]
AS
SELECT B.[BlogID],
B.[BlogName],
B.[BlogDescription],
B.[CreatedByID],
B.[ModifiedByID],
CREATEDBY.[ContactName] AS CreatedByName,
MODIFIEDBY.[ContactName] AS ModifiedByName,
(SELECT COUNT(*) FROM Post P WHERE P.BlogID = B.BlogID) AS PostCount
FROM Blog AS B
JOIN Contact AS CREATEDBY ON B.CreatedByID = CREATEDBY.ContactID
JOIN Contact AS MODIFIEDBY ON B.ModifiedByID = MODIFIEDBY.ContactID
ALTER VIEW [icoprod].[PostDetail]
AS
SELECT P.[PostID],
P.[BlogID],
P.[Entry],
P.[CreatedByID],
P.[ModifiedByID],
CREATEDBY.[ContactName] AS CreatedByName,
MODIFIEDBY.[ContactName] AS ModifiedByName,
B.Name AS BlogName
FROM Post AS P
JOIN Contact AS CREATEDBY ON P.CreatedByID = CREATEDBY.ContactID
JOIN Contact AS MODIFIEDBY ON P.ModifiedByID = MODIFIEDBY.ContactID
JOIN Blog AS B ON B.BlogID = P.BlogID
これが私の「POCO」オブジェクトの概要です。
public class Blog
{
public int ID { get; set; }
public string Name { get; set; }
public int CreatedByID { get; set; }
public DateTime ModifiedByID { get; set; }
}
public class Post
{
public int ID { get; set; }
public string Name { get; set; }
public int CreatedByID { get; set; }
public DateTime ModifiedByID { get; set; }
}
public class Contact
{
public int ID { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Title { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string MobilePhone { get; set; }
}
public class BlogDetails : Blog
{
public string CreatedByName { get; set; }
public string ModifiedByName { get; set; }
public int PostsCount { get; set; }
}
public class PostDetails : Post
{
public string CreatedByName { get; set; }
public string ModifiedByName { get; set; }
public string BlogName { get; set; }
}
このアプローチが好きな理由は、テーブルまたはビューに基づいてデータベースから情報を取得できることと、ビューをロードすると、ビューにすべての「テーブル」情報が含まれているため、ビューからロードして保存することができるためです。テーブル。IMO、これは私に両方の長所を与えてくれます。
私は過去にこのアプローチを使用しましたが、通常は、データ行またはストアドプロシージャからの情報を使用して、DBから情報をロードするか、DBからロードした後に亜音速のactiverecordパターンとマップされたフィールドを使用しました。EFで何かを実行して、別の抽象化レイヤーを作成せずにこれらのオブジェクトをロードできるようになることを本当に望んでいます。
これが私が構成に使用しようとしたものです(Fluent APIとコードファーストEFを使用):
public class PostConfiguration : EntityTypeConfiguration<Post>
{
public PostConfiguration()
: base()
{
HasKey(obj => obj.ID);
Property(obj => obj.ID).
HasColumnName("PostID").
HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).
IsRequired();
Map(m =>
{
m.ToTable("Post");
});
}
}
public class BlogConfiguration : EntityTypeConfiguration<Blog>
{
public BlogConfiguration()
: base()
{
HasKey(obj => obj.ID);
Property(obj => obj.ID).
HasColumnName("BlogID").
HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).
IsRequired();
Map(m =>
{
m.ToTable("Blog");
});
}
}
public class ContactConfiguration : EntityTypeConfiguration<Contact>
{
public ContactConfiguration()
: base()
{
HasKey(obj => obj.ID);
Property(obj => obj.ID).
HasColumnName("ContactID").
HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).
IsRequired();
Map(m =>
{
m.ToTable("Contact");
});
}
}
public class PostDetailsConfiguration : EntityTypeConfiguration<PostDetails>
{
public PostDetailsConfiguration()
: base()
{
Map(m =>
{
m.MapInheritedProperties();
m.ToTable("icoprod.PostDetails");
});
}
}
public class BlogDetailsConfiguration : EntityTypeConfiguration<BlogDetails>
{
public BlogDetailsConfiguration()
: base()
{
Map(m =>
{
m.MapInheritedProperties();
m.ToTable("icoprod.BlogDetails");
});
}
}
この時点で、「拡張」情報を含むテーブルのすべての情報を含むビューを使用しようとしましたが、これを実行すると、恐ろしい3032エラーが発生します(エラーサンプルはこちら)。次に、ビューにテーブルの主キーと「拡張」プロパティのみを含めるようにしました(たとえば、[エントリ]はPostDetailsビューにありません)。これを試してみると、次のエラーが発生します。
All objects in the EntitySet 'DBContext.Post' must have unique primary keys. However, an instance of type 'PostDetails' and an instance of type 'Post' both have the same primary key value, 'EntitySet=Post;ID=1'.
だから私はMapAliExpressPropertiesを少し省略して遊んだことがありますが、運がありません。同様のエラーが引き続き発生します。
ベース/テーブルオブジェクトを「拡張」してビューから情報をロードする方法について誰かが提案を持っていますか?繰り返しになりますが、これを行うことでパフォーマンスが大幅に向上すると思います。この質問の冒頭で参照した記事には2つの解決策がありますが、1つは(一般的なルックアップ情報を取得するためだけに)DBヒットが多すぎる必要があり、もう1つは抽象化の追加レイヤーが必要です(そして私は本当に直接行きたいですマッピングを記述せずに、DBからのPOCO)。
最後に、このような質問に答えてくださった皆様、ありがとうございました。長年にわたって対応に貢献してくださった皆様に拍手を送ります。私たち開発者の多くは、この情報を当然のことと思っていると思います!!