2

I found that question that is exactly what I am trying to do. The correct answer seems to do exactly what I want, but I would like to make something not to type all parameters again.

I have a <Repere>, with a function I already implemented for a simple parameterized query :

public MySqlParameter[] GetListMySqlParams()
{
    return this.getListMySqlParams();
}
private MySqlParameter[] getListMySqlParams()
{
    MySqlParameter[] listParams = new MySqlParameter[]
    {
        new MySqlParameter("idContract", this.contrat.ID),
        new MySqlParameter("contractCode", this.Contrat.Code),
        new MySqlParameter("phaseName", this.fase.Name),
        new MySqlParameter("assemblyName", this.assembly.Name),
        new MySqlParameter("idPhase", this.fase.ID),
        new MySqlParameter("idAss", this.assembly.ID),
        new MySqlParameter("name", this.name),
        new MySqlParameter("priority", this.priority),
        new MySqlParameter("quantity", this.quantity),
        new MySqlParameter("totalQuantity", this.totalQuantity),
        new MySqlParameter("listOperations", this.listOperations.ConvertToString()),
        new MySqlParameter("material", this.geometry.Material),
        new MySqlParameter("drawing", this.geometry.Drawing),
        new MySqlParameter("profile", this.geometry.Profile.Name),
        new MySqlParameter("groupeProfil", this.geometry.GroupeProfil),
        new MySqlParameter("length", this.geometry.Length),
        new MySqlParameter("weightNet", this.geometry.WeightNet),
        new MySqlParameter("revision", this.geometry.Revision),
        new MySqlParameter("principal", this.principal),
        new MySqlParameter("unloadingZone", this.unloadingZone),
        new MySqlParameter("executionClass", this.executionClass),
        new MySqlParameter("category", this.category.ID),
        new MySqlParameter("description", this.description),
        new MySqlParameter("workingOrder", this.workingOrder),
        new MySqlParameter("isMountingPart", this.isMountingPart),
        new MySqlParameter("isPRS", this.isPRS),
        new MySqlParameter("idPRS", this.idPRS),
        new MySqlParameter("importOk",this.geometry.ImportOk),
    };
    return listParams;
}

What I would like is using the solution there, but not having to type again all parameters (as I would like to do it on several objects, and lot of parameters).

Here is what I have for now :

private void insertListReperes(List<Repere> listReperes)
{
    if (this.OpenConnection() == true)
    {
        using (this.connection)
        {
            string query = "INSERT INTO [vsteel].detail (ID_CONTRACT,NAME_CONTRACT,NAME_PHASE,NAME_ASS,ID_PHASE,ID_ASS,NAME,NAME_ORI,PRIORITY,QTE,QTE_TOT,OP," +
            "MATERIAL,DRAWING,PROFILE,GROUP_PROFILE,LENGTH,WEIGHT,REVISION,PRINCIPAL,UNLOADING_ZONE,EXECUTION_CLASS,ID_CATEGORY,DESCRIPTION,WORKING_ORDER," +
            "IS_MOUNTING_PART,IS_PRS,ID_PRS,IMPORT_OK) " +
            "VALUES (@idContract,@contractCode,@phaseName,@assemblyName,@idPhase,@idAss,@name,@name,@priority,@quantity,@totalQuantity,@listOperations," +
            "@material,@drawing,@profile,@groupeProfil,@length,@weightNet,@revision,@principal,@unloadingZone,@executionClass,@category,@description,@workingOrder," +
            "@isMountingPart,@isPRS,@idPRS,@importOk)";
            using (MySqlCommand cmd = new MySqlCommand(query.Replace("[vsteel].", ""), connection))
            {
                for(int i=0;i<listReperes.Count();i++)
                {
                    Repere repere = listReperes[i];
                    if(i==0)
                    {
                        MySqlParameter[] listParams = repere.GetListMySqlParams();
                        for (int j = 0; j < listParams.Count(); j++)
                        {
                            cmd.Parameters.Add(listParams[i]);
                        }
                        cmd.Prepare();
                        cmd.ExecuteNonQuery();
                    }
                    else
                    {
                        MySqlParameter[] listParams = repere.GetListMySqlParams();
                        for (int j = 0; j < listParams.Count(); j++)
                        {
                            cmd.Parameters[listParams[i].ParameterName].Value = listParams[i].Value;
                        }
                        cmd.ExecuteNonQuery();
                    }
                    repere.ID = cmd.LastInsertedId;
                }
            }
        }
    }
}

But when I try to set parameter at line cmd.Parameters[listParams[i].ParameterName].Value = listParams[i].Value; it gives me the error that parameter is already defined. Is there a way to do it without copying all parameters names?

