Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Store and retrieve related items from RelatedItem attribute (complete refactor for new project - clean PR) #23

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics;
using CoreHelpers.WindowsAzure.Storage.Table.Tests.Contracts;
using CoreHelpers.WindowsAzure.Storage.Table.Tests.Delegates;
using CoreHelpers.WindowsAzure.Storage.Table.Tests.Extensions;
Expand Down
121 changes: 121 additions & 0 deletions CoreHelpers.WindowsAzure.Storage.Table.Tests/ITS026RelatedTable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
using System;
using CoreHelpers.WindowsAzure.Storage.Table.Tests.Contracts;
using CoreHelpers.WindowsAzure.Storage.Table.Tests.Extensions;
using CoreHelpers.WindowsAzure.Storage.Table.Tests.Models;
using Xunit.DependencyInjection;

namespace CoreHelpers.WindowsAzure.Storage.Table.Tests
{
[Startup(typeof(Startup))]
[Collection("Sequential")]
public class ITS026RelatedTable
{
private readonly IStorageContext _rootContext;

public ITS026RelatedTable(IStorageContext context)
{
_rootContext = context;
}

[Fact]
public async Task ReadRelatedTable()
{
using (var storageContext = _rootContext.CreateChildContext())
{
// set the tablename context
storageContext.SetTableContext();
//
// create a new user
var user = new UserModel2() { FirstName = "Egon", LastName = "Mueller", Contact = "[email protected]" };
var demo = new DemoModel3() { P = "P2", R = "R2", UserContact = "[email protected]" };

// ensure we are using the attributes
storageContext.AddAttributeMapper();

// ensure the tables exists
await storageContext.CreateTableAsync<UserModel2>();
await storageContext.CreateTableAsync<DemoModel3>();

// inser the models
await storageContext.MergeOrInsertAsync<UserModel2>(user);
await storageContext.MergeOrInsertAsync<DemoModel3>(demo);

// query all
var result = await storageContext.QueryAsync<DemoModel3>();
Assert.Single(result);
Assert.Equal("Egon", result.First().User?.FirstName);
Assert.Equal("Mueller", result.First().User?.LastName);
Assert.Equal("[email protected]", result.First().User?.Contact);

// Clean up
user = result.First().User;
if (user != null)
await storageContext.DeleteAsync<UserModel2>(user);

var userResult = await storageContext.QueryAsync<UserModel2>();
Assert.NotNull(userResult);
Assert.Empty(userResult);


await storageContext.DeleteAsync<DemoModel3>(result);
result = await storageContext.QueryAsync<DemoModel3>();
Assert.NotNull(result);
Assert.Empty(result);

await storageContext.DropTableAsync<UserModel2>();
await storageContext.DropTableAsync<DemoModel3>();
}
}


[Fact]
public async Task WriteRelatedTable()
{
using (var storageContext = _rootContext.CreateChildContext())
{
// set the tablename context
storageContext.SetTableContext();
//
// create a new user
var user = new UserModel2() { FirstName = "Egon", LastName = "Mueller", Contact = "[email protected]" };
var demo = new DemoModel4() { P = "P2", R = "R2", UserContact = "[email protected]", User = user };

// ensure we are using the attributes
storageContext.AddAttributeMapper();

// ensure the tables exists
await storageContext.CreateTableAsync<UserModel2>();
await storageContext.CreateTableAsync<DemoModel4>();

// inser the model
await storageContext.MergeOrInsertAsync<DemoModel4>(demo);

// query all
var result = await storageContext.QueryAsync<DemoModel4>();
Assert.Single(result);
Assert.Equal("Egon", result.First().User?.FirstName);
Assert.Equal("Mueller", result.First().User?.LastName);
Assert.Equal("[email protected]", result.First().User?.Contact);

// Clean up
user = result.First().User;
if (user != null)
await storageContext.DeleteAsync<UserModel2>(user);

var userResult = await storageContext.QueryAsync<UserModel2>();
Assert.NotNull(userResult);
Assert.Empty(userResult);


await storageContext.DeleteAsync<DemoModel4>(result);
result = await storageContext.QueryAsync<DemoModel4>();
Assert.NotNull(result);
Assert.Empty(result);

await storageContext.DropTableAsync<UserModel2>();
await storageContext.DropTableAsync<DemoModel4>();
}
}
}
}

22 changes: 22 additions & 0 deletions CoreHelpers.WindowsAzure.Storage.Table.Tests/Models/DemoModel3.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using CoreHelpers.WindowsAzure.Storage.Table.Attributes;

namespace CoreHelpers.WindowsAzure.Storage.Table.Tests.Models
{
[Storable]
public class DemoModel3
{

[PartitionKey]
public string P { get; set; } = "P1";

[RowKey]
public string R { get; set; } = "R1";

public string UserContact { get; set; } = "[email protected]";

[RelatedTable("Partition01", RowKey = "UserContact")]
public UserModel2? User { get; set; }
}
}

