Evolutie van de programmatie binnen Mercator

changement syntaxe mercator 10.1


Mercator 10.1 brengt een revolutie met zich mee met betrekking tot het programmeren binnen Mercator. Dit geldt voor customizers, externe applicaties, websites....

Momenteel verwerken we steeds DataRows in C# om toegang te krijgen tot de gegevens op de SQL-server. Dit wordt bijvoorbeeld gedaan door een code: billingEngine.PIEDS["veldnaam"] = …
Dit brengt enkele problemen met zich mee:

  • De programmeur moet ervoor zorgen dat de kolom "veldnaam" bestaat in de SQL tabel PIEDS_V / PIEDS_A
  • Men moet het type van het veld weten, bijvoorbeeld Convert.ToDoucle(billingEngine.PIEDS[""]);
  • Je kan IntelliSense niet gebruiken om over suggesties te beschikken.
  • De compiler zal nooit zeggen dat het veld "veldnaam" niet bestaat. Het bestaan van het veld controleren valt dus weg!
  • Het hele ding is lang om [] " + Contver.To te schrijven. Nu is de tijsbesparing aanzienlijk vergroot.

Op basis van dezelfde observatie heeft Microsoft verschillende oplossingen geleverd waardoor er min of meer automatische objectklassen gecreëerd kunnen worden die overeenkomen met de database-structuur. Deze oplossingen zijn:

  • Linq to SQL
  • Entity Framework

Voor Mercator hebben ze wel 1 groot nadeel: het is onmogelijk om deze klassen buiten de ontwikkelomgeving, at run-time, te genereren.
Nu zijn alle Mercators verschillend. Ze beschikken niet over dezelfde SQL-database en de database wijzigt voortdurend. Mercator Majuro heeft ook een goede flexibiliteit in onze relatie tot de database gebracht, het is niet langer verplicht om gebruikers uit Mercator te halen om het database schema te wijzigen.
Het zou dan ook spijtig zijn om deze flexibiliteit te verliezen, om over bovengenoemde tools te beschikken.

Merk op dat het Entity Framework extreem krachtig is en een veel bredere ambitie heeft dan de opmerkingen in dit bericht.


Om deze flexibiliteit te behouden lijkt het ons zinvol om de syntaxis te moderniseren.
Dit is gedaan en werd mogelijk gemaakt via een mechanisme die je toelaat om het database-schema te compileren vanuit het Mercatordossier (in MercatorDatabase.dll).
We distribueren een relatief agnostische versie van MercatorDatabase.dll (met basisvelden, "minimum bestaansvermogen"), maar het is ten allen tijde mogelijk om de klassen die deze bevat te wijzigen. Het databaseschema kan daarom op aanvraag worden geüpdatet via de Geavanceerde Tools van Mercator. De dll kan dan verdeeld worden via Beheer -> SQL-bestanden -> Assemblies). De cirkel is rond!


Daarnaast is het een feit dat het gebruik van Linq ook erg interessant en opmerkelijk is!

Linq is een uitbreidingsset waarmee u de optelbare objecten van het .NET-framework kan gebruiken in een compactere syntax.
Hieronder is een voorbeeld gegeven:

// voor
double d1 = 0;
foreach (DataRow dr in billingEngine.LIGNES.Select("sel=1"))
    d1 += Convert.ToDouble(dr["q"]);
// na
double d1 = billingEngine.LignesVRecords.Where(l => l.SEL).Sum(l => l.Q);

3 regels code worden dus 1. En deze code doet bovendien uitsluitend een aanroep op types die door de compiler bekend zijn, via Intellisense.

Het gebruik van Linq is later opgenomen in Mercator. De afwezigheid van Linq werd veroorzaakt door de compatibiliteitsbeperking tussen de Aruba en Majuro versies van Mercator. De Aruba versie was beperkt tot framework 2.0, terwijl Linq geïntegreerd werd in framework 3.5. Hierdoor was het niet mogelijk om Linq vroeger ter beschikking te stellen.


