Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
89d4772
Add support for identity primary keys
APErebus May 27, 2021
c9d5143
Throw exception when using CustomAssignedId with Identity column
APErebus Jun 10, 2021
c6bb1d8
Remove unnecessary try-catch when executing DbReader
APErebus Jun 10, 2021
fd85926
Fixes from rebase
slewis74 Jun 24, 2021
7695284
added PrimaryKeyHandlers
slewis74 Jun 11, 2021
fe08fb6
Rounding out the PrimaryKeyHandler support, including Load and Alloca…
slewis74 Jun 14, 2021
f68a526
Updated tests to include a sample of TinyType usage, on the Customer …
slewis74 Jun 14, 2021
22ccb0b
cleanup/split into separate files
slewis74 Jun 14, 2021
1106bc1
Adjustments to how the Id prefix can be set, as well as the Format
slewis74 Jun 14, 2021
e419ff1
renamed PrimaryKeyHandlerRegistry property to PrimaryKeyHandlers, for…
slewis74 Jun 14, 2021
1061a4c
Add ability to get the key prefix from the configuration, Octopus ser…
slewis74 Jun 14, 2021
72d171e
Including more of the methods that allow TKey when loading documents
slewis74 Jun 17, 2021
5b8ac76
cleanup
slewis74 Jun 17, 2021
077542d
Fixed IReadQueryExecutor interface to correctly express nullable refe…
slewis74 Jun 17, 2021
706f33b
rolling back the nullable reference type changes to ExecuteScalar
slewis74 Jun 18, 2021
a6266ab
don't use the term TinyType
slewis74 Jun 21, 2021
2427cb4
renamed GetPrimitiveValue and removed FormatKey from IPrimitivePrimar…
slewis74 Jun 21, 2021
82e4bd4
Refactored the DocumentMap and IdColumnMapping builders to be better …
slewis74 Jun 21, 2021
df2c99e
Failing test for custom prefix
pawelpabich Jun 21, 2021
fd30294
The usage of custom Prefix resulted in a wrong type of id handler bei…
pawelpabich Jun 21, 2021
15c83d8
Added an easier to use escape hatch for people that use custom prefix
pawelpabich Jun 21, 2021
162d66a
This was added by mistake
pawelpabich Jun 22, 2021
a9c48d8
Fixed the Delete/DeleteAsync methods on IWriteQueryExecutor, to clean…
slewis74 Jun 22, 2021
7401f6a
Actually load the data
pawelpabich Jun 22, 2021
0a19e30
Make the built in primary key handler types public, so they can be cr…
slewis74 Jun 22, 2021
9d35dea
Changed the StringPrimaryKeyHandler so it takes a string for the pref…
slewis74 Jun 22, 2021
788eb92
Fixed IdColumnMapping constructor, it wasn't setting MaxLength and Di…
slewis74 Jun 28, 2021
7b439e0
Octopus server testing showed up an issue with LoadMany's query prepa…
slewis74 Jun 30, 2021
459f6eb
Re-instated `.In` extension method for use on custom primary key types
slewis74 Jul 6, 2021
f3c566f
Merge pull request #149 from OctopusDeploy/sl/primarykeyhandlers
slewis74 Jul 6, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions source/Nevermore.IntegrationTests/Advanced/HooksFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@ public void ShouldCallHooks()

using var transaction = Store.BeginTransaction();

var customer = new Customer {Id = "C131", FirstName = "Fred", LastName = "Freddy"};
var customer = new Customer {Id = "C131".ToCustomerId(), FirstName = "Fred", LastName = "Freddy"};
transaction.Insert(customer);
AssertLogged(log, "BeforeInsert", "AfterInsert");

customer = transaction.Load<Customer>("C131");
customer = transaction.Load<Customer, CustomerId>("C131".ToCustomerId());

transaction.Update(customer);
AssertLogged(log, "BeforeUpdate", "AfterUpdate");

transaction.Delete(customer);
transaction.Delete<Customer, CustomerId>(customer);
AssertLogged(log, "BeforeDelete", "AfterDelete");

transaction.Commit();
Expand Down
130 changes: 130 additions & 0 deletions source/Nevermore.IntegrationTests/Advanced/IdentityIdFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
using System;
using System.Threading.Tasks;
using FluentAssertions;
using Nevermore.IntegrationTests.Model;
using Nevermore.IntegrationTests.SetUp;
using NUnit.Framework;

