6

Szczęśliwego nowego roku dla wszystkich! Właśnie rozpocząłem modelowanie danych aplikacji ASP.NET MVC 5 dla klienta, który prowadzi działalność związaną z wynajmem narzędzi. Częścią rozwiązania jest utworzenie funkcji administratora (zaplecza), za pomocą której użytkownicy administracyjni mogą tworzyć/edytować niestandardowe atrybuty lub metadane narzędzi, które są dołączone do każdego narzędzia z określonej grupy narzędzi. Pracuję nad stwierdzeniem, że w czasie wykonywania aplikacja nie powinna wiedzieć, jaki będzie schemat metadanych. Zacząłem od tego: enter image description here Tak, wiem ... kolejny koszmar EAV! Wiem, że jeśli dane są poprawnie normalizowane i tworzone są odpowiednie indeksy, to nie powinno być tak źle. Ale szczerze mówiąc, nie widzę innego wyboru. Tak więc na przykład: zbożoweEntityFramework 6 - Obsługa zdefiniowanych przez użytkownika atrybutów

akumulatorowe Bosch

  • grupy narzędzi: Wiertła
  • Marka: Bosch (listitem - oznakowane od MetaAttributeListOption tabeli)
  • Typ: Cordless (listitem - oznakowane od MetaAttributeListOption tabeli)
  • Uchwyt bezkluczowy: Tak (boolean)
  • Napięcie: 14,4 V (tekst)
  • ...

Teraz Atrybuty te będą służyć celom: 3

  1. Wyświetl na Frontend jako "Specyfikacje"
  2. użyte do filtrowania Narzędzia na Frontend
  3. (potencjalnie) Używane w raportowaniu określ "Popularne marki" (na przykład)

Zgaduję więc, że utknąłem z RDBMS (SQL Server) do tego. Wiem, że popularnym podejściem do tego byłoby użycie jakiegoś rozwiązania NoSQL, ale szczerze mówiąc, nie mam zbyt dużego doświadczenia, aby używać go w połączeniu z MSSQL. Mogłabym połączyć tabele Values w jedną tabelę, gdzie każda wartość typu danych jest we własnej kolumnie, ale to pozostawi mnie z mnóstwem null do walki.

Więc ja zostaję z następującymi pytaniami gdyby był Pan uprzejmy mi pomóc z:

  1. Czy tego modelu pracy pod względem mojego wymogu? Nie jestem pewien, czy poprawnie zaprojektowałem relację tabeli MetaAttributeListOption.
  2. Czy istnieje alternatywa dla tej metody EAV?
  3. Zakładając, że mój model powyżej (lub jego pochodnej) jest moją jedyną opcją, w jaki sposób mam to wdrożyć z Entity Framework 6? W przypadku stron ASP View w zapleczu administracyjnym, wyobrażam sobie, że potrzebuję jakiegoś HTML Helper, aby określić poprawnego edytora do renderowania, a następnie zapełnić go odpowiednio.

Byłbym wdzięczny za pomoc społeczności StackOverflow w tej sprawie. Daj mi znać, jeśli potrzebujesz więcej informacji i nie zamykaj tego, jeśli uważasz to za nie na temat, ponieważ uważam, że moje pytania dotyczą programowania. Dziękuję Ci!


EDIT: Zaczynam bounty na tej wartości 200 moich własnych punktów ... 100 za pomoc/doradza mi na moje pytania 1 & 2 i 100 punktów na pytanie 3. podziękowaniem Ci

Odpowiedz

