EF 4 Code First - Połącz widoki i tabele

Przez wiele dni badałem to pytanie i nie mogę znaleźć opcji, z którą dobrze się czuję; jednak tutaj jest link do bardzo podobnego pytania:

Dodaj pole Obliczone do modelu

Ostatecznie mam to samo pytanie, ale mam nadzieję na lepsze rozwiązanie.

Rozważ następujące tabele DB:

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

Chciałbym teraz użyć widoków do załadowania „wspólnego” wyszukiwania / obliczonych informacji. Za każdym razem, gdy wyświetlamy post na stronie, chcemy znać nazwisko osoby, która utworzyła posta i która go ostatnio zmodyfikowała. Są to dwa pola, które są przechowywane w oddzielnych tabelach z tabeli postów. Mogłem z łatwością użyć następującej składni (zakładając, że zastosowano ładowanie Lazy / eager, a CreatedBy był właściwością typu Contact, opartą na CreatedByID): currentPost.CreatedBy.Name;

Problem z tym podejściem to liczba wywołań Db, a także duży rekord pobrany do kontaktu, ale w tej sytuacji używamy tylko Nazwa 99%. Zdaję sobie sprawę, że powyższy schemat DB jest niewielki, ale jest to tylko uproszczony przykład, a rzeczywista tabela kontaktów ma około 50 pól.

Aby zarządzać tego typu sytuacjami w przeszłości (przed użyciem EF), zazwyczaj budowałem widoki „szczegółów” dla tabel, których będę używać. Widoki „szczegółów” zawierają wspólne pola wyszukiwania / obliczeniowe, dzięki czemu wystarczy tylko 1 wywołanie do bazy danych, aby skutecznie uzyskać wszystkie potrzebne informacje (UWAGA: Używamy również indeksowania w naszych widokach SQL, aby uczynić to niezwykle wydajnym do czytania). lista widoków, które będę powszechnie używać (ponieważ będą one zawierały pola „przeglądaj” z powiązanych tabel):

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

Oto przegląd moich obiektów „POCO”:

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

Powodem, dla którego podoba mi się to podejście, jest to, że pozwala mi pobierać informacje z bazy danych na podstawie tabel lub widoków, a jeśli załaduję widok, widok zawiera wszystkie informacje „tabeli”, które pozwolą mi załadować z widoku, ale zapisać do stół. IMO, daje mi to najlepsze z obu światów.

Użyłem tego podejścia w przeszłości, ale zwykle ładowałem informacje z bazy danych przy użyciu datarows lub informacji z zapisanych proc, albo nawet używali poddźwiękowego wzorca aktywnego rekordu i mapowanych pól po załadowaniu z bazy danych. Naprawdę mam nadzieję, że mogę zrobić coś w EF, co pozwoli mi załadować te obiekty bez tworzenia kolejnej warstwy abstrakcji.

Oto, co próbowałem użyć do konfiguracji (przy użyciu Fluent API i kodu 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>

W tym momencie próbowałem użyć widoku zawierającego wszystkie informacje z tabeli z „rozszerzonymi” informacjami, a kiedy próbuję to uzyskać, otrzymałem straszny błąd 3032 (próbka błędu tutaj). Następnie starałem się, aby widok zawierał TYLKO klucz podstawowy tabeli i właściwości „rozszerzone” (np. [Wpis] nie jest w widoku Szczegóły). Gdy to wypróbuję, pojawia się następujący błąd:

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

Grałem więc z pominięciem trochę MapInheritedProperties, ale bez powodzenia. Nadal otrzymuję podobny błąd.

Czy ktoś ma sugestię, jak „rozszerzyć” obiekt bazy / tabeli i załadować informacje z widoku? Ponownie uważam, że dzięki temu można uzyskać duży wzrost wydajności. Artykuł, do którego odwoływałem się na początku tego pytania, ma 2 potencjalne rozwiązania, ale 1 wymaga zbyt wielu hitów DB (tylko po to, by uzyskać typowe informacje na temat wyszukiwania), a drugi wymaga dodatkowej warstwy abstrakcji (i naprawdę chciałbym przejść bezpośrednio do moje POCO z DB, bez pisania żadnego mapowania).

W końcu,Dziękuję Ci wszystkim, którzy odpowiadają na tego typu pytania. Pochwalam wszystkich, którzy przyczynili się do odpowiedzi na przestrzeni lat. Myślę, że zbyt wielu z nas deweloperów traktuje te informacje jako oczywiste !!

questionAnswers(1)

yourAnswerToTheQuestion