Skip to content
This repository was archived by the owner on Dec 24, 2022. It is now read-only.

Commit e61371e

Browse files
authored
Enhance Insert/InsertAsync, and support manual-specified value of auto-incrementing primary key of generic type. (#658)
* add ToString format paramater support for MySql DateTime * Add tests for DateTime.ToString() format parameter in Mysql * Improve the implementation of FloatConverter to fix the conversion failure problem in some cases. * Enhance Insert/InsertAsync, and support manual-specified value of auto-incrementing primary key of generic type. * Adjust ModelDefinition<T>.Definition.IsPrimaryKey To ModelDefinition<T>.Definition.FieldDefinitions.FirstOrDefault(f => f.IsPrimaryKey) to avoid System.InvalidOperationException on models without a primary key field.
1 parent 7444a49 commit e61371e

5 files changed

Lines changed: 114 additions & 19 deletions

File tree

src/ServiceStack.OrmLite/Async/OrmLiteWriteCommandExtensionsAsync.cs

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -308,17 +308,41 @@ internal static Task<int> DeleteAsync(this IDbCommand dbCmd, Type tableType, str
308308
return dbCmd.ExecuteSqlAsync(dbCmd.GetDialectProvider().ToDeleteStatement(tableType, sql), token);
309309
}
310310

311-
internal static async Task<long> InsertAsync<T>(this IDbCommand dbCmd, T obj, Action<IDbCommand> commandFilter, bool selectIdentity, CancellationToken token)
311+
internal static async Task<long> InsertAsync<T>(this IDbCommand dbCmd, T obj, Action<IDbCommand> commandFilter, bool selectIdentity, bool enableIdentityInsert,CancellationToken token)
312312
{
313313
OrmLiteUtils.AssertNotAnonType<T>();
314-
315314
OrmLiteConfig.InsertFilter?.Invoke(dbCmd, obj);
316315

317316
var dialectProvider = dbCmd.GetDialectProvider();
318-
dialectProvider.PrepareParameterizedInsertStatement<T>(dbCmd,
319-
insertFields: dialectProvider.GetNonDefaultValueInsertFields<T>(obj));
317+
var pkField = ModelDefinition<T>.Definition.FieldDefinitions.FirstOrDefault(f => f.IsPrimaryKey);
318+
if (!enableIdentityInsert || pkField == null || !pkField.AutoIncrement)
319+
{
320+
dialectProvider.PrepareParameterizedInsertStatement<T>(dbCmd,
321+
insertFields: dialectProvider.GetNonDefaultValueInsertFields<T>(obj));
322+
return await InsertInternalAsync<T>(dialectProvider, dbCmd, obj, commandFilter, selectIdentity, token).ConfigAwait();
323+
}
324+
else
325+
{
326+
try
327+
{
328+
await dialectProvider.EnableIdentityInsertAsync<T>(dbCmd, token);
329+
dialectProvider.PrepareParameterizedInsertStatement<T>(dbCmd,
330+
insertFields: dialectProvider.GetNonDefaultValueInsertFields<T>(obj),
331+
shouldInclude: f => f == pkField);
332+
await InsertInternalAsync<T>(dialectProvider, dbCmd, obj, commandFilter, selectIdentity, token).ConfigAwait();
333+
if (selectIdentity)
334+
{
335+
var id = pkField.GetValue(obj);
336+
return Convert.ToInt64(id);
337+
}
320338

321-
return await InsertInternalAsync<T>(dialectProvider, dbCmd, obj, commandFilter, selectIdentity, token).ConfigAwait();
339+
return default;
340+
}
341+
finally
342+
{
343+
await dialectProvider.DisableIdentityInsertAsync<T>(dbCmd, token);
344+
}
345+
}
322346
}
323347

324348
internal static async Task<long> InsertAsync<T>(this IDbCommand dbCmd, Dictionary<string,object> obj, Action<IDbCommand> commandFilter, bool selectIdentity, CancellationToken token)
@@ -464,14 +488,14 @@ internal static async Task<bool> SaveAsync<T>(this IDbCommand dbCmd, T obj, Canc
464488
if (modelDef.HasAutoIncrementId)
465489
{
466490

467-
var newId = await dbCmd.InsertAsync(obj, commandFilter: null, selectIdentity: true, token:token).ConfigAwait();
491+
var newId = await dbCmd.InsertAsync(obj, commandFilter: null, selectIdentity: true, enableIdentityInsert:false, token:token).ConfigAwait();
468492
var safeId = dbCmd.GetDialectProvider().FromDbValue(newId, modelDef.PrimaryKey.FieldType);
469493
modelDef.PrimaryKey.SetValue(obj, safeId);
470494
id = newId;
471495
}
472496
else
473497
{
474-
await dbCmd.InsertAsync(obj, commandFilter:null, selectIdentity:false, token:token).ConfigAwait();
498+
await dbCmd.InsertAsync(obj, commandFilter:null, selectIdentity:false, enableIdentityInsert: false, token: token).ConfigAwait();
475499
}
476500

477501
modelDef.RowVersion?.SetValue(obj, await dbCmd.GetRowVersionAsync(modelDef, id, token).ConfigAwait());
@@ -528,14 +552,14 @@ internal static async Task<int> SaveAllAsync<T>(this IDbCommand dbCmd, IEnumerab
528552
{
529553
if (modelDef.HasAutoIncrementId)
530554
{
531-
var newId = await dbCmd.InsertAsync(row, commandFilter:null, selectIdentity:true, token:token).ConfigAwait();
555+
var newId = await dbCmd.InsertAsync(row, commandFilter:null, selectIdentity:true, enableIdentityInsert: false, token: token).ConfigAwait();
532556
var safeId = dialectProvider.FromDbValue(newId, modelDef.PrimaryKey.FieldType);
533557
modelDef.PrimaryKey.SetValue(row, safeId);
534558
id = newId;
535559
}
536560
else
537561
{
538-
await dbCmd.InsertAsync(row, commandFilter:null, selectIdentity:false, token:token).ConfigAwait();
562+
await dbCmd.InsertAsync(row, commandFilter:null, selectIdentity:false, enableIdentityInsert: false, token:token).ConfigAwait();
539563
}
540564

541565
rowsAdded++;

src/ServiceStack.OrmLite/OrmLiteWriteApi.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,9 @@ public static int ExecuteSql(this IDbConnection dbConn, string sql, Dictionary<s
6767
/// Insert 1 POCO, use selectIdentity to retrieve the last insert AutoIncrement id (if any). E.g:
6868
/// <para>var id = db.Insert(new Person { Id = 1, FirstName = "Jimi }, selectIdentity:true)</para>
6969
/// </summary>
70-
public static long Insert<T>(this IDbConnection dbConn, T obj, bool selectIdentity = false)
70+
public static long Insert<T>(this IDbConnection dbConn, T obj, bool selectIdentity = false,bool enableIdentityInsert = false)
7171
{
72-
return dbConn.Exec(dbCmd => dbCmd.Insert(obj, commandFilter: null, selectIdentity: selectIdentity));
72+
return dbConn.Exec(dbCmd => dbCmd.Insert(obj, commandFilter: null, selectIdentity: selectIdentity, enableIdentityInsert));
7373
}
7474

7575
/// <summary>

src/ServiceStack.OrmLite/OrmLiteWriteApiAsync.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,19 @@ public static Task<int> ExecuteSqlAsync(this IDbConnection dbConn, string sql, o
3838
/// Insert 1 POCO, use selectIdentity to retrieve the last insert AutoIncrement id (if any). E.g:
3939
/// <para>var id = db.Insert(new Person { Id = 1, FirstName = "Jimi }, selectIdentity:true)</para>
4040
/// </summary>
41-
public static Task<long> InsertAsync<T>(this IDbConnection dbConn, T obj, bool selectIdentity = false, CancellationToken token = default)
41+
public static Task<long> InsertAsync<T>(this IDbConnection dbConn, T obj, bool selectIdentity = false,
42+
bool enableIdentityInsert =false,CancellationToken token = default)
4243
{
43-
return dbConn.Exec(dbCmd => dbCmd.InsertAsync(obj, commandFilter: null, selectIdentity: selectIdentity, token: token));
44+
return dbConn.Exec(dbCmd => dbCmd.InsertAsync(obj, commandFilter: null, selectIdentity: selectIdentity, enableIdentityInsert,token: token));
4445
}
4546

4647
/// <summary>
4748
/// Insert 1 POCO, use selectIdentity to retrieve the last insert AutoIncrement id (if any). E.g:
4849
/// <para>var id = db.Insert(new Person { Id = 1, FirstName = "Jimi }, selectIdentity:true)</para>
4950
/// </summary>
50-
public static Task<long> InsertAsync<T>(this IDbConnection dbConn, T obj, Action<IDbCommand> commandFilter, bool selectIdentity = false, CancellationToken token = default)
51+
public static Task<long> InsertAsync<T>(this IDbConnection dbConn, T obj, Action<IDbCommand> commandFilter, bool selectIdentity = false, bool enableIdentityInsert = false, CancellationToken token = default)
5152
{
52-
return dbConn.Exec(dbCmd => dbCmd.InsertAsync(obj, commandFilter: commandFilter, selectIdentity: selectIdentity, token: token));
53+
return dbConn.Exec(dbCmd => dbCmd.InsertAsync(obj, commandFilter: commandFilter, selectIdentity: selectIdentity, enableIdentityInsert, token: token));
5354
}
5455

5556
/// <summary>

src/ServiceStack.OrmLite/OrmLiteWriteCommandExtensions.cs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -711,17 +711,42 @@ internal static int Delete(this IDbCommand dbCmd, Type tableType, string sql, ob
711711
return dbCmd.ExecuteSql(dbCmd.GetDialectProvider().ToDeleteStatement(tableType, sql));
712712
}
713713

714-
internal static long Insert<T>(this IDbCommand dbCmd, T obj, Action<IDbCommand> commandFilter, bool selectIdentity = false)
714+
internal static long Insert<T>(this IDbCommand dbCmd, T obj, Action<IDbCommand> commandFilter, bool selectIdentity = false, bool enableIdentityInsert=false)
715715
{
716716
OrmLiteUtils.AssertNotAnonType<T>();
717717

718718
OrmLiteConfig.InsertFilter?.Invoke(dbCmd, obj);
719719

720720
var dialectProvider = dbCmd.GetDialectProvider();
721-
dialectProvider.PrepareParameterizedInsertStatement<T>(dbCmd,
722-
insertFields: dialectProvider.GetNonDefaultValueInsertFields<T>(obj));
721+
var pkField = ModelDefinition<T>.Definition.FieldDefinitions.FirstOrDefault(f => f.IsPrimaryKey);
722+
if (!enableIdentityInsert || pkField == null || !pkField.AutoIncrement)
723+
{
724+
dialectProvider.PrepareParameterizedInsertStatement<T>(dbCmd,
725+
insertFields: dialectProvider.GetNonDefaultValueInsertFields<T>(obj));
723726

724-
return InsertInternal<T>(dialectProvider, dbCmd, obj, commandFilter, selectIdentity);
727+
return InsertInternal<T>(dialectProvider, dbCmd, obj, commandFilter, selectIdentity);
728+
}
729+
else
730+
{
731+
try
732+
{
733+
dialectProvider.EnableIdentityInsert<T>(dbCmd);
734+
dialectProvider.PrepareParameterizedInsertStatement<T>(dbCmd,
735+
insertFields: dialectProvider.GetNonDefaultValueInsertFields<T>(obj),
736+
shouldInclude: f => f == pkField);
737+
InsertInternal<T>(dialectProvider, dbCmd, obj, commandFilter, selectIdentity);
738+
if (selectIdentity)
739+
{
740+
var id = pkField.GetValue(obj);
741+
return Convert.ToInt64(id);
742+
}
743+
return default;
744+
}
745+
finally
746+
{
747+
dialectProvider.DisableIdentityInsert<T>(dbCmd);
748+
}
749+
}
725750
}
726751

727752
internal static long Insert<T>(this IDbCommand dbCmd, Dictionary<string,object> obj, Action<IDbCommand> commandFilter, bool selectIdentity = false)

tests/ServiceStack.OrmLite.Tests/OrmLiteInsertTests.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Diagnostics;
34
using System.Runtime.Versioning;
45
using System.Threading.Tasks;
56
using NUnit.Framework;
@@ -11,12 +12,56 @@
1112

1213
namespace ServiceStack.OrmLite.Tests
1314
{
15+
internal class CustomerWithAutoIncrementPrimaryKey
16+
{
17+
[PrimaryKey, AutoIncrement]
18+
public int Id { get; set; }
19+
public string Name { get; set; }
20+
}
1421

1522
[TestFixtureOrmLite]
1623
public class OrmLiteInsertTests : OrmLiteProvidersTestBase
1724
{
1825
public OrmLiteInsertTests(DialectContext context) : base(context) {}
1926

27+
[Test]
28+
public void Can_insert_into_CustomerWithAutoIncrementPrimaryKey_table()
29+
{
30+
var id = (int)DateTime.Now.ToOADate();
31+
using (var db = OpenDbConnection())
32+
{
33+
db.CreateTable<CustomerWithAutoIncrementPrimaryKey>(true);
34+
35+
var row = new CustomerWithAutoIncrementPrimaryKey()
36+
{
37+
Id = id,
38+
Name = Guid.NewGuid().ToString()
39+
};
40+
41+
var ret = db.Insert(row, true, true);
42+
Debug.Assert(ret == id);
43+
}
44+
}
45+
46+
[Test]
47+
public async Task Can_insert_async_into_CustomerWithAutoIncrementPrimaryKey_table()
48+
{
49+
var id = (int)DateTime.Now.ToOADate();
50+
using (var db =await OpenDbConnectionAsync())
51+
{
52+
db.CreateTable<CustomerWithAutoIncrementPrimaryKey>(true);
53+
54+
var row = new CustomerWithAutoIncrementPrimaryKey()
55+
{
56+
Id = id,
57+
Name = Guid.NewGuid().ToString()
58+
};
59+
60+
var ret =await db.InsertAsync(row, true, true);
61+
Debug.Assert(ret == id);
62+
}
63+
}
64+
2065
[Test]
2166
public void Can_insert_into_ModelWithFieldsOfDifferentTypes_table()
2267
{

0 commit comments

Comments
 (0)