Pagina 1 di 2 1 2 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 12
  1. #1

    [C#] [Class library] Logica di business e ORM

    Ciao, mi occupo di applicativi contabili e che hanno una logica di business molto complessa. Di solito lavoro in c# e con sql server come dbms.
    Per molti anni col mio team abbiamo lavorato su un framework proprietario e che si basava sui dataset per la rappresentazione in memoria dei dati. Il riempimento del grafo della vista di ogni pagina/form ed il salvataggio era totalmente a carico del framework e quasi trasparente all'applicazione.
    Ora intendiamo aggiornare la tecnologia per avvalerci di un ORM pubblico.
    Stiamo quindi analizzando i software esistenti per capire come muoverci. Ho analizzato diversi ORM (tra cui NHibernate e EF ) ma c'è un'esigenza che mi sembra non coprano ed ora ve la illustro.
    Abbiamo la necessità di avere un ciclo del tipo (molto sinteticamente):


    begin transaction
    Applica modifiche al database
    interroga la logica di business (una parte di essa, le regole "post")
    se ci sono messaggi di errore
    rollback transaction
    altrimenti
    commit transaction

    Ho omesso altre cose (i controlli di validazione prima della transazione lato cliente e server e le regole di business "pre-salvataggio") perché quelle sono invece abbastanza semplici da implementare.
    La nostra necessità è di chiamare una serie di stored procedures (la logica di business è pre-compilata in codice sql) che devono agire dopo che le modifiche sono stati applicate, ed i trigger sono scattati, ma prima della commit, perché altrimenti non avremmo modo di annullare tutto se le regole che scattano sono non ignorabili oppure sono ignorabili ma il cliente decide di non ignorarle.

    Vi chiedo quindi se conoscete NHibernate o qualsiasi altro ORM di quelli più noti che consenta una personalizzazione del genere.

    Vi ringrazio in anticipo.
    Gaetano

  2. #2
    Moderatore di Programmazione L'avatar di LeleFT
    Registrato dal
    Jun 2003
    Messaggi
    17,326

    Moderazione

    I linguaggi .NET hanno un'area dedicata: "Visual Basic e .NET Framework".

    Sposto.


    Ciao.
    "Perchè spendere anche solo 5 dollari per un S.O., quando posso averne uno gratis e spendere quei 5 dollari per 5 bottiglie di birra?" [Jon "maddog" Hall]
    Fatti non foste a viver come bruti, ma per seguir virtute e canoscenza

  3. #3
    Se avete un framework vostro tenetevelo ben stretto, nessuno può conoscere meglio di voi le vostre esigenze ed il pattern dataset + adapter non è per nulla stupido, anzi...

    Per il momento sconsiglio l'uso di Entity Framework, ha troppe lacune nella gestione dello stato interno per le quali si è costretti molto spesso a ricorrere a ricaricare i dati dalla data source anche quando non è strettamente necessario (ad esempio quando un trigger lato server interrompe una transazione generando un errore).

  4. #4

    infatti il nodo cruciale sono i trigger

    Grazie della risposta, infatti il problema è che le regole sono abbastanza complesse e fanno spesso uso o di totalizzatori gestiti da trigger oppure in alcuni casi fanno dei controlli incrociati tra varie tabelle ed è per questo che è necessario che alcune condizioni siano testate dopo l'applicazione delle modifiche.

    In realtà però non utilizziamo i dataadapter, diamo i dataset direttamente in pasto ad una classe generica che si occupa di salvarli e di interrogare le regole lato server (che sono precompilate come sp in base alla configurazione del cliente).

    Seguendo il tuo consiglio allora potrei cercare di non abbandonare l'uso dei dataset ma vedo che ormai non li usa nessuno nelle grosse applicazioni...

  5. #5
    Volevo precisare che lo "sconsiglio" era solo riguardante Entity Framework microsoft, se si comincia ad avere bisogno di implementare logiche custom mostra le sue lacune, non conosco NHibernate, per quanto possa saperne io potrebbe esser ottimo o pessimo, non ne ho idea.

  6. #6
    Utente di HTML.it L'avatar di rsdpzed
    Registrato dal
    Aug 2001
    Messaggi
    764
    ciao Galeazzo
    con ef puoi persistere le modifiche senza azzerare il context. In questo modo in caso di validazione post fallita puoi attraversare gli oggetti del context e ripristinare lo stato precedente.
    Questo a grandi linee perche personalmente non ho mai avuto bisogno di addentrarmi piu di tanto in questo ramo di EF se non teoricamente quando l'ho dovuto imparare.

    Potresti, nell'ambito di un metodo di una classe di business, fare qualcosa tipo:

    codice:
    //eseguo operazioni sul context. il context si popola di oggetti con relativo ObjectState
    
    //persisto ma lascio che il context continui a tenere traccia dello stato degli oggetti attraverso l'objectStateManager.
    ctx.SaveChanges(false);
    
    //lancio le SP
    
    if (esito ==  ok)
      //ctx.AcceptAllChanges(); //azzero il context, tutti gli oggetti e i relativi stati vengono eliminati
    else
      //avvalendomi degli oggetti ancora presenti nel context (tracciati dall'objectstateManager) eseguo il rollback.
    questo a grandi linee, ora la parte piu complessa è capire se o in che misura è fattibile l'operazione di rollback in casi complessi come i tuoi, per questo credo ci sia bisogno di studiare ef piu approfonditamente e testare.

    P.S.
    visto che devi riscrivere parte del software non si potrebbe pensare di spostare un po di logica prima della persistenza?

  7. #7
    Ciao rsdpzed,
    se non ho compreso male mi stai dicendo che avverrebbe comunque il commit però poi dovrei, a ritroso, ripristinare la situazione precedente.
    Quindi, se ho fatto delle delete, rifare le insert, se ho fatto delle insert fare le delete e se ho fatto delle update ripristinare i valori di origine. Vedo degli svantaggi però:
    1) lasciarei il db temporaneamente in una situazione di violazione delle regole
    2) non sono certo che rifacendo le operazioni i trigger mi ripristinerebbero la situazione correttamente. Ad esempio, se c'è (è solo un esempio, nel caso mio non ce ne sono, ma vi possono essere casi del genere) un trigger che fa il cascade delete, rifacendo l'inserimento non ricrea le righe cancellate
    Insomma non posso permettere che venga fatto un commit di una situazione errata...


    questo a grandi linee, ora la parte piu complessa è capire se o in che misura è fattibile l'operazione di rollback in casi complessi come i tuoi, per questo credo ci sia bisogno di studiare ef piu approfonditamente e testare.
    Si ma questo è chiaro, ossia, come avevo detto, le validazioni lato client e le regole di business "pre-salvataggio" ci sono ma su quelle non ho problemi.


    Mi era anche venuto in mente che potevo racchiudere il tutto in una transazione esterna ma a quanto pare nhibernate non ammette transaction annidate. Forse EF le ammette? devo indagare.

    Inoltre mi farebbe molto comodo una istruzione simile a quella che nel dataset è RejectChanges nei dati da salvare.
    Ossia tipo una ctx.RejectChanges(), spero ci sia.

  8. #8
    Manca proprio il rejectchanges. Dovresti implementarti a mano la logica, ma è talmente poco documentata, talmente poco trasparente, che si fa prima a ricaricare tutti i dati dal context. Questo è un grosso punto debole a mio avviso nell usare l'entity framework come qualcosa di più di un mero framework di persistenza per gli oggetti applicativi. Per applicazioni orientate ai dati secondo me causa troppo overhead e più grattacapi che soluzioni. Per applicazioni middle tier con moli di dati importanti poi, secondo me, le prestazioni non sono all'altezza, troppo overhead nella conversione dei dati in oggetti, meglio le stored procedures ed il buon vecchio paradigma relazionale.

    Scrivevi che il tuo framework non è al 100% trasparante, con un ORM penso peggiorerai le cose: a mio avviso nessun ORM può essere al 100% trasparente.

    Ad esempio in E.F.

    codice:
        var query = context.ExecuteQuery("A");
        foreach(var item in query)
        {
              Console.WriteLine(item.A.ID.ToString() + "->" + item.B.ID.ToString());
        }
    apri sql profiler, vedrai che per ogni record in A viene eseguita una query sql select * from B where A_ID=@ID, uccidendo le prestazioni, a meno che tu non esegua la query seguente

    codice:
        var query = context.ExecuteQuery("A").Include("B");
    Non mi pare un grosso esempio di trasparenza, semplicemente perchè non è possibile avere trasparenza al 100%, quindi la mia conclusione è che:

    gli ORM non sono trasparenti
    vanno studiati ed imparati, ma non evitano di dovere imparare ad usare uno o più DBMS
    bisogna fare workarounds per permettergli di eseguire regole complesse
    sono più lenti rispetto l'uso dei client nativi
    incapsuleranno anche motori di tipo diverso (EF no), ma lo stesso fanno anche i dataadapters (o codice custom)
    sono vantaggiosi solo nel caso servano a rendere persistenti oggetti di semplici applicazioni.

    Ti posto un interessante articolo in inglese di un blog che riassume il mio pensiero circa il fatto che gli ORM sono un ANTI PATTERN

    Ovviamente sono opinioni personali, un po' come meglio le brune o le bionde, meglio mac o windows, ruby o phyton... normalmente non mi piace fare polemiche ma siccome nessuno parla mai male degli ORM, perchè ora è la moda imperante, ho dovuto un po fare io questa parte di avvocato del diavolo.

  9. #9
    Utente di HTML.it L'avatar di U235
    Registrato dal
    Mar 2006
    Messaggi
    1,539
    Ciao,
    usando EF Code Firt e facendo un override del metodo ValidateEntity del DbContext, puoi passare alle classi che rappresentano le tue tabelle (che ovviamente devono implementare IValidatableObject) il contesto attuale (quello che stai andando a modificare) all'interno del metodo Validate, in questo modo se ci sono degli errori puoi accodarli al IEnumerable<ValidationResult> restituito dal metodo (Validate) e determinare se è possibile proseguire con il salvataggio, e qualora non sia possibile avresti anche a disposizione le motivazioni senza dover eseguire nessun rollback, oltretutto, se implementi nella vista un meccanismo di notifica degli errori, ottieni che nel momento stesso che viene inserito un dato che va contro le regole, ti viene notificato immediatamente, se poi avessi bisogno di azzerare tutto del context attuale (quello che ha ancora le modifiche ma che non sono state salvate perchè contengono errori) puoi usare il ChangeTracker del DbContext e fare un reload di tutto o del singolo dato/tabella all'ultimo salvataggio, oppure semplicemente segnalare di quale errore si tratta, in questo caso fintanto che non sarà "sistemato" dall'utente non potrai salvare continuando a segnalare l'errore senza dover ripartire da capo.
    Quando si parla di EF, bisognerebbe distinguere nettamente se si parla di Database First (magari anche con edmx) o di Code First e classi POCO. Con la seconda hai il totale controllo di qualsiasi aspetto.


  10. #10

    Ma cosa mi costa di più?

    Ciao U235,
    nel mentre ho trovato che anche con NHibernate posso aggiungere un evento AfterSave che mi consente di interrogare il db dopo che sono state effettuate le modifiche e scattati i trigger ma prima del commit.
    Tuttavia in Nhibernate l'unica cosa che posso fare è scatenare un'eccezione.
    Sinceramente sto pensando di adattare l'ORM che ho, perché sto vedendo che alcune funzioni che oggi ottengo in modo trasparente con gli altri ORM dovrei lavorare un bel po'.
    Giusto per intenderci:
    1) gestione della sicurezza
    Attualmente posso gestire quali sono le operazioni di C/R/U/D per ciascuna voce dell'organigramma, dove nelle regole di sicurezza posso usare variabili d'ambiente e che dipendono dallo specifico utente, inoltre posso effettuare il check di sicurezza sulla base dei valori della riga modificata e non solo in base al nome della tabella

    2)campi ad autoincremento
    Posso gestire dei campi tipici delle tabelle contabili che dipendono, per il calcolo, dai valori di altre colonne. Ad esempio, tipicamente, la numerazione dei mandati ricomincia da uno ogni anno.
    Nell'ORM che uso semplicemente, nella classe metadato, in un metodo che si chiama GetNewRow, scrivo (faccio copia incolla):

    RowChange.MarkAsAutoincrement(T.Columns["npay"], null, null, 0);
    RowChange.SetMySelector(T.Columns["npay"], "ypay", 0);
    ed ho finito. Quel che non mi piace è che si basa sul dataset sottostante, ma quanto lavoro devi fare per ottenere la stessa cosa con un altro orm? E ti ho fatto un caso semplice, in realtà a volte il calcolo dipende da più campi, maschere di bit etc.

    3)gestione regole pre e post nel ciclo di interazione
    Quando le regole che scattano sono ignorabili, devo presentarle all'utente che può decidere di ignorarle e quindi procedere ugualmente col salvataggio. A quel punto devo RIFARE tutto quello che avevo fatto nel primo salvataggio e vedere se scattano altre regole o sono sempre quelle di prima. Se sono quelle di prima (e quindi mi serve essermele salvate da qualche parte) posso procedere altrimenti devo RIPRESENTARE all'utente le regole che prima non erano scattate. Può capitare perché il db nel mentre potrebbe essere cambiato.
    E così via sin quando
    a) non scattano nuove regole
    b) le regole che scattano sono NON ignorabili

    Questo con l'ORM attuale è automatico e mi sembra che dovrei fare un sacco di lavoro per ottenerlo con gli ORM in giro

    4)Salvataggio chiavi delle master/detail, inclusi campi ad autoincremento
    Se ho dei campi ad autoincremento nella tabella master che devono essere riportati nelle tabelle detail (possono esserci più righe da salvare sia nell'una che nelle altre tabelle) l'ORM attuale stabilisce da solo l'ordine di salvataggio e ribalta le modifiche fatte in fase di inserimento ( i campi sono calcolati appena prima l'INSERT della singola riga) e ove necessario aggiorna le righe detail, in base alle relazioni tra le tabelle, in cascata anche su 3 o più livelli. Per ottenere la stessa cosa con gli altri ORM devo scrivere codice.

    E questi sono solo alcuni esempi...

    insomma mi pare che ci voglia più lavoro ad ottenere dagli altri ORM quello che già ho che non migliorare l'orm attuale per far si che non usi più i dataset ma classi POCO.

    Questa è l'idea che mi sto facendo, e sinceramente ero partito con tutt'altre intenzioni.

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.