namespace Nevermore.IntegrationTests.Advanced
{
public class IdentityIdFixture : FixtureWithRelationalStore
{
[Test]
public void InsertUpdatesDocumentId()
{
// ChaosSqlCommand is set to retry some of the reads which breaks row versioning code because INSERTS/UPDATES,
// even executed via SqlReader, must not be retired.
NoMonkeyBusiness();

var document1 = new DocumentWithIdentityId {Name = "Name"};

document1.Id.Should().Be(0);

RunInTransaction(transaction => transaction.Insert(document1));

document1.Id.Should().NotBe(0);
}

[Test]
public void InsertManyUpdatesDocumentIds()
{
// ChaosSqlCommand is set to retry some of the reads which breaks row versioning code because INSERTS/UPDATES,
// even executed via SqlReader, must not be retired.
NoMonkeyBusiness();

var document1 = new DocumentWithIdentityId {Name = "Name"};
var document2 = new DocumentWithIdentityId {Name = "Name"};

document1.Id.Should().Be(0);
document2.Id.Should().Be(0);

RunInTransaction(transaction => transaction.InsertMany(new[]
{
document1,
document2
}));

document1.Id.Should().NotBe(0);
document2.Id.Should().NotBe(0);
document1.Id.Should().NotBe(document2.Id);
}

[Test]
public async Task InsertAsyncUpdatesDocumentIdAndRowVersion()
{
// ChaosSqlCommand is set to retry some of the reads which breaks row versioning code because INSERTS/UPDATES,
// even executed via SqlReader, must not be retired.
NoMonkeyBusiness();

var document1 = new DocumentWithIdentityId {Name = "Name"};

document1.Id.Should().Be(0);

await RunInTransactionAsync(async transaction => await transaction.InsertAsync(document1));

document1.Id.Should().NotBe(0);
}


[Test]
public async Task InsertAsyncUpdatesDocumentId()
{
// ChaosSqlCommand is set to retry some of the reads which breaks row versioning code because INSERTS/UPDATES,
// even executed via SqlReader, must not be retired.
NoMonkeyBusiness();

var document1 = new DocumentWithIdentityIdAndRowVersion {Name = "Name"};

document1.Id.Should().Be(0);

await RunInTransactionAsync(async transaction => await transaction.InsertAsync(document1));

document1.Id.Should().NotBe(0);

var document2 = RunInTransaction(transaction => transaction.Load<DocumentWithIdentityIdAndRowVersion>(document1.Id));

document2.RowVersion.Should().Equal(document1.RowVersion);

document2.Name = "Name2";
RunInTransaction(transaction => transaction.Update(document2));

document2.RowVersion.Should().NotEqual(document1.RowVersion);
}

TResult RunInTransaction<TResult>(Func<IRelationalTransaction, TResult> func)
{
using var transaction = Store.BeginTransaction();
var result = func(transaction);
transaction.Commit();

return result;
}

void RunInTransaction(Action<IRelationalTransaction> action)
{
RunInTransaction(transaction =>
{
action(transaction);
return string.Empty;
});
}

async Task<TResult> RunInTransactionAsync<TResult>(Func<IRelationalTransaction, Task<TResult>> func)
{
using var transaction = Store.BeginTransaction();
var result = await func(transaction);
await transaction.CommitAsync();

return result;
}

async Task RunInTransactionAsync(Func<IRelationalTransaction,Task> action)
{
await RunInTransactionAsync(async transaction =>
{
await action(transaction);
return true;
});
}
}
}
16 changes: 8 additions & 8 deletions source/Nevermore.IntegrationTests/Advanced/MappingFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class User

// Test accessibility
public string Prop1 { get; set; }
public string Prop2 { get; } = "Hello";
public string Prop2 { get; } = "Hello";
public string Prop3 { get; private set; }
public string Prop4 { get; protected set; }
public string Prop5 { get; private set; }
Expand All @@ -31,13 +31,13 @@ public void SetOtherProps(string prop3, string prop4, string prop5)
Prop5 = prop5;
}
}

enum Education { School, HighSchool, College }