2
  1. modelu pytanie wygląda opłacalne, a relacje skonfigurowane prawidłowo, z tym wyjątkiem, że nadmiarowe OptionLabels mógłby zostać utworzony, jeśli istnieje wiele duplikatów. Są jednak pewne zmiany i dezodoryzujące kompromisy, które podjąłem. (Patrz # 3)

  2. Z Twojego filtrowania i wymogów raportowania i względnego komfortu z MSSQL myślę wykorzystaniem RDBMS jest najlepszym

  3. Widziałem podejście przedstawione poniżej stosowane w kilku innych twórców "API i wydaje się być wystarczająco dobry kompromis, który jest mniej znormalizowane, ale sprawia, że ​​model danych prostsze i wysyłania zapytań o wartości znacznie bardziej elastyczne

    • Dodałem MetaAttributeList aby umożliwić jednej listy, aby zastosować do wielu MetaAttributes . W tym modelu Booleans będzie reprezentowany jako Tak/Nie ListOption.
    • Model pytania wymagałby sprawdzenia wartości pod kątem zbadania (jednej z) 3 tabel, a odpowiedni MetaAttribute zawsze będzie znany z wyprzedzeniem
    • Model zapytania, domyślnie z kodem EF First, będzie miał problem z wieloma Ścieżki CASCADE, które wymagałyby użycia FluentApi (nie jest to wielka sprawa, ale może być niewygodne do śledzenia).
    • Podejście to (opcjonalnie?) Wymagałoby, aby egzekwowanie poprawnych wpisów ListOption było obsługiwane w kodzie zamiast w bazie danych
    • Wyświetlanie różnych typów wartości nie wymagałoby żadnej dodatkowej pracy do poprawnego renderowania
    • Admin Interfejs musiałby sprawdzić za MetaAttribute.ListOption w celu ustalenia, czy wyświetlać TextBox lub listitem (i ewentualnie wyboru jeśli ListItemOptions są Tak/Nie)

    • Możesz dodać kolejną tabelę ToolGroups że zawęża z MetaAttributes prezentowane użytkownikowi

Uwaga: Ponieważ metoda EF i język nie zostały określone, użyłem EF kod najpierw i VB.NET. Migracje IMO i łatwiejsze przejście na EF7 to wystarczający powód, aby użyć kodu First. Nieco lepiej podoba mi się czytelność VB.Net, ale w razie potrzeby chętnie zmienię C# (lub użyję konwertera this).

Imports System.ComponentModel.DataAnnotations 
Namespace Models 
    'I didn't bother specifying string lengths with <StringLength(#)> 
    Public Class HireTool 
     Public Property Id As Integer 
     '... other properties 

     'Navigation Properties 
     Public Overridable Property HireToolMetaAttributes As ICollection(Of HireToolMetaAttribute) 
    End Class 
    Public Class MetaAttribute 
     Public Enum MetaAttributeTypeEnum 
      Text = 1 
      ListItem = 2 
     End Enum 
     Public Property Id As Integer 
     Public Property Code As String 
     Public Property Label As String 
     Public Property Type As MetaAttributeTypeEnum 
     Public Property Required As Boolean 
     Public Property Position As Integer 
     'Navigation Properties 
     Public Overridable Property List As MetaAttributeList 
    End Class 
    Public Class MetaAttributeList 
     Public Property ID As Integer 
     Public Property Name As String 
     'Navigation Properties 
     <Required> 
     Public Property ListOptions As ICollection(Of MetaAttributeListOption) 
    End Class 
    Public Class MetaAttributeListOption 
     Public Property Id As Integer 
     Public Property OptionLabel As String 
    End Class 
    Public Class HireToolMetaAttribute 
     Public Property Id As Integer 
     <Schema.Index> <StringLength(1000)> 
     Public Property Value As String 
     <Required> 
     Public Overridable Property HireTool As HireTool 
     <Required> 
     Public Overridable Property MetaAttribute As MetaAttribute 
    End Class 
End Namespace   

Edycja: Oto generowane SQL:

CREATE TABLE [dbo].[MetaAttributeLists] (
    [ID] INT   IDENTITY (1, 1) NOT NULL, 
    [Name] NVARCHAR (MAX) NULL, 
    CONSTRAINT [PK_dbo.MetaAttributeLists] PRIMARY KEY CLUSTERED ([ID] ASC) 
); 
CREATE TABLE [dbo].[HireTools] (
    [Id] INT IDENTITY (1, 1) NOT NULL, 
    CONSTRAINT [PK_dbo.HireTools] PRIMARY KEY CLUSTERED ([Id] ASC) 
); 
CREATE TABLE [dbo].[MetaAttributeListOptions] (
    [Id]     INT   IDENTITY (1, 1) NOT NULL, 
    [OptionLabel]   NVARCHAR (MAX) NULL, 
    [MetaAttributeList_ID] INT   NULL, 
    CONSTRAINT [PK_dbo.MetaAttributeListOptions] PRIMARY KEY CLUSTERED ([Id] ASC), 
    CONSTRAINT [FK_dbo.MetaAttributeListOptions_dbo.MetaAttributeLists_MetaAttributeList_ID] FOREIGN KEY ([MetaAttributeList_ID]) REFERENCES [dbo].[MetaAttributeLists] ([ID]) 
); 
CREATE TABLE [dbo].[MetaAttributes] (
    [Id]  INT   IDENTITY (1, 1) NOT NULL, 
    [Code]  NVARCHAR (MAX) NULL, 
    [Label] NVARCHAR (MAX) NULL, 
    [Type]  INT   NOT NULL, 
    [Required] BIT   NOT NULL, 
    [Position] INT   NOT NULL, 
    [List_ID] INT   NULL, 
    CONSTRAINT [PK_dbo.MetaAttributes] PRIMARY KEY CLUSTERED ([Id] ASC), 
    CONSTRAINT [FK_dbo.MetaAttributes_dbo.MetaAttributeLists_List_ID] FOREIGN KEY ([List_ID]) REFERENCES [dbo].[MetaAttributeLists] ([ID]) 
); 
CREATE TABLE [dbo].[HireToolMetaAttributes] (
    [Id]    INT    IDENTITY (1, 1) NOT NULL, 
    [Value]   NVARCHAR (1000) NULL, 
    [HireTool_Id]  INT    NOT NULL, 
    [MetaAttribute_Id] INT    NOT NULL, 
    CONSTRAINT [PK_dbo.HireToolMetaAttributes] PRIMARY KEY CLUSTERED ([Id] ASC), 
    CONSTRAINT [FK_dbo.HireToolMetaAttributes_dbo.HireTools_HireTool_Id] FOREIGN KEY ([HireTool_Id]) REFERENCES [dbo].[HireTools] ([Id]) ON DELETE CASCADE, 
    CONSTRAINT [FK_dbo.HireToolMetaAttributes_dbo.MetaAttributes_MetaAttribute_Id] FOREIGN KEY ([MetaAttribute_Id]) REFERENCES [dbo].[MetaAttributes] ([Id]) ON DELETE CASCADE 
); 
GO 
CREATE NONCLUSTERED INDEX [IX_Value] 
    ON [dbo].[HireToolMetaAttributes]([Value] ASC); 
GO 
CREATE NONCLUSTERED INDEX [IX_HireTool_Id] 
    ON [dbo].[HireToolMetaAttributes]([HireTool_Id] ASC); 
GO 
CREATE NONCLUSTERED INDEX [IX_MetaAttribute_Id] 
    ON [dbo].[HireToolMetaAttributes]([MetaAttribute_Id] ASC); 
GO 
CREATE NONCLUSTERED INDEX [IX_MetaAttributeList_ID] 
    ON [dbo].[MetaAttributeListOptions]([MetaAttributeList_ID] ASC); 
GO 
CREATE NONCLUSTERED INDEX [IX_List_ID] 
    ON [dbo].[MetaAttributes]([List_ID] ASC); 
+0

Hey Dave, Wow! Dziękuję za wyczerpującą odpowiedź. Proszę pozwolić mi trochę czasu na przeczytanie, zrozumienie i wdrożenie. Jeśli mam jakieś pytania lub uwagi, opublikuję je tutaj :) – Shalan

+0

Przepraszamy za niedostarczenie - używam EF Code First, ale z C#. Ale jestem całkowicie komfortowy, konwertując twój VB – Shalan

+0

Cieszę się, że znalazłeś go kompletnie :) Na szczęście znalazłem to pytanie w tym samym dniu, w którym tworzyłem bardzo podobny model dla siebie, więc właśnie włożyłem twój model do mojego kodu do pisania i testowania.Przepraszam za brak źródeł, moja pamięć jest trochę niewyraźna, gdy widziałem ten wzór. –