Skip to content

Commit 4cca48e

Browse files
committed
added sequential key mapper.
TODO: handle deletes
1 parent 1e4bc35 commit 4cca48e

File tree

4 files changed

+274
-4
lines changed

4 files changed

+274
-4
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System;
2+
using Xtensible.Time;
3+
4+
namespace TableStorage.Abstractions.POCO
5+
{
6+
public class SequentialKeyMapper<T, TKey> : KeyMapper<T, TKey>
7+
{
8+
private static Random _rng = new Random();
9+
10+
private static string GetSequence(bool reverseOrder)
11+
{
12+
long sequence;
13+
if (!reverseOrder)
14+
{
15+
sequence = (Clock.Default.UtcNow.Ticks);
16+
}
17+
else
18+
{
19+
sequence = (DateTimeOffset.MaxValue.Ticks - Clock.Default.UtcNow.Ticks);
20+
}
21+
22+
return $"{sequence.ToString("D20")}.{_rng.Next(9999)}";
23+
}
24+
public SequentialKeyMapper(bool reverseOrder = true) : base(x=>GetSequence(reverseOrder), null, null,x=>GetSequence(reverseOrder))
25+
{
26+
}
27+
28+
}
29+
}

src/TableStorage.Abstractions.POCO/TableStorage.Abstractions.POCO.csproj

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<TargetFramework>netstandard2.0</TargetFramework>
5-
<Version>2.5-beta</Version>
5+
<Version>2.6.0-beta</Version>
66
<Authors>Giovanni Galbo</Authors>
77
<Company>Giovanni Galbo</Company>
88
<Description>A repository wrapper for Azure Table Storage that uses POCOs (Plain Old CLR Objects) instead of objects that implemeent ITableEntity.
@@ -19,18 +19,20 @@ The library will convert simple properties to fields in Azure Table Storage. Com
1919
<PackageProjectUrl>https://github.com/giometrix/TableStorage.Abstractions.POCO</PackageProjectUrl>
2020
<RepositoryUrl>https://github.com/giometrix/TableStorage.Abstractions.POCO</RepositoryUrl>
2121
<PackageTags>table-storage azure-table-storage poco table-entities tableentity</PackageTags>
22-
<PackageReleaseNotes>Add events for insert, update, delete, table deleted</PackageReleaseNotes>
22+
<PackageReleaseNotes>Added events for insert, update, delete, table deleted
23+
Added a new SequentialKeyMapper for rowkeys that can be in chronological (or reverse chronological) order</PackageReleaseNotes>
2324
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
2425
<PackageLicenseExpression>MIT</PackageLicenseExpression>
25-
<AssemblyVersion>2.5.0.0</AssemblyVersion>
26-
<FileVersion>2.5.0.0</FileVersion>
26+
<AssemblyVersion>2.6.0.0</AssemblyVersion>
27+
<FileVersion>2.6.0.0</FileVersion>
2728
<PackageIcon>xtensible-x.png</PackageIcon>
2829
<PackageIconUrl />
2930
</PropertyGroup>
3031

3132
<ItemGroup>
3233
<PackageReference Include="TableStorage.Abstractions" Version="3.1.0" />
3334
<PackageReference Include="TableStorage.Abstractions.TableEntityConverters" Version="1.2.0" />
35+
<PackageReference Include="Xtensible.Time.Clock" Version="1.1.0" />
3436
</ItemGroup>
3537

3638
<ItemGroup>

tests/TableStorage.Abstractions.POCO.SecondaryIndexes.Tests/IndexTests.cs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,100 @@ public async Task get_by_index_partition_key_string_paged_async()
406406

407407
}
408408