public override void OneTimeSetUp()
{
base.OneTimeSetUp();

NoMonkeyBusiness();
KeepDataBetweenTests();

Expand Down Expand Up @@ -86,7 +86,7 @@ public void ShouldFailIfReadOnlyNotMapped()
[Test, Order(2)]
public void ShouldWorkIfProp2IsSaveOnly()
{
var map = ((IDocumentMap)new UserMap()).Build();
var map = ((IDocumentMap)new UserMap()).Build(Configuration.PrimaryKeyHandlers);
// Pretend the user edited their document map to set it to SaveOnly
((IColumnMappingBuilder) map.Columns.Single(c => c.ColumnName == "Prop2")).SaveOnly();
Configuration.DocumentMaps.Register(map);
Expand All @@ -105,9 +105,9 @@ public void ShouldInsert()
Prop1 = "Prop1",
FirstName = "Fred"
};

user.SetOtherProps("Prop3", "Prop4", "Prop5");

transaction.Insert(user);
transaction.Update(user);
transaction.Commit();
Expand All @@ -117,7 +117,7 @@ public void ShouldInsert()
public void ShouldLoad()
{
using var transaction = Store.BeginTransaction();

var user = transaction.Load<User>("users-123");
user.Should().NotBeNull();
user.Age.Should().Be(18);
Expand All @@ -132,7 +132,7 @@ public void ShouldLoad()
public void ShouldDelete()
{
using var transaction = Store.BeginTransaction();

transaction.Delete<User>("users-123");
var user = transaction.Load<User>("users-123");
user.Should().BeNull();
Expand Down
23 changes: 10 additions & 13 deletions source/Nevermore.IntegrationTests/Advanced/NoJsonFixture.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
using System.Collections.Generic;
using System.Data;
using FluentAssertions;
using Microsoft.Data.SqlClient.Server;
using Nevermore.IntegrationTests.SetUp;
using Nevermore.Mapping;
using NUnit.Framework;
Expand All @@ -14,9 +11,9 @@ public override void OneTimeSetUp()
{
base.OneTimeSetUp();
NoMonkeyBusiness();

ExecuteSql("create table TestSchema.Car([Id] nvarchar(50), [Name] nvarchar(100))");

Store.Configuration.DocumentMaps.Register(new CarMap());
}

Expand All @@ -25,23 +22,23 @@ class Car
public string Id { get; set; }
public string Name { get; set; }
}
class CarMap : DocumentMap<Car>

class CarMap : DocumentMap<Car>
{
public CarMap()
{
Column(m => m.Name);

JsonStorageFormat = JsonStorageFormat.NoJson;
}
}

[Test]
public void ShouldMapWithoutJson()
{
using var transaction = Store.BeginTransaction();
transaction.Insert(new Car { Name = "Volvo" });

var car = transaction.Load<Car>("Cars-1");
car.Should().NotBeNull();
car.Name.Should().Be("Volvo");
Expand All @@ -54,9 +51,9 @@ public void ShouldMapWithoutJson()
car.Name.Should().Be("Bertie");

transaction.Query<Car>().Count().Should().Be(1);
transaction.Delete(car);

transaction.Delete<Car, string>(car);

transaction.Query<Car>().Count().Should().Be(0);

car = transaction.Load<Car>("Cars-1");
Expand Down
26 changes: 13 additions & 13 deletions source/Nevermore.IntegrationTests/KeyAllocatorFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public void ShouldAllocateKeysInChunks()
var allocatorA = new KeyAllocator(Store, 10);
var allocatorB = new KeyAllocator(Store, 10);

// A gets 1-10
// A gets 1-10
AssertNext(allocatorA, "Todos", 1);
AssertNext(allocatorA, "Todos", 2);
AssertNext(allocatorA, "Todos", 3);
Expand Down Expand Up @@ -76,7 +76,7 @@ public void ShouldAllocateInParallel()
const int allocationCount = 20;
const int threadCount = 10;

var projectIds = new ConcurrentBag<string>();
var customerIds = new ConcurrentBag<CustomerId>();
var deploymentIds = new ConcurrentBag<string>();
var random = new Random(1);

Expand All @@ -90,21 +90,21 @@ public void ShouldAllocateInParallel()
var sequence = random.Next(3);
if (sequence == 0)
{
var id = transaction.AllocateId(typeof (Customer));
projectIds.Add(id);
var id = transaction.AllocateId<Customer, CustomerId>();
customerIds.Add(id);
transaction.Commit();
}
else if (sequence == 1)
{
// Abandon some transactions (just projects to make it easier)
var id = transaction.AllocateId(typeof(Customer));
var id = transaction.AllocateId<Customer, CustomerId>();
// Abandoned Ids are not returned to the pool
projectIds.Add(id);
customerIds.Add(id);
transaction.Dispose();
}
else if (sequence == 2)
{
var id = transaction.AllocateId(typeof(Order));
var id = transaction.AllocateId<Order, string>();
deploymentIds.Add(id);
transaction.Commit();
}
Expand All @@ -115,21 +115,21 @@ public void ShouldAllocateInParallel()
Task.WaitAll(tasks);
Func<string, int> removePrefix = x => int.Parse(x.Split('-')[1]);

var projectIdsAfter = projectIds.Select(removePrefix).OrderBy(x => x).ToArray();
var customerIdsAfter = customerIds.Select(x => removePrefix(x.Value)).OrderBy(x => x).ToArray();
var deploymentIdsAfter = deploymentIds.Select(removePrefix).OrderBy(x => x).ToArray();

projectIdsAfter.Distinct().Count().Should().Be(projectIdsAfter.Length);
customerIdsAfter.Distinct().Count().Should().Be(customerIdsAfter.Length);
deploymentIdsAfter.Distinct().Count().Should().Be(deploymentIdsAfter.Length);

// Check that there are no gaps in sequence

var firstProjectId = projectIdsAfter.First();
var lastProjectId = projectIdsAfter.Last();
var firstProjectId = customerIdsAfter.First();
var lastProjectId = customerIdsAfter.Last();

var expectedProjectIds = Enumerable.Range(firstProjectId, lastProjectId - firstProjectId + 1)
.ToList();

projectIdsAfter.Should().BeEquivalentTo(expectedProjectIds);
customerIdsAfter.Should().BeEquivalentTo(expectedProjectIds);
}

static void AssertNext(KeyAllocator allocator, string collection, int expected)
Expand Down
Loading