22 changes: 22 additions & 0 deletions CoreHelpers.WindowsAzure.Storage.Table.Tests/Models/DemoModel4.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using CoreHelpers.WindowsAzure.Storage.Table.Attributes;

namespace CoreHelpers.WindowsAzure.Storage.Table.Tests.Models
{
[Storable]
public class DemoModel4
{

[PartitionKey]
public string P { get; set; } = "P1";

[RowKey]
public string R { get; set; } = "R1";

public string UserContact { get; set; } = "[email protected]";

[RelatedTable("Partition01", RowKey = "UserContact", AutoSave = true)]
public UserModel2? User { get; set; }
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System;
namespace CoreHelpers.WindowsAzure.Storage.Table.Attributes
{
public class RelatedTableAttribute : Attribute
{
/// <summary>
/// The partitionkey of the related table, if this is the name of a property on the model the property value will be used.
/// </summary>
public string PartitionKey { get; set; }

/// <summary>
/// The rowkey of the related table, if this is a property on the model, the property value will be loaded, if it is empty this will default to the name of the type.
/// </summary>
public string RowKey { get; set; }

/// <summary>
/// On saving the parent entity also save the related tables, currently not supported with null values in <see cref="RowKey"/>, Defaults to False.
/// </summary>
public bool AutoSave { get; set; }

//TODO:
//public bool AutoDelete { get; set; }

/// <summary>
///
/// </summary>
/// <param name="partitionKey">The partitionkey of the related table, if this is the name of a property on the model the property value will be used.</param>
public RelatedTableAttribute(string partitionKey)
{
PartitionKey = partitionKey;
}

/// <summary>
///
/// </summary>
/// <param name="partitionKey">The partitionkey of the related table, if this is the name of a property on the model the property value will be used.</param>
/// <param name="rowKey">The rowkey of the related table, if this is a property on the model, the property value will be loaded, if it is empty this will default to the name of the type.</param>
public RelatedTableAttribute(string partitionKey, string rowKey)
{
PartitionKey = partitionKey;
RowKey = rowKey;
}

/// <summary>
///
/// </summary>
/// <param name="partitionKey">The partitionkey of the related table, if this is the name of a property on the model the property value will be used.</param>
/// <param name="rowKey">The rowkey of the related table, if this is a property on the model, the property value will be loaded, if it is empty this will default to the name of the type.</param>
/// <param name="autoSave">Sets the autosave property</param>
public RelatedTableAttribute(string partitionKey, string rowKey, bool autoSave)
{
PartitionKey = partitionKey;
RowKey = rowKey;
AutoSave = autoSave;
}

}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Linq;

namespace CoreHelpers.WindowsAzure.Storage.Table.Extensions
{
public enum ExportEdmType
Expand Down Expand Up @@ -34,7 +36,25 @@ public static ExportEdmType GetEdmPropertyType(this Type type)
else if (type == typeof(Int64))
return ExportEdmType.Int64;
else
throw new NotImplementedException($"Datatype {type.ToString()} not supporter");
throw new NotImplementedException($"Datatype {type.ToString()} not supporter");
}

public static bool IsDerivedFromGenericParent(this Type type, Type parentType)
{
if (!parentType.IsGenericType)
{
throw new ArgumentException("type must be generic", "parentType");
}
if (type == null || type == typeof(object))
{
return false;
}
if (type.IsGenericType && type.GetGenericTypeDefinition() == parentType)
{
return true;
}
return type.BaseType.IsDerivedFromGenericParent(parentType)
|| type.GetInterfaces().Any(t => t.IsDerivedFromGenericParent(parentType));
}
}
}
Expand Down
15 changes: 15 additions & 0 deletions CoreHelpers.WindowsAzure.Storage.Table/Internal/DynamicLazy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace CoreHelpers.WindowsAzure.Storage.Table.Internal
{
internal class DynamicLazy<T> : Lazy<T>
{
public DynamicLazy(Func<object> factory) : base(() => (T)factory())
{

}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ private bool MoveNextInternal(bool initialPage)
}

// set the item
Current = TableEntityDynamic.fromEntity<T>(_inPageEnumerator.Current, _context.context.GetEntityMapper<T>());
Current = TableEntityDynamic.fromEntity<T>(_inPageEnumerator.Current, _context.context.GetEntityMapper<T>(), _context.context);

// done
return true;
Expand Down Expand Up @@ -140,9 +140,12 @@ private void InitializePageEnumeratorIfNeeded()

// evaluate the maxItems
int? maxPerPage = _context.maxPerPage.HasValue && _context.maxPerPage.Value > 0 ? _context.maxPerPage : null;


// fix Azurite bug
var filter = string.IsNullOrWhiteSpace(_context.filter) ? null : _context.filter;
// start the query
_pageEnumerator = tc.Query<TableEntity>(_context.filter, maxPerPage, _context.select, _context.cancellationToken).AsPages().GetEnumerator();
_pageEnumerator = tc.Query<TableEntity>(filter, maxPerPage, _context.select, _context.cancellationToken).AsPages().GetEnumerator();

}
}

Expand Down
Loading