Dit is waar de twee punten elkaar ontmoeten: de structuur van de DB-klassen en Linq. Inderdaad, alle verzamelingen in MercatorDatabase.dll implementeren de IEnumerable interface. Dit maakt ze onmiddelijk bruikbaar in Linq, zonder een Cast<>() te moeten doorlopen.
De programmeringsstijl rond Mercator zal zich hierop verder ontwikkelen.

Alle modules op onze website onder de rubriek Voorbeelden van codes zijn herzien en herschreven op basis van bovenstaande methodes.
De vergelijking Voor - Na van enkele modules is interessant, bijvoorbeeld de heel bekende customizer cadeaubonnen:

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Windows.Forms;
using MercatorApi;
using MercatorUi;

namespace Billing
{
    public class Customizer : MercatorUi.ICustomizers.IBillingEngineCreated, MercatorUi.ICustomizers.IBillingEngineClosed
    {
        public void BillingEngineCreated(MercatorUi.Engine.Gescom.BillingEngine BillingEngine)
        {
            BillingEngine.AfterInsertItem += new MercatorUi.Engine.Gescom.BillingEngine.AfterInsertItemEventHandler(AfterInsertItem);
            BillingEngine.BeforePayment += new EventHandler(BeforePayment);
            BillingEngine.AfterSave += new EventHandler(AfterSave);
        }

        public void BillingEngineClosed(MercatorUi.Engine.Gescom.BillingEngine BillingEngine)
        {
            BillingEngine.AfterInsertItem -= new MercatorUi.Engine.Gescom.BillingEngine.AfterInsertItemEventHandler(AfterInsertItem);
            BillingEngine.BeforePayment -= new EventHandler(BeforePayment);
            BillingEngine.AfterSave -= new EventHandler(AfterSave);
        }

        void AfterInsertItem(object sender, MercatorUi.Engine.Gescom.BillingEngine.AfterInsertItemEventArgs e)
        {
            MercatorUi.Engine.Gescom.BillingEngine billingEngine = (MercatorUi.Engine.Gescom.BillingEngine)sender;
            if (e.DataRowLignes["S_CLE1"].ToString().Trim() == "UTILBONCAD")
            {
                string id_boncad = Dialogs.AskString("Gelieve de barcode te scannen", "", true, 35);
                if (id_boncad != "")
                {
                    DataRow[] drcad = billingEngine.LIGNES.Select("id_boncad='" + id_boncad.Trim() + "'");
                    int nbBoncad = 0;
                    if (drcad != null)
                    {
                        foreach (DataRow drcad2 in drcad)
                        {
                            nbBoncad++;
                        }
                    }
                    if (nbBoncad == 0)
                    {
                        string reqSqlcad = "select count(*) as nbUtil, sum(q) as q, sum(pu) as pu from lignes_v where id_boncad=@id_boncad";
                        DataSet dscad = Api.Zselect(MercatorUi.Globals.RepData, reqSqlcad, new MercatorSqlParam("@id_boncad", id_boncad, SqlDbType.Char));
                        if (dscad != null)
                        {
                            if (Convert.ToInt16(dscad.Tables[0].Rows[0]["nbUtil"]) == 0)
                            {
                                Dialogs.Stop("Deze geschenkbon bestaat niet!");
                                e.CancelInsertItem = true;
                            }
                            else
                            {
                                if (Convert.ToInt16(dscad.Tables[0].Rows[0]["nbUtil"]) > 1)
                                {
                                    Dialogs.Stop("Deze geschenkbon werd al gebruikt!");
                                    e.CancelInsertItem = true;
                                }
                                else
                                {
                                    e.DataRowLignes["Q"] = Convert.ToDouble(dscad.Tables[0].Rows[0]["q"]) * -1;
                                    e.DataRowLignes["PU"] = Convert.ToDouble(dscad.Tables[0].Rows[0]["pu"]);
                                    e.DataRowLignes["ID_BONCAD"] = id_boncad.Trim();
                                }
                            }
                        }
                        else
                        {
                            e.CancelInsertItem = true;
                        }
                    }
                    else
                    {
                        Dialogs.Stop("Deze geschenkbon werd al gebruikt in deze verkoop!");
                        e.CancelInsertItem = true;
                    }
                }
                else
                {
                    Dialogs.Stop("Lege barcode !");
                    e.CancelInsertItem = true;
                }
            }
        }

