.NET 5 i EntityFramework - podstawy

0

Cześć,

w końcu postanowiłem przestać być dinozaurem i tworzę testową aplikację wykorzystującą EntityFramework z podejściem CodeFirst.
Chciałbym żebyście doradzili mi czy dobrze do tego podchodzę.

Chciałbym stworzyć strukturę modeli dla czegoś typu magazyn. na ten moment zakładam następującą sytuację:

  1. Istnieją różne kategorie przedmiotów (enum)
  2. Na podstawie kategorii wczytywana jest lista właściwości przedmiotu (np. inne pola ma do uzupełnienia kategoria A, a inne kategoria B (chociaż niektóre mogą się powtarzać).
  3. Każda właściwość przedmiotu ma dostępne tłumaczenia na różne języki (np. "tytuł" => "title" => "titel" (pl, en, de).

Stworzyłem więc następujące modele:

Definicje:

public class ItemDetailDefinition
    {
        public int Id { get; set; }

        [Required]
        public string Name { get; set; }

        [Required]
        public System.Type ValueType { get; set; }

        public ICollection<ItemDetailTranslate> Translations { get; set; }
    }
public class ItemDetailTranslate
    {
        public int Id { get; set; }

        [Required]
        public string Translate { get; set; }

        [Required]
        public string Languages { get; set; }

        public ItemDetailDefinition ItemDetailDefinition { get; set; }
    }

Model mający połączyć definicję właściwości z daną kategorią:

public class ItemDetailDefinitionCategoryType
    {
        public int Id { get; set; }

        [Required]
        public bool IsRequired { get; set; }

        [Required]
        public DepositoryType ItemType { get; set; }

        [Required]
        public ItemDetailDefinition DetailDefinition { get; set; }
    }

zakładam, że w tabelce ItemDetailDefinitionCategoryType będzie wiele wierszy z np. kategorią(ItemType) A i w każdej będzie id z ItemDetailDefinition. W ten sposób będę mógł wyciągnąć wszystkie pola z danej kategorii i zbudować później z tego jakiś widok.

Pytanie tylko jak to połączyć podczas dodawania danego przedmiotu. Przykładowo user wybiera sobie kategorię A, wyświetlają mu sie pola do uzupełnienia, uzupełnia je, zapisuje, i co dalej? Mam wstępnie stworzony model Item:

public class Item
    {
        public int Id { get; set; }

        public DateTime? AddedDate { get; set; }

        [Required]
        public DepositoryType Type { get; set; }

        [Required]
        public ApplicationUser AddedBy { get; set; }

    }

Czy teraz powinienem mieć jeszcze model (i tabelę), który będzie miał w sobie Id itemu, Id Definicji pola i wpisaną wartość?

Coś typu:

    public class ItemValues
    {
        public int Id { get; set; }
        public Item Item { get; set; }
        public ItemDetailDefinition ItemDetailDefinition { get; set; }
        public string Value { get; set; }
    }

Widzę tutaj taki problem, że Value to może być dowolny typ, więc muszę użyć stringa.

Czy w ogóle takie podejście jest dobre, czy robi się to inaczej?
Ilość i rodzaj pól może się zmieniać.

2

Zacząłeś z grubej rury. Przede wszystkim pytanie, czy kategorie w enum są ok. Jeśli dojdzie jakaś kategoria, będzie to wymagało zmian w kodzie, a to jest już złe. Zatem kategoria powinna mieć własną tabelę:

class Category
{
  public int Id {get; set;}
  public string Name {get;set;}
}

Ale, jeśli chcesz mieć to przetłumaczalne, to tłumaczenia też powinny być w innej tabeli, np:

class TranslatedCategoryName
{
  public int Id {get;set;}
  public string LangCode {get;set;}
  public string Value {get;set;}
}

Z tego wychodzi, że klasa Category zamiast prostego Name powinna mieć raczej:

class Category
{
  public int Id {get; set;}
  public TranslatedCategoryName Name {get;set;}
}

Potem, jeśli chcesz mieć w różnych kategoriach różne parametry, to ja bym podszedł chyba do zadania tak, że w osobnych tabelach byłyby osobne parametry. To generalnie nie jest łatwe i proste zadanie. Trzeba wiele rzeczy przemyśleć, potem pewnie pozmieniać w trakcie itd. Zacznij od czegoś prostszego. Zrób coś w rodzaju systemu zamówień. Będzie dużo łatwiej zacząć i naprawdę dużo się nauczysz.

0
Juhas napisał(a):

Zacząłeś z grubej rury.

To nie jest tak, że nie wiem nic. Po prostu EF jest dla mnie nowością ;)

Przede wszystkim pytanie, czy kategorie w enum są ok. Jeśli dojdzie jakaś kategoria, będzie to wymagało zmian w kodzie, a to jest już złe. Zatem kategoria powinna mieć własną tabelę:

Dobre pytanie i dobry pomysł ;) Ja po prostu bardziej się skupiłem w moich rozmyślaniach na samym problemie przechowywania informacji o parametrach, ale jak najbardziej się zgadzam.

Potem, jeśli chcesz mieć w różnych kategoriach różne parametry, to ja bym podszedł chyba do zadania tak, że w osobnych tabelach byłyby osobne parametry.

Możesz rozwinąć tą myśl: "że w osobnych tabelach byłyby osobne parametry."?

