Entity framework EF 4代码优先-组合视图和表

Entity framework EF 4代码优先-组合视图和表,entity-framework,c#-4.0,views,ef-code-first,Entity Framework,C# 4.0,Views,Ef Code First,我对这个问题研究了好几天,似乎找不到一个让我感到满意的选择;然而,这里有一个链接指向一个非常类似的问题: 最终,我有同样的问题,但我希望有更好的解决办法 考虑以下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]

我对这个问题研究了好几天,似乎找不到一个让我感到满意的选择;然而,这里有一个链接指向一个非常类似的问题:

最终,我有同样的问题,但我希望有更好的解决办法

考虑以下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表不同的表中。我可以很容易地使用以下语法(假设应用了惰性/急切加载,并且CreatedBy是一个属性,类型为Contact,基于CreatedByID):currentPost.CreatedBy.Name

这种方法的问题是Db调用的数量以及为contact检索的大记录,但在这种情况下,我们只使用了99%的Name。我意识到上面的DB模式很小,但这只是一个简化的示例,实际的contact表大约有50个字段

为了管理过去的这种情况(在使用EF之前),我通常会为我将要使用的表构建“细节”视图。“详细信息”视图包含常用的查找/计算字段,因此只需对数据库进行一次调用,即可有效地获取我所需的所有信息(注意:我们还在SQL视图上使用索引,以提高阅读效率)。以下是我常用的视图列表(因为它们将包含相关表中的“查找”字段):

下面是我的“POCO”对象的概述:

我喜欢这种方法的原因是,它允许我基于表或视图从数据库检索信息,如果我加载一个视图,该视图包含所有“表”信息,这些信息允许我从视图加载,但保存到表中。在我看来,这给了我两全其美的感觉

我在过去使用过这种方法,但通常情况下,我只是使用datarows或存储过程中的信息从DB加载信息,甚至在从DB加载后使用亚音速activerecord模式和映射字段。我真的希望我能在EF中做点什么,让我加载这些对象而不需要创建另一层抽象

以下是我尝试用于配置的内容(使用Fluent API和code first EF):

所以我一直在玩着略去MapInheritedProperties的游戏,但是运气不好。我继续得到一个类似的错误

有人对如何“扩展”基/表对象和从视图加载信息有什么建议吗?同样,我相信这样做会带来很大的性能提升。我在这个问题的开头引用的文章有两种可能的解决方案,但第一种需要太多的数据库点击(只是为了获得一些常见的查找信息),而另一种需要额外的抽象层(我真的希望从数据库直接转到我的POCO,而不编写任何映射)


最后,感谢所有回答这类问题的人。我赞扬多年来为回应作出贡献的每一个人。我想我们中有太多的开发者认为这些信息是理所当然的

从视图加载记录并将其保存到表不会与代码映射一起工作-博客实体将始终从表加载并保存到表,而博客详细信息实体将始终从视图加载并保存到视图-因此您必须具有可更新的视图或而不是触发器来支持此场景。若使用EDMX,还可以映射为insert、update和delete执行的自定义SQL/存储过程,以强制保存到表中,但此功能在代码映射中不可用。无论如何,这不是你最大的问题

您可以使用视图,也可以像以前一样将其映射到类,但不能映射继承。原因在于继承的工作方式。继承说实体是父实体或子实体(可以充当父实体)。永远不可能存在既可以是父级(我的意思是只有父级)也可以是子级的数据库记录。在.NET中甚至是不可能的,因为要支持此场景,您需要两个实例—一个是父类型的实例,另一个是子类型的实例。这两个实例不相等,因为纯父对象不能强制转换为子对象(它不是子对象)。最大的问题来了。映射继承后,键在整个继承层次结构中必须是唯一的。因此,不能有两个实例(一个用于父实例,一个用于子实例)具有相同的密钥

作为一种解决方法,不要从映射的实体(
Blog
)派生
blogdail
)。使用第三个未映射类作为两个或接口的父类。也不要使用
MapInheritedProperties
使您的
blogdeail
Blog
完全无关

另一个解决方法是根本不映射blogdeail。在这种情况下,您可以按原样使用代码,而不是使用视图创建简单的带投影的可重用查询:

var blogDetails = from b in context.Blogs
                  where ... 
                  select new BlogDetail
                      {
                          Name = b.Name,
                          CreatedByID = b.CreatedByID,
                          ...
                          CreatedByName = b.CreatedBy.Name // You need navigation property
                          ...   
                      }; 

在这两种情况下,如果您需要保存
Blog
,则必须创建新实例并从
blogdail
填充它。将其附加到上下文后,将其设置为修改状态并保存更改。

感谢您的详细解释。我最终还是照你的建议做了。我使用了3个独立的对象(1个用于视图,1个用于表,1个用于包含属性的“域”模型)。加载时,我从视图加载,映射到我的域模型。保存时,我从域模型映射到表模型。这几乎是一种活动记录模式,但它将我的所有映射都放在代码中,从而更容易调试。
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; }
}
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");
            });

    }

}
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'.
var blogDetails = from b in context.Blogs
                  where ... 
                  select new BlogDetail
                      {
                          Name = b.Name,
                          CreatedByID = b.CreatedByID,
                          ...
                          CreatedByName = b.CreatedBy.Name // You need navigation property
                          ...   
                      };