Skip to content

Commit

Permalink
Merge pull request #52 from gblmarquez/master
Browse files Browse the repository at this point in the history
Ignoring a property is not properly respected
  • Loading branch information
gblmarquez authored Sep 29, 2016
2 parents 888815c + 2d68a3b commit c0ae22f
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ internal static class EntityTypeDataFactory
.Distinct();

return (IEntityTypeData<T>) TypesData.GetOrAdd(type,
key => FindsEntityTypeMap(assemblies, type) ?? new EntityTypeData<T>());
key => FindsEntityTypeMap(assemblies, type) ?? new EntityTypeData<T>());
}

/// <summary>
Expand All @@ -62,6 +62,10 @@ public static void RegisterEntityTypeData(Type entityType, object entityTypeData
TypesData.GetOrAdd(entityType, type => entityTypeData);
}

/// <summary>
/// Registers an assembly with entity type mappings.
/// </summary>
/// <param name="assemblies"></param>
public static void RegisterMappingAssembly(params Assembly[] assemblies)
{
_mappingAssemblies = assemblies;
Expand Down Expand Up @@ -90,7 +94,15 @@ private static object FindsEntityTypeMap(IEnumerable<Assembly> assembliesToSearc
try
{
var entityTypeData = Activator.CreateInstance(type);

var e = entityTypeData as EntityTypeMap;
if (e != null)
{
e.AutoMap();
}

RegisterEntityTypeData(entityType, entityTypeData);

return entityTypeData;
}
catch (TargetInvocationException ex)
Expand Down
56 changes: 34 additions & 22 deletions src/WindowsAzure/Table/EntityConverters/TypeData/EntityTypeMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,38 +22,43 @@ public static void RegisterAssembly(params Assembly[] assemblies)
{
EntityTypeDataFactory.RegisterMappingAssembly(assemblies);
}

/// <summary>
/// Auto map all properties
/// </summary>
internal virtual void AutoMap()
{ }
}

/// <summary>
/// Maps an entity type data.
/// </summary>
/// <typeparam name="T">Entity type.</typeparam>
public class EntityTypeMap<T> : IEntityTypeData<T> where T : class, new()
public class EntityTypeMap<T> : EntityTypeMap, IEntityTypeData<T> where T : class, new()
{
private const BindingFlags PropertyMapFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
private const string PartitionKeyPropertyName = "PartitionKey";
private const string RowKeyPropertyName = "RowKey";
private readonly Type _entityType = typeof (T);
private readonly Type _entityType = typeof(T);
private readonly Dictionary<string, string> _nameChanges = new Dictionary<string, string>();
private readonly IDictionary<string, IProperty<T>> _properties;
private readonly IDictionary<string, IProperty<T>> _properties = new Dictionary<string, IProperty<T>>();
private readonly HashSet<string> _propertiesToIgnore = new HashSet<string>();

/// <summary>
/// Constructor.
/// </summary>
public EntityTypeMap()
{
_properties = AutoMap();
}

/// <summary>
/// Constructor.
/// </summary>
/// <param name="classMapInitializer">The class map initializer.</param>
public EntityTypeMap(Action<EntityTypeMap<T>> classMapInitializer)
: this()
{
classMapInitializer(this);
CheckEntityMap();
AutoMap();
}

/// <summary>
Expand Down Expand Up @@ -90,7 +95,7 @@ public ITableEntity GetEntity(T entity)
throw new ArgumentNullException("entity");
}

var result = new DynamicTableEntity(string.Empty, string.Empty) {ETag = "*"};
var result = new DynamicTableEntity(string.Empty, string.Empty) { ETag = "*" };

foreach (var prop in _properties)
{
Expand All @@ -108,16 +113,32 @@ public IDictionary<string, string> NameChanges
get { return _nameChanges; }
}

internal IDictionary<string, IProperty<T>> AutoMap()
/// <summary>
/// Auto map all properties that can be read and write
/// </summary>
internal override void AutoMap()
{
// Retrieve class members
var members = new List<MemberInfo>(_entityType.GetProperties(PropertyMapFlags).Where(p => p.CanRead && p.CanWrite));
var members = _entityType.GetProperties(PropertyMapFlags).Where(p => p.CanRead && p.CanWrite);

// Create properties for entity members
var properties = members.Select(member => new {Key = member.Name, Value = (IProperty<T>) new RegularProperty<T>(member)})
.Where(result => result != null && result.Value != null);
foreach (var member in members)
{
var name = member.Name;
if (_propertiesToIgnore.Contains(name) == false
&& _properties.ContainsKey(name) == false)
{
_properties.Add(member.Name, new RegularProperty<T>(member));
}
}

return properties.ToDictionary(k => k.Key, e => e.Value);
// Check whether entity's composite key completely defined
if (!_nameChanges.ContainsValue(PartitionKeyPropertyName)
&& !_nameChanges.ContainsValue(RowKeyPropertyName))
{
var message = string.Format(Resources.EntityTypeDataMissingKey, _entityType);
throw new InvalidOperationException(message);
}
}

/// <summary>
Expand Down Expand Up @@ -146,6 +167,7 @@ public EntityTypeMap<T> Ignore<TMember>(Expression<Func<T, TMember>> propertyLam
var member = GetMemberInfoFromLambda(propertyLambda);
_nameChanges.Remove(member.Name);
_properties.Remove(member.Name);
_propertiesToIgnore.Add(member.Name);
return this;
}

Expand Down Expand Up @@ -284,15 +306,5 @@ private static PropertyInfo FindPropertyImplementation(PropertyInfo interfacePro
return actualPropertyAccessors.All(x => propertyAccessors.Contains(x));
});
}