409+
410+
[TestMethod]
411+
public async Task insert_record_with_secondary_index_using_sequential_key_mapper()
412+
{
413+
var pKeyMapper = new KeyMapper<Employee, int>(e => e.Id.ToString(), int.Parse, e => e.Id,
414+
id => id.ToString());
415+
var rKeyMapper = new SequentialKeyMapper<Employee, int>(true);
416+
417+
var keysConverter = new CalculatedKeysConverter<Employee, int, int>(pKeyMapper, rKeyMapper);
418+
419+
var logStore =
420+
new PocoTableStore<Employee, int, int>("IXLogIndex", "UseDevelopmentStorage=true", keysConverter);
421+
422+
TableStore.AddIndex("Log", logStore);
423+
424+
425+
var employee = new Employee
426+
{
427+
Name = "Test",
428+
CompanyId = 99,
429+
Id = 99,
430+
Department = new Department { Id = 5, Name = "Test" }
431+
};
432+
433+
await TableStore.InsertAsync(employee);
434+
435+
var records = TableStore.GetByPartitionKey(99);
436+
Assert.AreEqual(1, records.Count());
437+
438+
}
439+
440+
[TestMethod]
441+
public async Task update_record_with_secondary_index_using_sequential_key_mapper()
442+
{
443+
var pKeyMapper = new KeyMapper<Employee, int>(e => e.Id.ToString(), int.Parse, e => e.Id,
444+
id => id.ToString());
445+
var rKeyMapper = new SequentialKeyMapper<Employee, int>(true);
446+
447+
var keysConverter = new CalculatedKeysConverter<Employee, int, int>(pKeyMapper, rKeyMapper);
448+
449+
var logStore =
450+
new PocoTableStore<Employee, int, int>("IXLogIndex", "UseDevelopmentStorage=true", keysConverter);
451+
452+
TableStore.AddIndex("Log", logStore);
453+
454+
455+
var employee = new Employee
456+
{
457+
Name = "Test",
458+
CompanyId = 99,
459+
Id = 99,
460+
Department = new Department { Id = 5, Name = "Test" }
461+
};
462+
463+
await TableStore.InsertAsync(employee);
464+
employee.Name = "XXX";
465+
await TableStore.UpdateAsync(employee);
466+
467+
var records = await TableStore.GetByIndexPartitionKeyAsync("Log", 99);
468+
Assert.AreEqual(2, records.Count());
469+
470+
}
471+
472+
[TestMethod]
473+
public async Task delete_record_with_secondary_index_using_sequential_key_mapper()
474+
{
475+
var pKeyMapper = new KeyMapper<Employee, int>(e => e.Id.ToString(), int.Parse, e => e.Id,
476+
id => id.ToString());
477+
var rKeyMapper = new SequentialKeyMapper<Employee, int>(true);
478+
479+
var keysConverter = new CalculatedKeysConverter<Employee, int, int>(pKeyMapper, rKeyMapper);
480+
481+
var logStore =
482+
new PocoTableStore<Employee, int, int>("IXLogIndex", "UseDevelopmentStorage=true", keysConverter);
483+
484+
TableStore.AddIndex("Log", logStore);
485+
486+
487+
var employee = new Employee
488+
{
489+
Name = "Test",
490+
CompanyId = 99,
491+
Id = 99,
492+
Department = new Department { Id = 5, Name = "Test" }
493+
};
494+
495+
await TableStore.InsertAsync(employee);
496+
await TableStore.DeleteAsync(employee);
497+
498+
var records = await TableStore.GetByIndexPartitionKeyAsync("Log", 99);
499+
Assert.AreEqual(1, records.Count());
500+
501+
}
502+
409503
[TestMethod]
410504
public async Task reindex()
411505
{

tests/TableStorage.Abstractions.POCO.Tests/PocoTableStoreTests.cs

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Microsoft.Azure.Cosmos.Table;
66
using Microsoft.VisualStudio.TestTools.UnitTesting;
77
using TableStorage.Abstractions.Store;
8+
using Xtensible.Time;
89

910
namespace TableStorage.Abstractions.POCO.Tests
1011
{
@@ -16,6 +17,8 @@ public partial class PocoTableStoreTests
1617
[TestInitialize]
1718
public void CreateData()
1819
{
20+
Clock.Default = new MockClock(new DateTimeOffset(2020,7,1,11,42,0, TimeSpan.Zero));
21+
1922
tableStore = new PocoTableStore<Employee, int, int>("TestEmployee", "UseDevelopmentStorage=true",
2023
e => e.CompanyId,
2124
e => e.Id);
@@ -1839,6 +1842,148 @@ public void update_record_with_calculated_partition_key_from_multiple_properties
18391842
Assert.AreEqual("Ted", record.Name);
18401843
}
18411844

1845+
[TestMethod]
1846+
public void insert_record_with_sequential_row_key()
1847+
{
1848+
var pKeyMapper = new KeyMapper<Employee, int>(e => e.Id.ToString(), int.Parse, e => e.Id,
1849+
id => id.ToString());
1850+
var rKeyMapper = new SequentialKeyMapper<Employee, int>(false);
1851+
1852+
var keysConverter = new CalculatedKeysConverter<Employee, int, int>(pKeyMapper, rKeyMapper);
1853+
1854+
tableStore =
1855+
new PocoTableStore<Employee, int, int>("TestEmployee", "UseDevelopmentStorage=true", keysConverter);
1856+
1857+
1858+
var employee = new Employee
1859+
{
1860+
CompanyId = 1,
1861+
Id = 242443,
1862+
Name = "Mr. Jim CEO",
1863+
Department = new Department { Id = 22, Name = "Executive" }
1864+
};
1865+
1866+
tableStore.Insert(employee);
1867+
var records = tableStore.GetByPartitionKey(242443);
1868+
1869+
Assert.AreEqual(1,records.Count());
1870+
}
1871+
1872+
[TestMethod]
1873+
public void insert_record_with_sequential_row_key_sorts_correctly()
1874+
{
1875+
var pKeyMapper = new KeyMapper<Employee, int>(e => e.Id.ToString(), int.Parse, e => e.Id,
1876+
id => id.ToString());
1877+
var rKeyMapper = new SequentialKeyMapper<Employee, int>(false);
1878+
1879+
var keysConverter = new CalculatedKeysConverter<Employee, int, int>(pKeyMapper, rKeyMapper);
1880+
1881+
tableStore =
1882+
new PocoTableStore<Employee, int, int>("TestEmployee", "UseDevelopmentStorage=true", keysConverter);
1883+
1884+
1885+
var employee = new Employee
1886+
{
1887+
CompanyId = 1,
1888+
Id = 242443,
1889+
Name = "1",
1890+
Department = new Department { Id = 22, Name = "Executive" }
1891+
};
1892+
1893+
tableStore.Insert(employee);
1894+
1895+
Clock.AsMockClock().Adjust(1000);
1896+
1897+
employee = new Employee
1898+
{
1899+
CompanyId = 1,
1900+
Id = 242443,
1901+
Name = "2",
1902+
Department = new Department { Id = 22, Name = "Executive" }
1903+
};
1904+
1905+
tableStore.Insert(employee);
1906+
Clock.AsMockClock().Adjust(1000);
1907+
1908+
employee = new Employee
1909+
{
1910+
CompanyId = 1,
1911+
Id = 242443,
1912+
Name = "3",
1913+
Department = new Department { Id = 22, Name = "Executive" }
1914+
};
1915+
1916+
1917+
tableStore.Insert(employee);
1918+
Clock.AsMockClock().Adjust(1000);
1919+
1920+
var records = tableStore.GetByPartitionKey(242443).ToList();
1921+
1922+
Assert.AreEqual(3, records.Count());
1923+
1924+
Assert.AreEqual("1", records[0].Name);
1925+
Assert.AreEqual("2", records[1].Name);
1926+
Assert.AreEqual("3", records[2].Name);
1927+
}
1928+
1929+
[TestMethod]
1930+
public void insert_record_with_reverse_sequential_row_key_sorts_correctly()
1931+
{
1932+
var pKeyMapper = new KeyMapper<Employee, int>(e => e.Id.ToString(), int.Parse, e => e.Id,
1933+
id => id.ToString());
1934+
var rKeyMapper = new SequentialKeyMapper<Employee, int>(true);
1935+
1936+
var keysConverter = new CalculatedKeysConverter<Employee, int, int>(pKeyMapper, rKeyMapper);
1937+
1938+
tableStore =
1939+
new PocoTableStore<Employee, int, int>("TestEmployee", "UseDevelopmentStorage=true", keysConverter);
1940+
1941+
1942+
var employee = new Employee
1943+
{
1944+
CompanyId = 1,
1945+
Id = 242443,
1946+
Name = "1",
1947+
Department = new Department { Id = 22, Name = "Executive" }
1948+
};
1949+
1950+
tableStore.Insert(employee);
1951+
1952+
Clock.AsMockClock().Adjust(1000);
1953+
1954+
employee = new Employee
1955+
{
1956+
CompanyId = 1,
1957+
Id = 242443,
1958+
Name = "2",
1959+
Department = new Department { Id = 22, Name = "Executive" }
1960+
};
1961+
1962+
tableStore.Insert(employee);
1963+
Clock.AsMockClock().Adjust(1000);
1964+
1965+
employee = new Employee
1966+
{
1967+
CompanyId = 1,
1968+
Id = 242443,
1969+
Name = "3",
1970+
Department = new Department { Id = 22, Name = "Executive" }
1971+
};
1972+
1973+
1974+
tableStore.Insert(employee);
1975+
Clock.AsMockClock().Adjust(1000);
1976+
1977+
var records = tableStore.GetByPartitionKey(242443).ToList();
1978+
1979+
Assert.AreEqual(3, records.Count());
1980+
1981+
Assert.AreEqual("3", records[0].Name);
1982+
Assert.AreEqual("2", records[1].Name);
1983+
Assert.AreEqual("1", records[2].Name);
1984+
}
1985+
1986+
18421987
[TestCleanup]
18431988
public void Cleanup()
18441989
{

0 commit comments

Comments
 (0)