Zacznij od czegoś prostszego. Zrób coś w rodzaju systemu zamówień. Będzie dużo łatwiej zacząć i naprawdę dużo się nauczysz.

Spróbuje jednak zostać przy tym. Jak rzeczywiście nie pójdzie to naklepie system zamówień :P

0
[Required]
public System.Type ValueType { get; set; }

dlaczego to jest System.Type?

0

@WeiXiao: Chwilowo, żebym pamiętał, że tam ma być typ i wymyślił jakego typu propertiesa użyć ;)

Taki moj brzydki zwyczaj, ktorego musze sie oduczyc.

1
kobi55 napisał(a):
Juhas napisał(a):

Zacząłeś z grubej rury.

To nie jest tak, że nie wiem nic. Po prostu EF jest dla mnie nowością ;)

No właśnie o to chodzi. Później będzie dużo kombinowania z mapowaniem. Dlatego zacząłbym prościej.

Potem, jeśli chcesz mieć w różnych kategoriach różne parametry, to ja bym podszedł chyba do zadania tak, że w osobnych tabelach byłyby osobne parametry.

Możesz rozwinąć tą myśl: "że w osobnych tabelach byłyby osobne parametry."?

To tylko pierwsza myśl, więc nie wiem, czy dobra. Miałem taki "przebłysk". Potraktuj to jako pobieżnie zarysowane tabelki:

CarCategoryParams (ID, CategoryId, Year, Milage, Color)
ComputerCategoryParams (ID, CategoryId, Hdd, Cpu, Model)
TVCategoryParams(ID, CategoryId, ScreenSize, OS)

Categories(ID, Name)

Item(ID, CategoryId, Name, Producent, IsNew)

Nie traktuj pola CategoryId w tabelach parametrów jako redundancji. Nazwy tabel zawsze mogą być zupełnie inne :)
Wydaje mi się, że w tą stronę bym poszedł.

2
kobi55 napisał(a):

w końcu postanowiłem przestać być dinozaurem i tworzę testową aplikację wykorzystującą EntityFramework z podejściem CodeFirst.

No to gratulacje, zostałeś mamutem. ;)

Czy w ogóle takie podejście jest dobre, czy robi się to inaczej?
Ilość i rodzaj pól może się zmieniać.

Ogólnie to, co chcesz osiągnąć nazywa się EAV (entity attribute value) model.

class Category
{
    int Id;  // PK
    MultiString Name;
}

class Attribute
{
    int Id;  // PK
    int CategoryId; // FK
    MultiString Name;
    Type ValueType; // tu chyba enum trzeba zrobić, bo normalny Type się do bazy raczej nie zapisze, no chyba, że jako string, ale to i tak nie ma sensu.
}

class Entity
{
    int Id;
    MultiString Name;
}

class Value
{
    int EntityId;  // PK FK
    int AttributeId;  // PK FK
    string RawValue;
}

To tak w najprostszej wersji. Ogólnie musisz tutaj sam dbać o parsowanie RawValue przy odczycie i serializowanie przy zapisie. Możesz to sobie ułatwić komplikując model w ten sposób:

abstract class Value
{
    int EntityId;  // PK FK
    int AttributeId;  // PK FK
}

class MultiStringValue : Value
{
    MultiString Value;
}

class IntValue : Value
{
    int Value;
}

class DateTimeValue : Value
{
    DateTime Value;
}

// itd.

Tu dochodzi kolejny dylemat, czy mapować to jako TPH (wszystkie klasy w jednej tabeli), TPC (każda klasa w oddzielnej tabeli) czy TPT (oddzielna tabele na klasę bazową i na dziedziczące).

No, a MultiString to po prostu typ na wielojęzyczne napisy, wewnętrznie to słownik Dictionary<string, string>, no i pewnie parę metod oraz konstruktor.
Może mieć po prostu metodę podającą tłumaczenie dla danego kodu języka, a można mu wstrzykiwać aktualny język systemu/aplikacji, aby sam zwrócił wartość dla użytkownika.

Użyłem pól, bo nie chce mi się get i set pisać wszędzie, ale to oczywiście powinny być właściwości.

No i generalnie zastanów się nad tym podejściem. Jest ono często uznawane za antywzorzec, bo jest trudne w implementacji, dość niewydajne i utrudnia raportowanie.

0

To tak w najprostszej wersji. Ogólnie musisz tutaj sam dbać o parsowanie RawValue przy odczycie i serializowanie przy zapisie. Możesz to sobie ułatwić komplikując model w ten sposób:

Tak, tego się spodziewałem :)

No i generalnie zastanów się nad tym podejściem. Jest ono często uznawane za antywzorzec, bo jest trudne w implementacji, dość niewydajne i utrudnia raportowanie.

No właśnie wydaje mi się to zagmatwane. Tylko jak inaczej zrobić to co chcę osiągnąć? Czyli możliwość dodawania w locie nowych kolumn do kategorii.

1

Dodawania nowych kolumn do kategorii się nie da zrobić inaczej. Pytanie, czy to w ogóle jest potrzebne? Czy nie można mieć np. jednej kolumny opisującej wszystkie atrybuty danego produktu?

0
somekind napisał(a):

Pytanie, czy to w ogóle jest potrzebne? Czy nie można mieć np. jednej kolumny opisującej wszystkie atrybuty danego produktu?

Jednej kolumny opisującej wszystkie atrybuty? W sensie trzymać to w jakimś xml/jsonie?

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0