EF 4 Code First - Ansichten und Tabellen kombinieren

Ich habe diese Frage tagelang recherchiert und kann anscheinend keine Option finden, bei der ich mich wohl fühle. Hier ist jedoch ein Link zu einer sehr ähnlichen Frage:

Berechnetes Feld zum Modell hinzufügen

Letztendlich habe ich die gleiche Frage, aber ich hoffe auf eine bessere Lösung.

Betrachten Sie die folgenden DB-Tabellen:

<code>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
)
</code>

Ich möchte jetzt Ansichten zum Laden von "allgemeinen" Nachschlägen / berechneten Informationen verwenden. Jedes Mal, wenn wir einen Beitrag auf der Website anzeigen, möchten wir den Namen der Person wissen, die den Beitrag erstellt und zuletzt geändert hat. Dies sind zwei Felder, die in separaten Tabellen von der Post-Tabelle gespeichert werden. Ich könnte leicht die folgende Syntax verwenden (vorausgesetzt, Lazy / eager loading wurde angewendet und CreatedBy war eine Eigenschaft vom Typ Contact, basierend auf CreatedByID): currentPost.CreatedBy.Name;

Das Problem bei diesem Ansatz ist die Anzahl der Datenbankanrufe und der große Datensatz, der für den Kontakt abgerufen wurde. In dieser Situation wird jedoch nur Name 99% verwendet. Mir ist klar, dass das obige DB-Schema winzig ist, aber dies ist nur ein vereinfachtes Beispiel, und die reale Kontakttabelle enthält ungefähr 50 Felder.

Um diese Art von Situation in der Vergangenheit zu verwalten (vor der Verwendung von EF), habe ich in der Regel Detailansichten für die Tabellen erstellt, die ich verwenden werde. Die "Detail" -Ansichten enthalten allgemeine Lookup- / berechnete Felder, sodass nur ein Aufruf der Datenbank erforderlich ist, um alle benötigten Informationen effizient abzurufen Eine Liste der häufig verwendeten Ansichten (da sie Nachschlagefelder aus verwandten Tabellen enthalten):

<code>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
</code>

Hier eine Übersicht meiner "POCO" -Objekte:

<code>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; }
}
</code>

Der Grund, warum ich diesen Ansatz mag, ist, dass ich Informationen aus der Datenbank basierend auf Tabellen oder Ansichten abrufen kann UND wenn ich eine Ansicht lade, enthält die Ansicht alle "Tabellen" -Informationen, die es mir ermöglichen würden, aus einer Ansicht zu laden, aber in zu speichern ein Tisch. IMO, das gibt mir das Beste aus beiden Welten.

Ich habe diesen Ansatz in der Vergangenheit verwendet, aber normalerweise habe ich nur Informationen aus der Datenbank geladen, indem ich Datenzeilen oder Informationen aus gespeicherten Prozessen verwendet habe, oder sogar Subsonic Activerecord-Muster und zugeordnete Felder nach dem Laden aus der Datenbank verwendet habe. Ich hoffe wirklich, dass ich etwas in EF tun kann, mit dem ich diese Objekte laden kann, ohne eine weitere Abstraktionsebene zu erstellen.

Folgendes habe ich für die Konfiguration versucht (mit Fluent API und Code-First EF):

<code>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");
            });

    }

}
</code>

An diesem Punkt habe ich versucht, eine Ansicht zu verwenden, die alle Informationen aus der Tabelle mit "erweiterten" Informationen enthält, und wenn ich dies versuche, erhalte ich den gefürchteten 3032-Fehler (Fehlerbeispiel hier). Dann habe ich versucht, dass die Ansicht NUR den Primärschlüssel der Tabelle und die "erweiterten" Eigenschaften enthält (z. B. [Eintrag] ist nicht in der Ansicht "PostDetails"). Wenn ich das versuche, erhalte ich die folgende Fehlermeldung:

<code>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'.
</code>

Also habe ich ein bisschen mit dem Auslassen von MapInheritedProperties gespielt, aber ohne Glück. Ich erhalte weiterhin einen ähnlichen Fehler.

Hat jemand einen Vorschlag, wie man ein Basis- / Tabellenobjekt "erweitert" und Informationen aus einer Ansicht lädt? Ich glaube wieder, dass dies einen großen Leistungsgewinn bedeutet. Der Artikel, auf den ich am Anfang dieser Frage verwiesen habe, enthält 2 mögliche Lösungen, aber einer erfordert zu viele DB-Treffer (nur um einige allgemeine Suchinformationen zu erhalten) und der andere erfordert eine zusätzliche Abstraktionsebene (und ich würde wirklich gerne direkt zu gehen) meine POCOs aus der DB, ohne ein Mapping zu schreiben).

Zuletzt,Danke dir an alle, die diese Art von Fragen beantworten. Ich begrüße alle, die im Laufe der Jahre zu Antworten beigetragen haben. Ich denke, zu viele von uns Entwicklern halten diese Informationen für selbstverständlich !!

Antworten auf die Frage(1)

Ihre Antwort auf die Frage