112

Using , how can I insert a C# List to database. Previously without dapper I used the below code to insert the List values to database.

try
{                
    connection.Open();

    for (int i = 0; i < processList.Count; i++)
    {
        string processQuery = "INSERT INTO PROCESS_LOGS VALUES (@Id, @st_Time, @ed_Time, @td_Time)";
        command = new SqlCommand(processQuery, connection);
        command.Parameters.Add("Id", SqlDbType.Int).Value = processList[i].ID;
        command.Parameters.Add("st_Time", SqlDbType.DateTime).Value = processList[i].ST_TIME;
        command.Parameters.Add("ed_Time", SqlDbType.DateTime).Value = processList[i].ED_TIME;
        command.Parameters.Add("td_Time", SqlDbType.DateTime2).Value = processList[i].TD_TIME;
        dataReader.Close();
        dataReader = command.ExecuteReader();
    }

    connection.Close();
}
catch (SqlException ex)
{
    //--Handle Exception
}

I'm familiar with fetching the data using dapper but this is my first try using insert query.

I tried the below code, using Exceute linked to query but stuck up with looping; I think using dapper tool, there is no need for looping statement.

connection.Execute(processQuery ... );

EDIT:

class ProcessLog
    {
        public int ID { get; set; }
        public DateTime ST_TIME { get; set; }
        public DateTime ED_TIME { get; set; }
        public DateTime TD_TIME { get; set; }
        public string frequency { get; set; }
    }

Please advice on this. FYI: I'm using SQL Server 2008.

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Praveen
  • 55,303
  • 33
  • 133
  • 164

3 Answers3

191

You'd have to do it a little differently. In Dapper, it matches on convention AKA property or field names being identical to SQL parameters. So, assuming you had a MyObject:

public class MyObject
{
    public int A { get; set; }

    public string B { get; set; }
}

And assuming processList = List<MyObject>, You'd want to do this

foreach (var item in processList)
{
    string processQuery = "INSERT INTO PROCESS_LOGS VALUES (@A, @B)";        
    connection.Execute(processQuery, item);
}

Note that the MyObject property names A and B match the SQL parameter names @A and @B.

If you don't want to rename objects, you can use anonymous types to do the mappings instead of concrete types:

foreach (var item in processList)
{
    string processQuery = "INSERT INTO PROCESS_LOGS VALUES (@A, @B)";        
    connection.Execute(processQuery, new { A = item.A, B = item.B });
}

EDIT:

Per Marc Gravell's comment, you can also have Dapper do the loop for you:

string processQuery = "INSERT INTO PROCESS_LOGS VALUES (@A, @B)";        
connection.Execute(processQuery, processList);
Benjamin Hodgson
  • 42,952
  • 15
  • 108
  • 157