private void CheckEntityMap()
{
// Check whether entity's composite key completely defined
if (!_nameChanges.ContainsValue(PartitionKeyPropertyName) && !_nameChanges.ContainsValue(RowKeyPropertyName))
{
var message = string.Format(Resources.EntityTypeDataMissingKey, _entityType);
throw new InvalidOperationException(message);
}
}
}
}
32 changes: 32 additions & 0 deletions test/WindowsAzure.Tests/Samples/Address.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,38 @@ public AddressInvalidMapping()
}
}

public class AddressInvalidPropertyMapping : EntityTypeMap<AddressInvalidProperty>
{
public AddressInvalidPropertyMapping()
{
PartitionKey(e => e.Country);
RowKey(e => e.Street);
}
}

public class AddressWithIgnoreMapping : EntityTypeMap<AddressIgnoreProperties>
{
public AddressWithIgnoreMapping()
{
PartitionKey(e => e.Country);
RowKey(e => e.Street);
Ignore(e => e.IgnoreOne);
Ignore(e => e.IgnoreTwo);
}
}

public class AddressIgnoreProperties : Address
{
public Address IgnoreOne { get; set; }
public Country IgnoreTwo { get; set; }
}

public sealed class AddressInvalidProperty : Address
{
public Address InvalidOne { get; set; }
public Country InvalidTwo { get; set; }
}

public sealed class AddressInvalidMap : Address
{
}
Expand Down
11 changes: 11 additions & 0 deletions test/WindowsAzure.Tests/Samples/EntityWithInvalidPropertyType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace WindowsAzure.Tests.Samples
{
public class EntityWithInvalidPropertyType
{
public string PKey { get; set; }
public string RKey { get; set; }

// Non-supported property type
public Country Country { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,32 @@ public void GetEntityTypeData_ValidMap()
Assert.Equal(8, entity.Properties.Count);
}

[Fact]
public void GetEntityTypeData_IgnorePropertyMap()
{
// Arrange & Act
var map = EntityTypeDataFactory.GetEntityTypeData<AddressIgnoreProperties>();

// Assert
Assert.NotNull(map.NameChanges);
Assert.Equal(2, map.NameChanges.Count);
Assert.Equal("PartitionKey", map.NameChanges["Country"]);
Assert.Equal("RowKey", map.NameChanges["Street"]);

var entity = (DynamicTableEntity)map.GetEntity(new AddressIgnoreProperties());
Assert.Equal(8, entity.Properties.Count);
}

[Fact]
public void GetEntityTypeData_InvalidPropertyMap()
{
// Act & Assert
Assert.Throws<ArgumentException>(() =>
{
var map = EntityTypeDataFactory.GetEntityTypeData<AddressInvalidProperty>();
});
}

[Fact]
public void GetEntityTypeData_IgnoreProperty()
{
Expand Down Expand Up @@ -108,5 +134,20 @@ public void CreateTypeMapWithOnlyOneKey()
Assert.Equal(1, map.NameChanges.Count);
Assert.Equal("PartitionKey", map.NameChanges["Country"]);
}

[Fact]
public void GetEntityTypeData_IgnoreProperty_EvenWhenUnsupportedType()
{
// Arrange & Act
var map = new EntityTypeMap<EntityWithInvalidPropertyType>(e =>
e.PartitionKey(p => p.PKey)
.RowKey(p => p.RKey)
.Ignore(p => p.Country));

// Assert
Assert.NotNull(map.NameChanges);
Assert.DoesNotContain(map.NameChanges, t => t.Key == "Country");
Assert.Equal(2, map.NameChanges.Count);
}
}
}
1 change: 1 addition & 0 deletions test/WindowsAzure.Tests/WindowsAzure.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
<Compile Include="Samples\Countries.cs" />
<Compile Include="Samples\EntityWithContructor.cs" />
<Compile Include="Samples\EntityWithFields.cs" />
<Compile Include="Samples\EntityWithInvalidPropertyType.cs" />
<Compile Include="Samples\EntityWithMultipleAttributes.cs" />
<Compile Include="Samples\EntityWithoutCompositeKey.cs" />
<Compile Include="Samples\EntityWithProperties.cs" />
Expand Down

0 comments on commit c0ae22f

Please sign in to comment.