C# JSON projekt Rozwiązania

0

VisualStudio 2017 Comunity

Stoję przed dylematem.
Przepisuję "system" księgowy z Delphi na C#
I myślę aby dane dokumentów przechowywać w Bazie w postaci JSON
Baza To Postgresql i on obsługuje JSON.
I teraz pytanie czy takie rozwiązanie nie będzie strzałem w nogę, chodzi o ewentualne problemy w zapytaniach SQL?

Robię to mniej więcej tak:

[DataContract]
    public class TDaneDokRozny
    {

        [DataMember]
        public TNaglowekRozny Naglowek { get; set; } = null;
        [DataMember]
        public TCialoRozny Cialo { get; set; } = null;
        public TDaneDokRozny()
        {
            Naglowek = new TNaglowekRozny();
            Cialo = new TCialoRozny();
        }

    }

następnie testowa serializacja do pliku

FileStream stream1 = new FileStream("C:\\Users\\Admin\\Desktop\\plik.JSON", FileMode.OpenOrCreate);
            DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(TDaneDokRozny));
            ser.WriteObject(stream1, ta);

i deserializacja

 FileStream stream1 = new FileStream("C:\\Users\\Admin\\Desktop\\plik.JSON", FileMode.OpenOrCreate);
                        DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(TDaneDokRozny));
                        stream1.Position = 0;
                        dane = (TDaneDokRozny)ser.ReadObject(stream1);

Dane widzę prawidłowe więc chyba działa z pliku.
Tylko czy to nie jest ślepa uliczka?

0

Jeśli będziesz kiedyś chciał wyszukiwać po polach w json, to będzie to strzał w stopę. Od tego tabelki mają kolumny, żeby umieszczać w nich dane w najprostszej postaci. Korzystając z tego można robić klucze obce, słowniki i pilnować integralności danych.

0

Prześledź poniższy wątek. Tam był poruszany temat kontrahentów
Sprzedawanie i kupowanie tego samego towaru po różnych cenach. Historia kosztów i przychodów.

0

Nie zostałem zrozumiany
Postgresql Obsługuje typ json czyli działa wyszukiwanie
http://www.postgresqltutorial.com/postgresql-json/
Zastanawiam się nad wydajnością.
przykład.

Tabela

CREATE TABLE ksiegidokumenty
(
  dok_id serial NOT NULL,
  dokument json
)
INSERT INTO ksiegidokumenty(
            dokument)
    VALUES ('{ "customer": "Lily Bush", "items": {"product": "Diaper","qty": 24}}');
...

SELECT dok_id, json_object_keys(dokument)
  FROM ksiegidokumenty
  ;

Wyszukiwanie

SELECT dok_id, dokument -> 'customer'
  FROM ksiegidokumenty
  where  dokument -> 'items' ->> 'product'= 'Diaper'
  ;

I Dostajemy

dok_id ?column?
2 "Lily Bush"
3 "test"

0

Ja wiem, że obsługuje. Tylko po co sobie utrudniać, gdy można zrobić prościej?

0
konsul41 napisał(a):

Nie zostałem zrozumiany
Postgresql Obsługuje typ json czyli działa wyszukiwanie

Napisałem: Jeśli będziesz kiedyś chciał wyszukiwać po polach w json, to będzie to strzał w stopę.
Nie napisałem: Nie będzie działać wyszukiwanie.
BTW to, żę postgres obsługuje json nie oznacza, że obsłuży wyszukiwanie po polach. Nie oznacza też, że zrobi to wydajnie. Może to oznaczać, że po prostu waliduje format json zanim zapisze go. Na Twoje szczęście działa i wyszukiwanie, i indeksowanie, a więc można wyszukiwać i to wydajnie.

konsul41 napisał(a):

Zastanawiam się nad wydajnością.

Napisałem: "Od tego tabelki mają kolumny, żeby umieszczać w nich dane w najprostszej postaci. Korzystając z tego można robić klucze obce, słowniki i pilnować integralności danych."
Co miałem na myśli: Jeśli nie da się zaindeksować pola json, to będziesz miał tragiczną wydajność. Na szczęście da się. Więc z punktu widzenia wydajności będzie ok. Niestety nie założysz na takie pole klucza obcego, więc łatwo o niespójność danych.
Ponadto, ponieważ nie za bardzo masz możliwość wymuszenia struktury zapisywanego jsona (chyba, że trigger), więc łatwo rozwalić integralność bazy. Ponownie - skoro możesz pola json zapisać jako wartości kolumn (i wymusić typy danych), to dlaczego nie chcesz tego zrobić? Jest to klasyczne podejście, które ma masę plusów i niewiele minusów.

