我研究了这个问题好几天,似乎找不到一个让我感觉良好的选项;但是,这是一个非常相似的问题的链接:
最终,我有同样的问题,但我希望有更好的解决方案。
考虑以下数据库表:
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 表不同的表中的字段。我可以轻松地使用以下语法(假设应用了延迟/急切加载,并且 CreatedBy 是基于 CreatedByID 的 Contact 类型的属性): currentPost.CreatedBy.Name;
这种方法的问题在于 Db 调用的数量以及为联系人检索到的大量记录,但在这种情况下我们只使用 Name 99%。我意识到上面的数据库模式很小,但这只是一个简化的例子,真正的联系人表有大约 50 个字段。
为了管理过去的这种情况(在使用 EF 之前),我通常会为我将使用的表构建“详细”视图。“详细”视图包含常见的查找/计算字段,因此只需 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,这给了我两全其美。
我过去使用过这种方法,但通常,我只是使用数据行或存储过程中的信息从数据库加载信息,甚至在从数据库加载后使用亚音速活动记录模式和映射字段。我真的希望我可以在 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 错误(错误示例here)。然后我试图让视图只包含表的主键和“扩展”属性(例如 [Entry] 不在 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'.
所以我玩了一点MapInheritedProperties,但没有运气。我继续收到类似的错误。
有人对如何“扩展”基/表对象并从视图加载信息有任何建议吗?同样,我相信这样做会带来很大的性能提升。我在这个问题开头引用的文章有 2 个潜在的解决方案,但是 1 个需要太多的 DB 命中(只是为了获得一些常见的查找信息),另一个需要额外的抽象层(我真的很想直接去我的 POCO 来自数据库,没有写任何映射)。
最后,感谢所有回答此类问题的人。我为多年来为响应做出贡献的每个人鼓掌。我认为我们太多的开发人员认为这些信息是理所当然的!!