Haney
  • 32,775
  • 8
  • 59
  • 68
  • 191
    Actually, little known fact: dapper will iterate *for you*; the middle one can be: `connection.Execute("INSERT INTO PROCESS_LOGS VALUES (@A, @B)", processList);` – Marc Gravell Jun 17 '13 at 15:10
  • @MarcGravell - awesome, didn't know that. We built a fancy wrapper over Dapper at my job (which we internally call the DapperWrapper, haha) and it offers CRUD operations. We didn't have a need for bulk inserts/updates/upserts so we didn't map that method over. Good stuff! – Haney Jun 17 '13 at 15:15
  • 9
    My god, Marc you never stop to amaze me. Now I have to go and re-check all the code I've written in the past 10 days. (Since I decided to try this little gem) – Steve Jun 17 '13 at 15:15
  • @user1671639 - you must loop to insert multiple values, unless you build a VERY fancy SPROC that would be quite convoluted. See Marc's comment on how to have Dapper do the loop for you. – Haney Jun 17 '13 at 15:16
  • @DavidH I'm facing an exception `Must declare the scalar variable "@Id"` should this be the same name given in the database? – Praveen Jun 17 '13 at 15:32
  • @user1671639 can you paste your processQuery? – Haney Jun 17 '13 at 15:36
  • @DavidH `string processQuery = "INSERT INTO PROCESS_LOGS VALUES (@Id, @st_Time, @ed_Time, @td_Time)";` you can refer the question above. – Praveen Jun 17 '13 at 15:38
  • Your object that you passed to Dapper did not have a field or property named "Id" – Haney Jun 17 '13 at 15:41
  • @DavidH Am I missing parameter? – Praveen Jun 17 '13 at 15:43
  • Yes, you're saying "i am declaring @Id as a parameter that I will set" but you're not supplying a matching "Id" property or field on your object that you're passing to Dapper. – Haney Jun 17 '13 at 15:49
  • 1
    @DavidH This working good, using your first approach. I'm trying to get it as Marc Gravell suggested. Something wrong in my code, I will try to fix it and let you know my mistakes Thanks. – Praveen Jun 17 '13 at 15:57
  • @MarcGravell Do we need to open the database connection when using `Execute`(ie., when I tried to use insert query, I faced an exception `"ExecuteNonQuery requires an open and available Connection. The connection's current state is closed."`) – Praveen Jun 18 '13 at 10:05
  • @user1671639 that depends: which library version are you using? it is a feature in newer builds – Marc Gravell Jun 18 '13 at 10:12
  • @MarcGravell I'm using `Dapper NET40`. – Praveen Jun 18 '13 at 10:14
  • @MarcGravell **FYI:** Also for my `select` statement I'm not opening any connection. – Praveen Jun 18 '13 at 10:21
  • 10
    Is there any way to simulate `(@a1, @b1),(@a2, @b2)` with Dapper? I'm trying to use SQL Server `MERGE tble USING VALUES (('a1', 'b1'),('a2','b2')) AS foo(a,b) ON tble.a=foo.a WHEN MATCHED...`. If not I'd rather iterate and run many statements than use a temp table, but Dapper creating my bracketed list would be very nice. Am doubtful as I think it is vendor specific. Although MySQL uses comma-delimited brackets to do multiple row inserts so perhaps it isn't? (`INS INTO tbl (a,b) VALUES (a1,b1),(a2,b2)`). – Chris Oct 20 '14 at 11:32
  • @MarcGravell Is it possible to do this using one static value and a list: `con.Execute("INSERT INTO Many2Many VALUES (@A, @B)", new { A = parentId, B = listOfChildren) });`? If not, will the param accept a Tuple? – Mackan Sep 24 '16 at 11:20
  • 3
    @Mackan dapper won't, but LINQ: use `listOfChildren.Select(childId => new { A = parentId, B = childId})` as the arg – Marc Gravell Sep 24 '16 at 11:42
  • @MarcGravell, How would one return all the inserted ids using this format? FYI here's something that does not work! http://stackoverflow.com/questions/39884768/why-is-dapper-unable-to-retun-multiple-inserted-row-ids – MaYaN Oct 05 '16 at 23:05
  • 1
    @Haney is it not a bit dangerous to do inserts without specifying the columns as well? What if the table schema changes? I had negative experience by using this model in the past. The table fields moved around (order), new were added, etc., and my insert would not work any longer. – Andrei Bazanov Oct 02 '19 at 10:52
  • 1
    @AndreiBazanov yeah it definitely is dangerous. Best practice I'd say is to include the column names. This is an older answer - I have changed my ways since. ;) – Haney Oct 02 '19 at 14:19
  • 1
    Is this not a REALLY bad idea performance wise? n round trips to SQL server? Just adding all the inserts to 1 string (somehow) then sending that to SQL Server would be far better surely. – niico Mar 05 '21 at 09:15
  • Dumb question: What if my ID field in my object is auto-incremented, and I don't want to specify it? Is there a way to exclude it? – Vin Shahrdar Apr 21 '21 at 23:44
  • @VinShahrdar yup, make sure the object you're inserting doesn't have a property matching the ID. – Haney Apr 23 '21 at 01:43
  • 1
    Anyone know if this does n round trips / execute to SQL server or does it neatly create 1 insert statement with multiple inserts? Surely the former will be very inefficient, specially if it's a long list?! – niico Jun 05 '22 at 13:03
  • @MarcGravell In your example, if one of the items throws an error in the middle of inserting, what happens? previous items are inserted? – Farhad Zamani Oct 22 '22 at 11:55
  • 1
    @FarhadZamani yes, but that can be influenced by using a transaction, and only committing at the end – Marc Gravell Oct 22 '22 at 14:24
26

I believe the bulk insert is better than iterate the list and insert one by one.

SqlTransaction trans = connection.BeginTransaction();

connection.Execute(@"
insert PROCESS_LOGS(Id, st_Time, ed_Time, td_Time)
values(@Id, @st_Time, @ed_Time, @td_Time)", processList, transaction: trans);

trans.Commit();

Reference: https://stackoverflow.com/a/12609410/1136277

Community
  • 1
  • 1
Thalles Noce
  • 791
  • 1
  • 9
  • 20
  • 5
    I don't believe that's a bulk insert. Individual inserts are done. See the comments in the link you provided. – Bob Horn Aug 07 '20 at 19:23
3

Use Dapper.Contrib https://dapper-tutorial.net/insert#example---insert-single ! You can effortlessly insert list of objects in db.