0
endrius napisał(a):

Ja wiem, że obsługuje. Tylko po co sobie utrudniać, gdy można zrobić prościej?

Serializacja ?

0

Ponadto, ponieważ nie za bardzo masz możliwość wymuszenia struktury zapisywanego jsona (chyba, że trigger), więc łatwo rozwalić integralność bazy. Ponownie - skoro możesz pola json zapisać jako wartości kolumn (i wymusić typy danych), to dlaczego nie chcesz tego zrobić? Jest to klasyczne podejście, które ma masę plusów i niewiele minusów.

Nie to, że nie chcę ale rozważam.
Przewagą nad klasycznym podejściem jest serializacja do obiektów C#;

0

A w jaki sposób będziesz json-a pobierał z bazy?

0

Z bazy pobieram Jako String (varchar)
public string daneJSON
{
get
{
return "";
}
set
{
using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(value)))
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(TDaneDokRozny));
DaneDok = (TDaneDokRozny)serializer.ReadObject(ms);
}
}
}
i mam odtworzony obiekt.

0

A jaki jest spodziewany zysk z takiego podejscia?
Księgowość raczej lubi tabelki.

0
konsul41 napisał(a):

Przewagą nad klasycznym podejściem jest serializacja do obiektów C#;

Ale co to za przewaga, skoro z baz relacyjnych też się da?

0

Tak czytając ten wątek, zastanawiam się do czego można wykorzystać ten ficzer tabel json

0
konsul41 napisał(a):

Z bazy pobieram Jako String (varchar)

Ale w jaki sposób to z bazy jest pobrane? Jakiś select?

0

Klasycznie
pole dokument jest typu json

SELECT dokument from ksiegidokumenty where dok_id = :dok_id;

Nie bagatelną zaletą jest prostota odwzorowania obiektu C# (serializacja) i zapisanie go w bazie, przy okazji nie tracimy zalet bazy w wyszukiwaniu.

Przy Takiej tabeli
CREATE TABLE ksiegidokumenty
(
dok_id serial NOT NULL,
dokument json
)

pole dokument mogło by wyglądać tak
{"Cialo":[{"brutto":128.08,"bruttoww":0,"dok_id":130593,"kontoma":"203-179","kontown":"751-1","main_id":50278,"nazwa":"Rónice Kursowe","opis":"Rónice Kursowe"}],"Naglowek":{"Kontrahent_id":14098,"NazwaDziennika":"inne","NumerDokumentu":"PK 2/2017","NumerDziennika":4,"TypDokumentu":"Rózny","brutto":0,"bruttoww":0,"datadok":"/Date(1483484400000+0100)/","datakurs":"/Date(1528437612957+0200)/","datapod":"/Date(1483484400000+0100)/","datarej":"/Date(1483484400000+0100)/","dok_id":50278,"dokwwal":false,"kurs":0,"miesiac":1,"opis":"","rok":2017,"tabelakurs":"","waluta":"","waluta_przelicznik":0}}
Przy okazji ten tekst pochodzi bezpośrednio z serializera i jest gotowy do wstawienia do bazy.

0

A tak wyglądałoby wyszukiwanie dokumentów z bieżącego okresu z dzienniaka inne

SELECT dok_id, dokument FROM ksiegidokumenty where dokument -> 'Naglowek'->>'rok'='2017' and dokument -> 'Naglowek'->>'miesiac'='1' and dokument -> 'Naglowek'->>'NazwaDziennika'='inne';
0
konsul41 napisał(a):

Klasycznie
pole dokument jest typu json

SELECT dokument from ksiegidokumenty where dok_id = :dok_id;

Nie bagatelną zaletą jest prostota odwzorowania obiektu C# (serializacja) i zapisanie go w bazie, przy okazji nie tracimy zalet bazy w wyszukiwaniu.

Przy Takiej tabeli
CREATE TABLE ksiegidokumenty
(
dok_id serial NOT NULL,
dokument json
)

