我研究了这个问题几天,似乎无法找到我感觉良好的选项;然而,这里是一个链接到一个非常类似的问题:EF 4代码优先 - 组合视图和表
最终,我也有同样的问题,但我希望有一个更好的解决方案。
考虑下面的数据库表:
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
)
我现在想使用视图用于装载“共同”查找/计算的信息。每次我们在网站上显示帖子时,我们都想知道创建帖子的人的姓名以及上次修改人的姓名。这些是存储在帖子表的不同表格中的两个字段。我可以很容易地使用下面的语法(假设应用了Lazy/eager加载,并且CreatedBy是基于CreatedByID的Contact类型的属性):currentPost.CreatedBy.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; }
}
的我喜欢这种方法的原因是,它允许我根据表或视图从数据库检索信息,如果我加载视图,则视图包含所有“表”信息w这将允许我从视图加载,但保存到表格。国际海事组织,这给了我两全其美。
我过去曾经使用过这种方法,但通常我只是使用来自存储过程的数据行或信息从DB加载信息,甚至在从数据库加载后使用亚音速活动记录模式和映射字段。我真的希望我可以在EF中做一些事情,让我可以加载这些对象而无需创建另一个抽象层。
这是我曾尝试使用用于配置(使用流利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错误(error sample 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位出场,但没有运气。我仍然有类似的错误。
有没有人有关于如何“扩展”基本/表对象和从视图加载信息的建议?再次,我相信这样做会带来巨大的性能提升。我在这个问题开始时引用的文章有两个潜在的解决方案,但1需要太多的数据库命中(只是为了获得一些常见的查找信息),另一个需要额外的抽象层(我真的想直接去我的POCO来自DB,没有写任何映射)。
最后,谢谢给所有回答这些类型问题的人。我赞扬多年来为回应做出贡献的每一个人。我想我们很多开发人员都会将这些信息视为理所当然!
感谢您的详细解释。我最终做了类似于你的建议。我使用3个独立的对象(1个用于查看,1个用于表格,1个包含属性的“域”模型)。在加载时,我从视图加载,映射到我的域模型。在保存时,我从我的域模型映射到表模型。这几乎是一种有效的记录模式,但它将我所有的映射放在代码中,这使得它更易于调试。 – 2012-10-09 15:52:12