        void BeforePayment(object sender, EventArgs e)
        {
            MercatorUi.Engine.Gescom.BillingEngine billingEngine = (MercatorUi.Engine.Gescom.BillingEngine)sender;
            DataRow[] drBonCad = billingEngine.LIGNES.Select("s_cle1='BONCAD'");
            if (drBonCad.Length != 0)
            {
                int nLigne = 0;
                foreach (DataRow drBC in billingEngine.LIGNES.Rows)
                {
                    if (drBC["S_CLE1"].ToString() == "BONCAD")
                    {
                        billingEngine.LIGNES.Rows[nLigne]["id_boncad"] = billingEngine.PIEDS["id"].ToString() + billingEngine.LIGNES.Rows[nLigne]["dl_id"].ToString();
                    }
                    nLigne++;
                }
            }
        }

        void AfterSave(object sender, EventArgs e)
        {
            MercatorUi.Engine.Gescom.BillingEngine billingEngine = (MercatorUi.Engine.Gescom.BillingEngine)sender;
            DataRow[] drBonCad = billingEngine.LIGNES.Select("s_cle1='BONCAD'");
            if (drBonCad.Length != 0)
            {
                DataSet ds = new DataSet();
                DataTable dt_lignes;
                DataTable dt_pieds;
                foreach (DataRow drBC in billingEngine.LIGNES.Rows)
                {
                    if (drBC["S_CLE1"].ToString() == "BONCAD")
                    {
                        ds = new DataSet();
                        dt_lignes = new DataTable();
                        dt_lignes = billingEngine.LIGNES.Clone();
                        dt_lignes.ImportRow(drBC);
                        dt_pieds = new DataTable();
                        dt_pieds = billingEngine.PIEDS.Table.Clone();
                        dt_pieds.ImportRow(billingEngine.PIEDS);
                        ds.Tables.Add(dt_lignes);
                        ds.Tables.Add(dt_pieds);
                        ds.Tables.Add(Api.DataTableFromDico(MercatorUi.Globals.ParamIdentif, "ParamIdentif"));
                        List<MercatorUi.Reporting.OutputDescriptor> listOutputDescriptors = new List<MercatorUi.Reporting.OutputDescriptor>();
                        listOutputDescriptors.Add(new MercatorUi.Reporting.OutputDescriptorPrint());
                        MercatorUi.Reporting.ReportingStatic.Reporting.RunReport("BonCadeau", @"<MainDir\BonCadeau.repx", ds, listOutputDescriptors);
                    }
                }
            }
        }
    }
}

Na

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Linq;
using MercatorApi;
using MercatorUi;

namespace Billing
{
    public class Customizer : MercatorUi.ICustomizers.IBillingEngineCreated, MercatorUi.ICustomizers.IBillingEngineClosed
    {
        public void BillingEngineCreated(MercatorUi.Engine.Gescom.BillingEngine BillingEngine)
        {
            BillingEngine.AfterInsertItem += AfterInsertItem;
            BillingEngine.BeforePayment += BeforePayment;
            BillingEngine.AfterSave += AfterSave;
        }

        public void BillingEngineClosed(MercatorUi.Engine.Gescom.BillingEngine BillingEngine)
        {
            BillingEngine.AfterInsertItem -= AfterInsertItem;
            BillingEngine.BeforePayment -= BeforePayment;
            BillingEngine.AfterSave -= AfterSave;
        }