pole dokument mogło by wyglądać tak
{"Cialo":[{"brutto":128.08,"bruttoww":0,"dok_id":130593,"kontoma":"203-179","kontown":"751-1","main_id":50278,"nazwa":"Rónice Kursowe","opis":"Rónice Kursowe"}],"Naglowek":{"Kontrahent_id":14098,"NazwaDziennika":"inne","NumerDokumentu":"PK 2/2017","NumerDziennika":4,"TypDokumentu":"Rózny","brutto":0,"bruttoww":0,"datadok":"/Date(1483484400000+0100)/","datakurs":"/Date(1528437612957+0200)/","datapod":"/Date(1483484400000+0100)/","datarej":"/Date(1483484400000+0100)/","dok_id":50278,"dokwwal":false,"kurs":0,"miesiac":1,"opis":"","rok":2017,"tabelakurs":"","waluta":"","waluta_przelicznik":0}}
Przy okazji ten tekst pochodzi bezpośrednio z serializera i jest gotowy do wstawienia do bazy.

Z tego, co napisano powyżej to nie jest żadna zaleta, a robienie tego w sposób, który podałeś jest bez sensu

0

A jaka zaleta jest podejścia klasycznego, jeżeli te dane będą sporadycznie przeliczane?
Nie potrzeba nawet specjalnego indeksowania, niby po co aby uzyskać listę dokumentów?
1 raz w miesiącu będzie sporządzony Rejestr VAT, Kilka razy w miesiącu zestawienie sprzedaży.

Zaletą jest prostota obsługi, moim zdaniem. Dodatkową zaletą jest proste odwzorowanie obiektu na dane w bazie.
Co widzisz przeciw?

0

Użytkownik konsul41
Patrząc od strony projektowania dużego systemu.
Dokument można przesłać jako json i określić jego typ(między modułami w sieci lokalnej), poprawia możliwości testowania systemu bez dostępu do bazy.

W Delphi ten "system" ma dobrze ponad 1mln wierszy kodu razem z pluginami i takie podejście rozwiązuje wiele problemów.

1

Każdy (?) obiekt możesz przesłać jako json. Nie ma znaczenia jak dane obiektu są przechowywane w bazie.
Plus jaki widzę (dla leniwych) to to, że nie trzeba generować tabelek w DB :). Tylko po co wtedy PGSQL? Może po prostu jakaś obiektowa baza?

1

Właśnie nie rozumiem, jaka jest zaleta przechowywania obiektu w bazie w polu json. Można przechowywać dane w normalnej tabeli i wyjdzie na to samo. ORM mapuje tabele na obiekty klas, więc tu nie musisz robić absolutnie nic, bo wszystko robi sie samo. A jak koniecznie do czegoś ci potrzebny json, to możesz serializować/deserializować w locie, metoda do tego to raptem 2 linie kodu.

0

