Skip to content

Commit 5e14a67

Browse files
committed
Add InsertAndReturn/Identity APIs and query support
- Add InsertAndReturn and InsertAndReturnIdentity (sync/async) to EntityMap<T> for retrieving generated values on insert - Add Query/QueryAsync to IDatabaseConnection/DatabaseConnection for DataTable result support (e.g., INSERT ... OUTPUT) - Update docs with usage examples and guidance - Add comprehensive unit and integration tests for new APIs - Fix typo: GetByPrymaryKey → GetByPrimaryKey - Bump version to beta.6
1 parent b4a7995 commit 5e14a67

8 files changed

Lines changed: 968 additions & 3 deletions

File tree

doc/API-Reference.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,8 @@ public long GetCount(IDatabaseConnection connection, Join? join = null,
314314

315315
// Write operations
316316
public void Insert(IDatabaseConnection connection, T newEntity)
317+
public T InsertAndReturn(IDatabaseConnection connection, T newEntity)
318+
public object? InsertAndReturnIdentity(IDatabaseConnection connection, T newEntity)
317319
public void Update(IDatabaseConnection connection, T entity)
318320
public void UpdateMany(IDatabaseConnection connection, IEnumerable<IAssign> assignments,
319321
IExpressionLogical? criteriaExpression = null)
@@ -341,6 +343,10 @@ public Task<long> GetCountAsync(IDatabaseConnection connection, Join? join = nul
341343
// Async write operations
342344
public Task InsertAsync(IDatabaseConnection connection, T newEntity,
343345
CancellationToken cancellationToken = default)
346+
public Task<T> InsertAndReturnAsync(IDatabaseConnection connection, T newEntity,
347+
CancellationToken cancellationToken = default)
348+
public Task<object?> InsertAndReturnIdentityAsync(IDatabaseConnection connection, T newEntity,
349+
CancellationToken cancellationToken = default)
344350
public Task UpdateAsync(IDatabaseConnection connection, T entity,
345351
CancellationToken cancellationToken = default)
346352
public Task UpdateManyAsync(IDatabaseConnection connection, IEnumerable<IAssign> assignments,
@@ -388,6 +394,14 @@ var users = await userMap.GetAsync(connection, filterExpression: filter);
388394
var newUser = new User { Name = "Alice", Email = "alice@example.com" };
389395
await userMap.InsertAsync(connection, newUser);
390396

397+
// Async Insert and get all generated values (auto-increment ID, computed columns, etc.)
398+
var insertedUser = await userMap.InsertAndReturnAsync(connection, newUser);
399+
Console.WriteLine($"Generated ID: {insertedUser.Id}");
400+
401+
// Or if you only need the identity value (more efficient)
402+
var identityValue = await userMap.InsertAndReturnIdentityAsync(connection, newUser);
403+
int generatedId = Convert.ToInt32(identityValue);
404+
391405
// Async Update
392406
existingUser.Name = "Updated Name";
393407
await userMap.UpdateAsync(connection, existingUser);

doc/Getting-Started.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,14 @@ var newUser = new User
169169
};
170170
userMap.Insert(connection, newUser);
171171

172+
// INSERT AND RETURN - Get the entity with all generated values
173+
var insertedUser = userMap.InsertAndReturn(connection, newUser);
174+
Console.WriteLine($"Generated ID: {insertedUser.Id}");
175+
176+
// INSERT AND RETURN IDENTITY ONLY - More efficient when you just need the ID
177+
var identityValue = userMap.InsertAndReturnIdentity(connection, newUser);
178+
int generatedId = Convert.ToInt32(identityValue);
179+
172180
// SELECT all
173181
User[] allUsers = userMap.Get(connection);
174182

@@ -237,6 +245,14 @@ User? user = await userMap.GetByPrymaryKeyAsync(connection, 1);
237245
var newUser = new User { Name = "Bob", Email = "bob@example.com", CreatedAt = DateTime.UtcNow };
238246
await userMap.InsertAsync(connection, newUser);
239247

248+
// Async INSERT AND RETURN - Get the entity with all generated values
249+
var insertedUser = await userMap.InsertAndReturnAsync(connection, newUser);
250+
Console.WriteLine($"Generated ID: {insertedUser.Id}");
251+
252+
// Async INSERT AND RETURN IDENTITY ONLY - More efficient when you just need the ID
253+
var identityValue = await userMap.InsertAndReturnIdentityAsync(connection, newUser);
254+
int generatedId = Convert.ToInt32(identityValue);
255+
240256
// Async UPDATE
241257
user.Email = "newemail@example.com";
242258
await userMap.UpdateAsync(connection, user);

src/XpressData.Test/Integration/AutoIncrementIntegrationTests.cs

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,4 +276,147 @@ public async Task Insert_BigIntAutoIncrement_GeneratesIdAutomatically()
276276
}
277277

278278
#endregion
279+
280+
#region InsertAndReturn Tests
281+
282+
[Fact]
283+
[Trait("Category", "Integration")]
284+
public async Task InsertAndReturn_ReturnsEntityWithGeneratedId()
285+
{
286+
// Arrange
287+
var entity = CreateTestEntity("InsertAndReturnTest");
288+
entity.Id = 0; // Should be generated
289+
290+
// Act
291+
var result = _entityMap!.InsertAndReturn(_connection!, entity);
292+
293+
// Assert
294+
Assert.NotNull(result);
295+
Assert.True(result.Id > 0);
296+
Assert.Equal("InsertAndReturnTest", result.Name);
297+
Assert.Same(entity, result); // Should be the same instance
298+
}
299+
300+
[Fact]
301+
[Trait("Category", "Integration")]
302+
public async Task InsertAndReturnAsync_ReturnsEntityWithGeneratedId()
303+
{
304+
// Arrange
305+
var entity = CreateTestEntity("InsertAndReturnAsyncTest");
306+
entity.Id = 0;
307+
308+
// Act
309+
var result = await _entityMap!.InsertAndReturnAsync(_connection!, entity, cancellationToken: TestContext.Current.CancellationToken);
310+
311+
// Assert
312+
Assert.NotNull(result);
313+
Assert.True(result.Id > 0);
314+
Assert.Equal("InsertAndReturnAsyncTest", result.Name);
315+
}
316+
317+
[Fact]
318+
[Trait("Category", "Integration")]
319+
public async Task InsertAndReturn_MultipleEntities_ReturnsSequentialIds()
320+
{
321+
// Arrange & Act
322+
var entity1 = _entityMap!.InsertAndReturn(_connection!, CreateTestEntity("First"));
323+
var entity2 = _entityMap.InsertAndReturn(_connection!, CreateTestEntity("Second"));
324+
var entity3 = _entityMap.InsertAndReturn(_connection!, CreateTestEntity("Third"));
325+
326+
// Assert
327+
Assert.True(entity1.Id > 0);
328+
Assert.Equal(entity1.Id + 1, entity2.Id);
329+
Assert.Equal(entity2.Id + 1, entity3.Id);
330+
}
331+
332+
[Fact]
333+
[Trait("Category", "Integration")]
334+
public async Task InsertAndReturn_WithNullableDescription_ReturnsNullForDescription()
335+
{
336+
// Arrange
337+
var entity = CreateTestEntityWithNullDescription("NullDescInsertAndReturn");
338+
339+
// Act
340+
var result = _entityMap!.InsertAndReturn(_connection!, entity);
341+
342+
// Assert
343+
Assert.True(result.Id > 0);
344+
Assert.Null(result.Description);
345+
}
346+
347+
#endregion
348+
349+
#region InsertAndReturnIdentity Tests
350+
351+
[Fact]
352+
[Trait("Category", "Integration")]
353+
public async Task InsertAndReturnIdentity_ReturnsGeneratedIdentityValue()
354+
{
355+
// Arrange
356+
var entity = CreateTestEntity("IdentityTest");
357+
358+
// Act
359+
var identityValue = _entityMap!.InsertAndReturnIdentity(_connection!, entity);
360+
361+
// Assert
362+
Assert.NotNull(identityValue);
363+
var identity = Convert.ToInt32(identityValue);
364+
Assert.True(identity > 0);
365+
}
366+
367+
[Fact]
368+
[Trait("Category", "Integration")]
369+
public async Task InsertAndReturnIdentityAsync_ReturnsGeneratedIdentityValue()
370+
{
371+
// Arrange
372+
var entity = CreateTestEntity("IdentityAsyncTest");
373+
374+
// Act
375+
var identityValue = await _entityMap!.InsertAndReturnIdentityAsync(_connection!, entity, cancellationToken: TestContext.Current.CancellationToken);
376+
377+
// Assert
378+
Assert.NotNull(identityValue);
379+
var identity = Convert.ToInt32(identityValue);
380+
Assert.True(identity > 0);
381+
}
382+
383+
[Fact]
384+
[Trait("Category", "Integration")]
385+
public async Task InsertAndReturnIdentity_DoesNotUpdateEntityProperties()
386+
{
387+
// Arrange
388+
var entity = CreateTestEntity("IdentityNoUpdateTest");
389+
entity.Id = 0;
390+
391+
// Act
392+
var identityValue = _entityMap!.InsertAndReturnIdentity(_connection!, entity);
393+
394+
// Assert
395+
Assert.NotNull(identityValue);
396+
Assert.Equal(0, entity.Id); // Entity should NOT be updated
397+
}
398+
399+
[Fact]
400+
[Trait("Category", "Integration")]
401+
public async Task InsertAndReturnIdentity_VsInsertAndReturn_PerformanceComparison()
402+
{
403+
// This test demonstrates both methods work correctly
404+
// InsertAndReturnIdentity is more efficient when only the ID is needed
405+
406+
// Arrange
407+
var entity1 = CreateTestEntity("IdentityOnly");
408+
var entity2 = CreateTestEntity("FullReturn");
409+
410+
// Act
411+
var identityOnly = _entityMap!.InsertAndReturnIdentity(_connection!, entity1);
412+
var fullReturn = _entityMap.InsertAndReturn(_connection!, entity2);
413+
414+
// Assert
415+
Assert.NotNull(identityOnly);
416+
Assert.NotNull(fullReturn);
417+
Assert.True(Convert.ToInt32(identityOnly) > 0);
418+
Assert.True(fullReturn.Id > 0);
419+
}
420+
421+
#endregion
279422
}

0 commit comments

Comments
 (0)