        void AfterInsertItem(object sender, MercatorUi.Engine.Gescom.BillingEngine.AfterInsertItemEventArgs e)
        {
            MercatorUi.Engine.Gescom.BillingEngine billingEngine = (MercatorUi.Engine.Gescom.BillingEngine)sender;
            if (e.LignesVRecord.S_CLE1 == "UTILBONCAD")
            {
                string id_boncad = Dialogs.AskString("Gelieve de barcode te scannen", "", true, 35).Trim();
                if (id_boncad != "")
                {
                    int nbBoncad = billingEngine.LignesVRecords.Where(l => l.ID_BONCAD == id_boncad).Count();
                    if (nbBoncad == 0)
                    {
                        string reqSqlcad = "select count(*) as nbUtil, sum(q) as q, sum(pu) as pu from lignes_v where id_boncad=@id_boncad";
                        DataSet dscad = Api.Zselect(MercatorUi.Globals.RepData, reqSqlcad, new MercatorSqlParam("@id_boncad", id_boncad, SqlDbType.Char));
                        if (dscad != null)
                        {
                            if (Convert.ToInt16(dscad.Tables[0].Rows[0]["nbUtil"]) == 0)
                            {
                                Dialogs.Stop("Deze geschenkbon bestaat niet!");
                                e.CancelInsertItem = true;
                            }
                            else if (Convert.ToInt16(dscad.Tables[0].Rows[0]["nbUtil"]) > 1)
                            {
                                Dialogs.Stop("Deze geschenkbon werd al gebruikt!");
                                e.CancelInsertItem = true;
                            }
                            else
                            {
                                e.LignesVRecord.Q = Convert.ToDouble(dscad.Tables[0].Rows[0]["q"]) * -1;
                                e.LignesVRecord.PU = Convert.ToDouble(dscad.Tables[0].Rows[0]["pu"]);
                                e.LignesVRecord.ID_BONCAD = id_boncad;
                            }

                        }
                        else
                        {
                            e.CancelInsertItem = true;
                        }
                    }
                    else
                    {
                        Dialogs.Stop("Deze geschenkbon werd al gebruikt in deze verkoop!");
                        e.CancelInsertItem = true;
                    }
                }
                else
                {
                    Dialogs.Stop("Lege barcode !");
                    e.CancelInsertItem = true;
                }
            }
        }

        void BeforePayment(object sender, EventArgs e)
        {
            MercatorUi.Engine.Gescom.BillingEngine billingEngine = (MercatorUi.Engine.Gescom.BillingEngine)sender;
            billingEngine.LignesVRecords.Where(l => l.S_CLE1 == "BONCAD").ToList().ForEach(l => l.ID_BONCAD = billingEngine.PiedsVRecord.ID + l.DL_ID);
        }

        void AfterSave(object sender, EventArgs e)
        {
            MercatorUi.Engine.Gescom.BillingEngine billingEngine = (MercatorUi.Engine.Gescom.BillingEngine)sender;
            if (billingEngine.LignesVRecords.Where(l => l.S_CLE1 == "BONCAD").Count() != 0)
            {
                DataSet ds = new DataSet();
                DataTable dt_lignes;
                DataTable dt_pieds;
                foreach (DataRow drBC in billingEngine.LIGNES.Select("S_CLE1='BONCAD'"))
                {
                    ds = new DataSet();
                    dt_lignes = new DataTable();
                    dt_lignes = billingEngine.LIGNES.Clone();
                    dt_lignes.ImportRow(drBC);
                    dt_pieds = new DataTable();
                    dt_pieds = billingEngine.PIEDS.Table.Clone();
                    dt_pieds.ImportRow(billingEngine.PIEDS);
                    ds.Tables.Add(dt_lignes);
                    ds.Tables.Add(dt_pieds);
                    ds.Tables.Add(Api.DataTableFromDico(MercatorUi.Globals.ParamIdentif, "ParamIdentif"));
                    List<MercatorUi.Reporting.OutputDescriptor> listOutputDescriptors = new List<MercatorUi.Reporting.OutputDescriptor>();
                    listOutputDescriptors.Add(new MercatorUi.Reporting.OutputDescriptorPrint());
                    MercatorUi.Reporting.ReportingStatic.Reporting.RunReport("BonCadeau", @"<MainDir\BonCadeau.repx", ds, listOutputDescriptors);
                }
            }
        }
    }
}
 

Alle nieuwe klassen hebben een naam die eindigt met Record of Records. Daardoor zijn ze heel makkelijk terug te vinden in Intellisense. Merk op dat deze klasen de informatie niet dupliceren. Dit is alleen een nieuwe manier om te verwijzen naar dezelfde gegevens als voorheen.

Het is natuurlijk niet verplicht om alle bestaande code te herschrijven met de nieuwe syntaxis. Deze nieuwe notatie heeft het doel om het comfort van de programmeur te verbeteren en is dus de keuze van elke programmeur.