Ok
obiekt dokumentu nie pobiera danych z bazy, dane ma dostarczane przez fabrykę po jego utworzeniu, Obiekt posiada repozytorium do pobierania słowników z bazy.
klasa danych
Zdefiniowana tak

 [DataContract]
    public class TDaneDokRozny
    {
        [DataMember]
        public TCialoRozny Cialo { get; set; } = null;
        [DataMember]
        public int dok_id { get; set; } = 0;
        [DataMember]
        public string NazwaDziennika { get; set; } = "";
        [DataMember]
        public int NumerDziennika { get; set; } = 0;
...

Kod ładujący dane do obiektu podejście klasyczne

string sql = "SELECT dok_id, nazwadziennika,mies, rok, dziennik, kontrachent_id, " +
                         " numerdok, datarej, datadok, dataop, datapod,   dokwwal ,typdok, waluta, kurs, datakurs, tabelakurs, przelicznik, " +
                         " brutto, bruttoww, obrotywn, obrotyma, opis" +
                         " FROM ksiegi_dokumenty_nag where dok_id=" + dok_id.ToString() + ";";
            NpgsqlConnection conn = new NpgsqlConnection(connstring);
            conn.Open();
            Npgsql.NpgsqlCommand cmd = new Npgsql.NpgsqlCommand(sql, conn);
            Npgsql.NpgsqlDataReader reader = cmd.ExecuteReader();
            reader.Read();
            TDaneDokRozny ta = new TDaneDokRozny();
            ta.dok_id = reader.IsDBNull(0) ? 0 : reader.GetInt32(0);
            ta.NazwaDziennika = reader.IsDBNull(1) ? "" : reader.GetString(1);
            ta.miesiac = reader.IsDBNull(2) ? 0 : Int32.Parse(reader.GetString(2));
            ta.rok = reader.IsDBNull(3) ? 0 : Int32.Parse(reader.GetString(3));
            ta.NumerDziennika = reader.IsDBNull(4) ? 0 : reader.GetInt32(4);
            ta.Kontrahent_id = reader.IsDBNull(5) ? 0 : reader.GetInt32(5);
            ta.NumerDokumentu = reader.IsDBNull(6) ? "" : reader.GetString(6);
            ta.datarej = reader.IsDBNull(7) ? DateTime.Now : reader.GetDateTime(7);
            ta.datadok = reader.IsDBNull(8) ? DateTime.Now : reader.GetDateTime(8);
            ta.datapod = reader.IsDBNull(9) ? DateTime.Now : reader.GetDateTime(9);
            ta.TypDokumentu = reader.IsDBNull(12) ? "" : reader.GetString(12);
            reader.Close();
            cmd.CommandText = "SELECT dok_id,main_id,waluta,kurs, " +
                         "nazwa,brutto,bruttoww,kontown,kontoma, " +
                         "opis,data,datakursu,status,waluta_przelicznik, " +
                         "datakurs,tabelakurs,datakurs_vat,tabelakurs_vat,przelicznik, " +
                         "kurs_vat,obrotywn,obrotyma,vat_ue,intrastat,doksiag,rejestrvat,koldek  " +
                         " FROM ksiegi_dokumenty_cialo where main_id=" + dok_id.ToString() + ";";
            reader = cmd.ExecuteReader();
            //bool wynik = reader.Read();
            while (reader.Read())
            {
                ta.Cialo.Add(new TCialoRoznyRow(reader.IsDBNull(0) ? 0 : reader.GetInt32(0),
                                                reader.IsDBNull(1) ? 0 : reader.GetInt32(1),
                                                reader.IsDBNull(4) ? "" : reader.GetString(4),
                                                reader.IsDBNull(5) ? 0 : reader.GetDecimal(5),
                                                reader.IsDBNull(6) ? 0 : reader.GetDecimal(6),
                                                reader.IsDBNull(7) ? "" : reader.GetString(7),
                                                reader.IsDBNull(8) ? "" : reader.GetString(8),
                                                reader.IsDBNull(4) ? "" : reader.GetString(4)));
            }
            conn.Close();
            FileStream stream1 = new FileStream("C:\\Users\\Admin\\Desktop\\plik.JSON", FileMode.OpenOrCreate);
            DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(TDaneDokRozny));
            ser.WriteObject(stream1, ta);
            return ta;

ładowanie danych nowe podejście

string sql = "SELECT dok_id, dokument FROM ksiegidokumenty where dokument ->> 'rok'='2017' and dokument ->> 'miesiac'='1' and dokument ->> 'NazwaDziennika'='inne';";
            NpgsqlConnection conn = new NpgsqlConnection(connstring);
            conn.Open();
            Npgsql.NpgsqlCommand cmd = new Npgsql.NpgsqlCommand(sql, conn);
            Npgsql.NpgsqlDataReader reader = cmd.ExecuteReader();
            reader.Read();
            return  = reader.IsDBNull(0) ? 0 : reader.GetInt32(0);

Korzyści z 2 zapytań mamy jedno kod dużo prostszy, pilnowanie tylko jednej tabeli w bazie.

Zauważcie że obiekt danych ma
[DataContract] i [DataMember] co umożliwia automatyzację serializacji i znacząco upraszcza tworzenie aplikacji.

3

No, ale jakbyś zamiast pisać z palca kod SQL podatny na SQL Injection (jak to nazwałeś w sposób "klasyczny") użył jakiegoś (Micro) ORMa, to pobieranie rekordu by zajęło 3 linijki.

0

To są wprawki.
Ja się przesiadam z Delphi i normalnie Parametryzuje zapytania.

Minusem ORM jest potrzeba opanowania kolej technologi.

0
karol75 napisał(a):

Ja się przesiadam z Delphi i normalnie Parametryzuje zapytania.

Co to znaczy "normalnie"? To znaczy z palca klepać zapytania? I co do tego ma Delphi, przecież (podobno) tam też są orm-y i z palca klepać nie trzeba

0

Ale Ja napisałem że nie korzystam z ORM.
Dobry jest chyba mormot, ale nie o to chodzi.

Co złego widzisz w klepaniu z palca SQL-a?

0
karol75 napisał(a):

Co złego widzisz w klepaniu z palca SQL-a?

Poza podatnością na SQL injection, to nie szkoda Ci po prostu życia na robienie małpiej roboty?

0

kulson a którego orm dla vc 2017 i c# polecasz?
Jakiś opis zastosowania?

0
karol75 napisał(a):

kulson a którego orm dla vc 2017 i c# polecasz?
Jakiś opis zastosowania?

Raczej już nie piszę nic w C#, ale pewnie Entity Framework będzie optymalny

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