Siegfried.V
  • 1,508
  • 1
  • 16
  • 34
  • How about just `Clear()`ing the parameter collection and then adding all your new ones with `AddRange(listParams)` ? – Caius Jard Dec 15 '21 at 18:36
  • Incidentally, if you used Dapper your entire `insertListReperes` code would reduce to `connection.Execute(query, repere)` plus the lines that set up the query and the connection - you wouldnt even need the `getListMySqlParams` method; Dapper will do that bit - just feed it an SQL full of `@parameterNames` that match your property names (you seem to have this) and pass it a `Repere` instance in the parameters argument – Caius Jard Dec 15 '21 at 18:37
  • I recommend not using case alone to distinguish method names. Also recommend not using camelCase for properties; use PascalCase please – Caius Jard Dec 15 '21 at 18:39
  • @CaiusJard CamelCase : `A`, PascalCase : `a`, right? Why not to use them? Is this kind of "rule"?(I am new to parameterized queries) then about your first comment, if I did everytime Clear(), then AddRange(listParams), I would need to prepare everytime, right? on the previous post it is said that avoiding preparing everytime hires some time (my problem is actually this, that's why I try to make it multi – Siegfried.V Dec 15 '21 at 18:43
  • @CaiusJard about not using case alone, same question please, I saw that on tutorials 5 years ago, so I always used that. I am not doubting you are right of course, but try to understandthe reason? – Siegfried.V Dec 15 '21 at 18:44
  • 1
    *I would need to prepare everytime* - I don't imagine so - https://dev.mysql.com/doc/connector-net/en/connector-net-programming-prepared.html creates parameters after prepare is called – Caius Jard Dec 15 '21 at 19:10
  • @CaiusJard ok, will try right now, thanks – Siegfried.V Dec 15 '21 at 19:13
  • 1
    PascalCaseStartsWithAnUpper, whereas camelCaseStartsWithALower - in general we name public things in C# with PascalCase, and local or private things (class fields) with camelCase.. As to case differentiation alone; it's not very obvious, and some languages in the CLR don't support case sens. Consuming a C# DLL with two pub methods differing on case alone would cause issues for VB, for example.. I appreciate that your methods are private, so it's only you who will be confused by them, but a "case alone" method might well be a stack overflow bug waiting to happen. – Caius Jard Dec 15 '21 at 19:14
  • What version of (and vendor of) MySql data access libs are you using, by the way... There are a few, and I suppose I've just assumed it's the official mysql-connector.net; I can't see any reason why setting the value would cause the "already added" – Caius Jard Dec 15 '21 at 19:15
  • @CaiusJard I use MariaDB 10.3.14 (then vendor, I don't understand what it is) – Siegfried.V Dec 15 '21 at 19:17
  • Where did you get the library that lets you connect to MySQL? What did you install that means writing `var x = new MySqlCommand` *doesn't* show a "are you missing an assemly reference?" error? Answers might be like "I opened nuget package manager, searched MySQL and installed MySql.Data 8.0.27 by oracle" – Caius Jard Dec 15 '21 at 19:18
  • @CaiusJard if I understand the question good, I installed wampserver. At beginning I used MySQL, but customer asked me to use MariaDB, cause he would need to buy a license. transfering DB from MySQL to MariaDB was very easy so I accepted. Why the question? – Siegfried.V Dec 15 '21 at 19:21
  • The complaint is that "doing X causes this error when I use some software provided by a third party", when it seems it shouldn't error.. It helps to know what software is in use - maybe it;s a bug in that lib. If the lib source code is available it can help chase it down, but of course we need to know what it *is* – Caius Jard Dec 15 '21 at 19:23
  • @CaiusJard MySql.Data.dll v4.0.30319 – Siegfried.V Dec 15 '21 at 19:29
  • If you right click on e.g. `MySqlCommand` and choose "go to definition" do you see at the top of the window something like "#region Assembly MySql.Data, Version=8.0.27.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" - what is the version? (the 4.0 from before is the .net runtime version, not the lib version) – Caius Jard Dec 15 '21 at 19:35
  • @CaiusJard regarding your comment to prepare first, then Clear() / AddRange(listParameters), it worked fine. If you want to put as an answer to close? Then I look now about MySqlCommand – Siegfried.V Dec 15 '21 at 19:36
  • Assembly MySql.Data, Version=8.0.21.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d (and thanks, didn't know about that) – Siegfried.V Dec 15 '21 at 19:38

1 Answers1

1

So, I found the problem. The error isn't being thrown on the line you said it was; it's on the cmd.Parameters.Add line, which makes more sense (the source code only ever throws a a"already existing" during an Add)

You've written:

cmd.Parameters.Add(listParams[i]);

You should have written:

cmd.Parameters.Add(listParams[j]);

By using [i] you repeatedly add the parameter for idContact because i is always 0 on every pass of the j loop


Note that you've made the same mistake on the lower line too, so you repeatedly set the value for idContact 21 times


By the way, I mentioned a library called Dapper (stackoverflow uses it for all its data access) that can make life simpler for you. If you write a code like:

using var connection = ... //get your connection here
var sql = "INSERT ... VALUES(@parameter, @names, @identical, @to, @your, @object, @properties); SELECT LAST_INSERT_IDENTITY()";
foreach(var v in itemstoinsert)
  v.id = connection.ExecuteScalar<int>(sql, v);

It should be all you need..

Caius Jard
  • 72,509
  • 5
  • 49
  • 80
  • yeah, you're right, sorry for mistake. Then I finally decided to do as you suggested (clear(),AddRange() everytime. the code is clearer than mine, so except if it would make earn time, I think it is better. Thanks – Siegfried.V Dec 16 '21 at 09:53
  • No problem. I also just added a bit about Dapper and how it can make life a bit easier! – Caius Jard Dec 16 '21 at 10:46