Performing Inserts and Updates with Dapper
We are looking at building a few helpers, still deciding on APIs and if this goes in core or not. See: https://code.google.com/archive/p/dapper-dot-net/issues/6 for progress.
In the mean time you can do the following
val = "my value";
cnn.Execute("insert into Table(val) values (@val)", new {val});
cnn.Execute("update Table set val = @val where Id = @id", new {val, id = 1});
etcetera
See also my blog post: That annoying INSERT problem
Update
As pointed out in the comments, there are now several extensions available in the Dapper.Contrib project in the form of these IDbConnection
extension methods:
T Get<T>(id);
IEnumerable<T> GetAll<T>();
int Insert<T>(T obj);
int Insert<T>(Enumerable<T> list);
bool Update<T>(T obj);
bool Update<T>(Enumerable<T> list);
bool Delete<T>(T obj);
bool Delete<T>(Enumerable<T> list);
bool DeleteAll<T>();
Dapper's parameterized Update and Insert?
Thanks to Marc Gravell. I found it here.
Dapper's open source development do have implementation for Insert<ClassName>(obj)
and Update<ClassName>(obj)
.
Can Dapper be used to update and insert models?
You can use the SqlMapperExtensions
class from Dapper.Contrib.Extensions
:
using Dapper;
using Dapper.Contrib.Extensions;
// don't forget the using since Update is an extension method
Dog entity; // you got it from somewhere
entity.Name = "fancy new dog name";
connection.Update(entity);
In order for this to work you need to add the [Key] annotation to your id.
Should look something like this:
using System.ComponentModel.DataAnnotations;
public class Dog
{
[Key]
public long Id { get; set; }
public int? Age { get; set; }
public string Name { get; set; }
public float? Weight { get; set; }
}
Can I Use Query Method in Dapper To Insert And Update Rows?
First, you need to use the OUTPUT
clause in order to return rows from an insert statement.
Second, QueryAsync
will return an IEnumerable<Hotel>
and not a Hotel
, so you won't be able to cast it.
Put together, it would look like this
public async Task<Hotel> CreateHotel(Hotel hotel)
{
var sql = "INSERT INTO Hotels" +
" (name, city)" +
" OUTPUT inserted.name, inserted.city" +
" VALUES (@name, @city)";
var newHotel = new Hotel()
{
Name = hotel.Name,
City = hotel.City
};
using (var connection = new SqlConnection(CONNECTION_STRING))
{
return (await connection.QueryAsync<Hotel>(sql, newHotel)).SingleOrDefault();
}
}
Can Dapper be used to update and insert models?
You can use the SqlMapperExtensions
class from Dapper.Contrib.Extensions
:
using Dapper;
using Dapper.Contrib.Extensions;
// don't forget the using since Update is an extension method
Dog entity; // you got it from somewhere
entity.Name = "fancy new dog name";
connection.Update(entity);
In order for this to work you need to add the [Key] annotation to your id.
Should look something like this:
using System.ComponentModel.DataAnnotations;
public class Dog
{
[Key]
public long Id { get; set; }
public int? Age { get; set; }
public string Name { get; set; }
public float? Weight { get; set; }
}
Is it possible to run an Insert and Get by dapper simultaneously?
UPDATE
doesn't return a grid (unless you are using the OUTPUT
clause. You can do as many operations as you like in a SQL operation - all that Dapper cares about is the result grids, so yes, you can perform an update
and (separately) a select
. The only significance of Execute
over Query
is that Execute
expects zero grids. If you're returning a single grid, then Query
; if you're returning multiple grids: QueryMultiple
. Dapper has no interest in what happens inside the query - it is entirely opaque - all it sees is result grids.
I highly recommend using the formal operation separator (;
), though. For example:
const string sql = @"
UPDATE likes AS L
SET L.isLike = @isLike
WHERE L.ItemId = @ItemId AND L.Ip = @Ip;
SELECT COUNT(*) FROM likes AS I WHERE I.ItemId = @ItemId AND L.isLike=1;
SELECT COUNT(*) FROM likes AS I WHERE I.ItemId = @ItemId AND L.isLike=0;
"
using (var data = await dbConnection.QueryMultipleAsync(sql,
new { ItemId, isLike, Ip }))
{
numberOfLikes = data.ReadSingle<int>();
numberOfDislikes = data.ReadSingle<int>();
}
although to be honest I might be tempted to do, instead, a single final select
, using Dapper's positional treatment of value-tuples:
const string sql = @"
UPDATE likes AS L
SET L.isLike = @isLike
WHERE L.ItemId = @ItemId AND L.Ip = @Ip;
SELECT
(SELECT COUNT(*) FROM likes AS I WHERE I.ItemId = @ItemId AND L.isLike=1),
(SELECT COUNT(*) FROM likes AS I WHERE I.ItemId = @ItemId AND L.isLike=0);
"
(var numberOfLikes, var numberOfDislikes) =
await dbConnection.QuerySingleAsync<(int,int)>(sql,
new { ItemId, isLike, Ip });
or something involving group by L.isLike
Dapper many-to-many insert
I believe the code snippet below will work for you.
You should have 3 insert statements, for Product, Attachment and AttachmentProductSpecificationLink. In your sample code you are missing one. I made the assumption, that your aINSERT should be for the Attachment table.
Another thing I noticed, Your ExecuteScalarAsync was returning an object, so I used ExecuteScalarAsync<int> instead to cast correctly.
Your entity ( Product class) variable contains a List of Attachments, Normally I would perform an ExecuteAsync and pass in the List because it is plain Inserts with no returning Clause. But in your case you are returning the Ids so I opted to use a foreach loop to iterate over the attachments for the inserts.
Within the foreach loop I return the returning ID into the variable attachmentId and use it for the following insert into AttachmentProductSpecificationLink. Since we aren't return anything I perform an ExecuteAsync.
using (var connection = GetConnection())
{
connection.Open();
using (var transaction = connection.BeginTransaction())
{
string pINSERT = "INSERT INTO prodcat.Product (otherpropertiesremovedforbrevity) " +
" VALUES (@otherpropertiesremovedforbrevity) RETURNING Id;";
string aINSERT = "INSERT INTO prodcat.Attachment (Name, URL) " +
" VALUES (@Name, @Url) RETURNING Id;";
string sql = "insert into prodcat.AttachmentProductSpecificationLink (ProductSpecificationId, AttachmentId) values(@Id, @AttachmentId)";
var res = await connection.ExecuteScalarAsync<int>(pINSERT, entity, transaction);
foreach( var a in entity.Attachment)
{
var attachmentId = await connection.ExecuteScalarAsync<int>(aINSERT, a, transaction);
var arows = await connection.ExecuteAsync(sql, new { Id = res, AttachmentId = attachmentId }, transaction);
}
transaction.Commit();
return Convert.ToInt64(res);
}
}
Dapper Contrib Output Parameter
You can try to use DynamicParameters
be the parameter that can declare ParameterDirection.Output
for the parameter which you want to be output, then we can use Get<int>
to get the output value.
var params = new DynamicParameters();
params.Add("@SkipCount",skip);
params.Add("@PageSize",pageSize);
params.Add("@TotalRows", dbType: DbType.Int32, direction: ParameterDirection.Output);
db.Query<Models.Product>("spCustomPaging", params, commandType: System.Data.CommandType.StoredProcedure);
var output = params.Get<int>("@TotalRows");
Related Topics
Lambda Variable Capture in Loop - What Happens Here
Jcontainer, Jobject, Jtoken and Linq Confusion
Check If Property Has Attribute
How to Insert a Data Table into SQL Server Database Table
Does a Locked Object Stay Locked If an Exception Occurs Inside It
Adding Unknown (At Design Time) Properties to an Expandoobject
Multi-Select Dropdown List in ASP.NET
Is Nameof() Evaluated at Compile-Time
In Wpf Can You Filter a Collectionviewsource Without Code Behind
In Mvvmcross How to Do Custom Bind Properties
Wpf Mvvm Why Use Contentcontrol + Datatemplate Views Rather Than Straight Xaml Window Views
ASP.NET Webapi2 Enable Cors Not Working with Aspnet.Webapi.Cors 5.2.3
Mvcbuildviews Not Working Correctly
How to Forcefully Propagate Role Changes to Users with ASP.NET Identity 2.0.1