Sinds altijd gebruikt Mercator System.Data.SqlClient om met de SQL-server te communiceren. Deze bibliotheek wordt vervangen door Microsoft.Data.SqlClient. De oude bibliotheek wordt door Microsoft beschouwd als zijnde in “maintenance mode” en zal dus geen nieuwe functionaliteiten meer ontvangen.
Microsoft raadt aan om te migreren naar de nieuwe bibliotheek
- omwille van performantie
- omdat de beveiliging van deze bibliotheek verbeterd is
- omdat ze ontwikkelingen mogelijk maakt die gericht zijn op artificiële intelligentie
- …
Meer informatie hierover vindt u op deze pagina.
Mercator Core, gecompileerd met .net 10, gebruikt Microsoft.Data.SqlClient. Voor een Mercator zonder maatwerkcode is deze wijziging volledig transparant. Daarentegen moet alle gepersonaliseerde code die System.Data.SqlClient gebruikt, worden aangepast, aangezien deze Mercator deze bibliotheek niet meer kent. Deze migratie moet zorgvuldig worden uitgevoerd en rekening houden met de mogelijkheid om in hybride modus te werken, m.a.w. binnen éénzelfde configuratie met Mercator in klassieke versie of .net Core 8.0.
Om deze overgang te vergemakkelijken, voorziet Mercator in een reeks nieuwe klassen, methodes en eigenschappen die het mogelijk maken om code “agnostisch” te maken ten opzichte van de gebruikte bibliotheek. Met andere woorden: deze klassen, methodes en eigenschappen bestaan identiek in alle versies van Mercator (.net 4.8, .net 8.0 en .net 10.0). Enkel hun interne implementatie bepaalt of System.Data.SqlClient dan wel Microsoft.Data.SqlClient wordt gebruikt.
✅ Indien de maatwerkcode uitsluitend bedoeld is voor gebruik in Mercator Core .net 10.0, volstaat het om overal in deze code
System.Data.SqlClient te vervangen door Microsoft.Data.SqlClient
en opnieuw te compileren. De rest van deze pagina is dan niet van toepassing.
➡️ In het andere geval begint de aanpassing van maatwerkcode in het algemeen met het verwijderen van de clausule
using System.Data.SqlClient;
die bovenaan de code staat.
Indien deze clausule niet bestaat en indien de tekenreeks “System.Data.SqlClient” nergens in deze maatwerkcode voorkomt, dan hoeft er geen enkele aanpassing te gebeuren.
In het andere geval zal het verwijderen van deze clausule enkele compilatiefouten veroorzaken. Het zijn deze regels die moeten worden aangepast. Hieronder geven we enkele voorbeelden van uit te voeren wijzigingen.
using (SqlCommand cmd = new SqlCommand("insert into ..."))
{
Api.SqlExec(Globals.RepData, cmd);
}
wordt
using (MercatorSqlCommand cmd = new MercatorSqlCommand("insert into ..."))
{
Api.SqlExec(Globals.RepData, cmd);
}
👉 De klasse SqlCommand behoort tot de namespace System.Data.SqlClient en is dus niet langer bruikbaar. De klasse MercatorSqlCommand bevindt zich in MercatorApi. Haar parameters zijn identiek aan die van SqlCommand, maar ze is “agnostisch” ten opzichte van beide bibliotheken. De methode Api.SqlExec aanvaardt beide commandotypes zonder onderscheid.
using (SqlCommand cmd = new SqlCommand(reqSql))
{
cmd.Parameters.AddWithValue("@id", currentUserId).SqlDbType = SqlDbType.Char;
Api.SqlExec(MercatorUi.Globals.RepData, cmd);
}
wordt
Api.SqlExec(MercatorUi.Globals.RepData, reqSql, new MercatorSqlParam("@id", currentUserId, SqlDbType.Char));
👉 Hier wordt de code vereenvoudigd en is het niet langer nodig om SqlCommand te gebruiken.
using (MercatorSqlConnection conn = new MercatorSqlConnection(Globals.RepData, true))
{
if (conn.Connection == null)
return;
using (SqlTransaction transac = conn.Connection.BeginTransaction())
{
using (SqlCommand cmd = new SqlCommand("update ... delete ...", conn.Connection, transac))
{
if (!Api.SqlExec(cmd))
Api.SafeRollback(transac);
else
Api.SafeCommit(transac);
}
}
}
wordt
using (MercatorSqlConnection conn = new MercatorSqlConnection(Globals.RepData, true))
{
if (!conn.IsConnected)
return;
using (MercatorSqlCommand cmd = new MercatorSqlCommand("update ... delete ..."))
{
Api.SqlExec(conn, cmd, underTransaction: true);
}
}
👉 Hier wordt de code vereenvoudigd en is het niet langer nodig om SqlTransaction te gebruiken.
MercatorSqlParam mySqlParam = Api.SqlParamsGlobal.FindByParamName("@myParam");
if (mySqlParam == null)
Api.SqlParamsGlobal.Add(new MercatorSqlParam("@myParam", "xx", SqlDbType.Char));
else
mySqlParam.pSql.Value = "xx";
wordt
MercatorSqlParam mySqlParam = Api.SqlParamsGlobal.FindByParamName("@myParam");
if (mySqlParam == null)
Api.SqlParamsGlobal.Add(new MercatorSqlParam("@myParam", "xx", SqlDbType.Char));
else
mySqlParam.Value = "xx";
👉 Hier wordt eenvoudigweg .pSql verwijderd, aangezien dit afhankelijk is van System.Data.SqlClient of Microsoft.Data.SqlClient. De rechtstreekse accessor .Value geeft dezelfde waarde terug.
billingEngine.CommandForFinalTransaction = new System.Data.SqlClient.SqlCommand("...");
billingEngine.CommandForInitialTransaction = new System.Data.SqlClient.SqlCommand("...");
wordt
billingEngine.MercatorSqlCommandForFinalTransaction = new MercatorSqlCommand("...");
billingEngine.MercatorSqlCommandForInitialTransaction = new MercatorSqlCommand("...");
👉 Hier worden nieuwe eigenschappen van de BillingEngine gebruikt die noch van System.Data.SqlClient, noch van Microsoft.Data.SqlClient afhangen. Dit principe is van toepassing op alle engines van Mercator.
⚠️ MercatorSqlCommandForFinalTransaction is geen bijkomende command naast CommandForFinalTransaction. Het is enkel een accessor om CommandForFinalTransaction te vullen. Er mag dus nooit tegelijk een waarde aan beide eigenschappen worden toegekend. Hetzelfde principe geldt voor InitialTransaction.
private void BillingEngine_DuringSave(object sender, MercatorUi.Engine.Gescom.BillingEngine.DuringSaveEventArgs e)
{
MercatorUi.Engine.Gescom.BillingEngine billingEngine = (MercatorUi.Engine.Gescom.BillingEngine)sender;
e.SqlCommand.CommandText = "update ...";
e.SqlCommand.Parameters.AddWithValue("@param", "...");
}
wordt
private void BillingEngine_DuringSave(object sender, MercatorUi.Engine.Gescom.BillingEngine.DuringSaveEventArgs e)
{
MercatorUi.Engine.Gescom.BillingEngine billingEngine = (MercatorUi.Engine.Gescom.BillingEngine)sender;
e.MercatorSqlCommand.CommandText = "update ...";
e.MercatorSqlCommand.Parameters.AddWithValue("@param", "...");
}
👉 Hier worden nieuwe eigenschappen van de eventArgs gebruikt die noch van System.Data.SqlClient, noch van Microsoft.Data.SqlClient afhangen. Dit principe is eveneens van toepassing op alle engines van Mercator.
⚠️ MercatorSqlCommand is geen extra command naast SqlCommand. Het is louter een accessor om SqlCommand te vullen. Er mag dus nooit tegelijk een waarde in beide eigenschappen worden geplaatst.
public class Customizer : MercatorUi.ICustomizers.ISqlCommandUpdater
{
public void SqlCommandUpdate(SqlCommand sqlCommandToModify, Form form)
{
sqlCommandToModify.CommandText = "... \r\n"
+ sqlCommandToModify.CommandText;
}
}
wordt
public class Customizer : MercatorUi.ICustomizers.IMercatorSqlCommandUpdater
{
public void MercatorSqlCommandUpdate(MercatorSqlCommand sqlCommandToModify, Form form)
{
sqlCommandToModify.CommandText = "... \r\n"
+ sqlCommandToModify.CommandText;
}
}
👉 Hier wordt eenvoudigweg de interface ISqlCommandUpdater vervangen door IMercatorSqlCommandUpdater.
void billingEngine_BeginningSave(object sender, MercatorUi.Engine.Gescom.BillingEngine.BeginningSaveEventArgs e)
{
MercatorUi.Engine.Gescom.BillingEngine billingEngine = (MercatorUi.Engine.Gescom.BillingEngine)sender;
using (SqlCommand cmd = new SqlCommand("...", e.Connection, e.Transaction))
{
...
}
}
wordt
void billingEngine_BeginningSave(object sender, MercatorUi.Engine.Gescom.BillingEngine.BeginningSaveEventArgs e)
{
MercatorUi.Engine.Gescom.BillingEngine billingEngine = (MercatorUi.Engine.Gescom.BillingEngine)sender;
using (MercatorSqlCommand cmd = new MercatorSqlCommand("...", e.Connection, e.DbTransaction))
{
...
}
}
👉 Let op het gebruik van e.DbTransaction, dat van het type System.Data.Common.DbTransaction is. Dit is een bovenliggende klasse van zowel System.Data.SqlClient.Transaction als Microsoft.Data.SqlClient.Transaction.
Zie ook: Asynchrone toegang tot de SQL-database met de Mercator-API
Indien het bovenstaande niet volstaat, zal de geïntegreerde compiler van Mercator de clausule #if NET10_0_OR_GREATER herkennen om twee afzonderlijke assemblies te genereren. Bij uitvoering kiest Mercator automatisch de juiste assembly in functie van de gebruikte versie.
using System.Data.SqlClient;
wordt
#if NET10_0_OR_GREATER
using Microsoft.Data.SqlClient;
#else
using System.Data.SqlClient;
#endif
⚠️ Deze methode heeft de volgende beperking: de DLL’s MercatorTunnel.dll en MercatorUi.dll die als referentie worden meegegeven bij het compileren, zijn altijd die van de versie die op dat moment wordt uitgevoerd. Dit zal verhinderen dat code wordt gecompileerd die bijvoorbeeld eigenschappen gebruikt van klassen in MercatorUi die zelf verwijzen naar System.Data.SqlClient of Microsoft.Data.SqlClient.
Tot slot, wanneer het gaat om code die zich in een extern project bevindt en vanuit Visual Studio wordt gecompileerd, is het gebruik van de “agnostische” klassen, methodes en eigenschappen eveneens mogelijk. Een andere aanpak bestaat erin om twee assemblies te genereren door multiplatform-compilatie te activeren.
Daarvoor vervangt u in het project (dat vooraf naar het SDK-formaat werd gemigreerd)
<TargetFramework>net48</TargetFramework>
door
<TargetFrameworks>net48;net10.0-windows</TargetFrameworks>
Geef de referenties correct op voor elke versie.
<ItemGroup Condition=" '$(TargetFramework)' == 'net48' ">
<Reference Include="MercatorComponents">
<HintPath>C:\DossiersClientsRefAssemblies\fw40\MercatorComponents.dll</HintPath>
</Reference>
<Reference Include="MercatorTunnel">
<HintPath>C:\DossiersClientsRefAssemblies\fw40\MercatorTunnel.dll</HintPath>
</Reference>
<Reference Include="MercatorUi">
<HintPath>C:\DossiersClientsRefAssemblies\fw40\MercatorUi.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net10.0-windows' ">
<Reference Include="MercatorComponents">
<HintPath>C:\DossiersClientsRefAssemblies\fw100\MercatorComponents.dll</HintPath>
</Reference>
<Reference Include="MercatorTunnel">
<HintPath>C:\DossiersClientsRefAssemblies\fw100\MercatorTunnel.dll</HintPath>
</Reference>
<Reference Include="MercatorUi">
<HintPath>C:\DossiersClientsRefAssemblies\fw100\MercatorUi.dll</HintPath>
</Reference>
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.3" />
</ItemGroup>
⚠️ Pas de paden naar de DLL’s aan en verwijs steeds naar de DLL die voor het juiste framework werd gecompileerd. De versies van de Mercator-DLL’s zijn herkenbaar aan het laatste cijfer dat de frameworkversie aanduidt.
Tot slot vervangt u overal in de code
using System.Data.SqlClient;
par
#if NET10_0_OR_GREATER
using Microsoft.Data.SqlClient;
#else
using System.Data.SqlClient;
#endif
De compilatie zal twee assemblies opleveren die u kunt plaatsen in “Beheer / SQL-bestanden / Assemblies”. Bij het opstarten zal Mercator automatisch de juiste versie downloaden in functie van de gebruikte versie.
De onderstaande SQL-query maakt het mogelijk om de in de database opgeslagen C#-codes op te sommen die System.Data.SqlClient bevatten :
select id,code from ASSEMBLIES where code like '%System.Data.SqlClient%'