Skip to content

Commit

Permalink
fix for duplicate instances in joins, removed validation around TOP w…
Browse files Browse the repository at this point in the history
…ithout ORDER BY
  • Loading branch information
MoonStorm committed Oct 25, 2016
1 parent 14f39fa commit 330f56b
Show file tree
Hide file tree
Showing 21 changed files with 571 additions and 411 deletions.
146 changes: 146 additions & 0 deletions Dapper.FastCRUD/SqlStatements/RelationshipEntityInstanceIdentity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
namespace Dapper.FastCrud.SqlStatements
{
using System.Linq;
using Dapper.FastCrud.Mappings;

/// <summary>
/// Typed entity identity used by the entity builder.
/// </summary>
internal class RelationshipEntityInstanceIdentity<TEntity> : RelationshipEntityInstanceIdentity
{
public RelationshipEntityInstanceIdentity(EntityMapping entityMapping, TEntity instance)
: base(entityMapping, instance)
{
this.TypedInstance = instance;
}

/// <summary>
/// Gets the instance attached to the current identity.
/// </summary>
public TEntity TypedInstance { get; }
}

/// <summary>
/// Entity identity used by the entity builder.
/// </summary>
internal class RelationshipEntityInstanceIdentity
{
private readonly object[] _keyPropertyValues;
private int _hashCode;

/// <summary>
/// Default constructor.
/// </summary>
public RelationshipEntityInstanceIdentity(EntityMapping entityMapping, object entity)
{
this.EntityMapping = entityMapping;
this.Instance = entity;
this.UniqueInstance = entity;

if (entity != null)
{
_keyPropertyValues = entityMapping.PropertyMappings.Values.Where(propMapping => propMapping.IsPrimaryKey).Select(
propMapping =>
{
var propDescriptor = propMapping.Descriptor;
var propValue = propDescriptor.GetValue(entity);

// assumes that all values for the entity keys provide proper hash keys
// 'rotating hash' is fast due to the use of bit operation, provides a good distribution and doesn't cause overflows
// http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx
_hashCode = (_hashCode << 4) ^ (_hashCode >> 28) ^ (propValue?.GetHashCode() ?? 0);
return propValue;
}).ToArray();
}
}

/// <summary>
/// Gets the entity mapping of the instance attached to this identity.
/// </summary>
public EntityMapping EntityMapping { get; }

/// <summary>
/// Gets or sets a flag indicating that the instance attached to this identity is a duplicate and should be ignored.
/// </summary>
public bool IsDuplicate { get; private set; }

/// <summary>
/// Gets or sets the unique instance different than the current instance in case <see cref="IsDuplicate"/> is set to true.
/// </summary>
public object UniqueInstance { get; private set; }

/// <summary>
/// Gets the untyped instance attached to the current identity.
/// </summary>
public object Instance { get; }

/// <summary>
/// Sets a unique instance in cse the current instance attached to the identity is a duplicate.
/// </summary>
public void SetDuplicate(object uniqueInstance)
{
this.UniqueInstance = uniqueInstance;
this.IsDuplicate = true;
}

/// <summary>Determines whether the specified object is equal to the current object.</summary>
/// <returns>true if the specified object is equal to the current object; otherwise, false.</returns>
/// <param name="other">The object to compare with the current object. </param>
public override bool Equals(object other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}

var otherIdentity = other as RelationshipEntityInstanceIdentity;
if (ReferenceEquals(otherIdentity, null))
{
return false;
}
return Equals(otherIdentity);
}

/// <summary>Serves as the default hash function. </summary>
/// <returns>A hash code for the current object.</returns>
public override int GetHashCode()
{
return _hashCode;
}

public static bool operator ==(RelationshipEntityInstanceIdentity left, RelationshipEntityInstanceIdentity right)
{
return Equals(left, right);
}

public static bool operator !=(RelationshipEntityInstanceIdentity left, RelationshipEntityInstanceIdentity right)
{
return !Equals(left, right);
}

protected bool Equals(RelationshipEntityInstanceIdentity other)
{
if (_keyPropertyValues.Length != other._keyPropertyValues.Length)
{
return false;
}

for (var keyValueIndex = 0; keyValueIndex < _keyPropertyValues.Length; keyValueIndex++)
{
var currentKeyValue = _keyPropertyValues[keyValueIndex];
var otherKeyValue = other._keyPropertyValues[keyValueIndex];

if (!object.Equals(currentKeyValue, otherKeyValue))
{
return false;
}
}

return true;
}
}
}
14 changes: 7 additions & 7 deletions Dapper.FastCrud.Benchmarks/DapperExtensionsSteps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public void WhenIInsertSingleIntKeyEntitiesUsingDapperExtensions(int entitiesCou
generatedEntity.Id = DapperExtensions.Insert(dbConnection, generatedEntity);

Assert.Greater(generatedEntity.Id, 1); // the seed starts from 2 in the db to avoid confusion with the number of rows modified
_testContext.LocalEntities.Add(generatedEntity);
_testContext.LocalInsertedEntities.Add(generatedEntity);
}
}

Expand All @@ -50,7 +50,7 @@ public void WhenISelectAllTheSingleIntKeyEntitiesUsingDapperExtensions()
public void WhenISelectAllTheSingleIntKeyEntitiesThatIPreviouslyInsertedUsingDapperExtensions()
{
var dbConnection = _testContext.DatabaseConnection;
foreach (var entity in _testContext.LocalEntities.OfType<SimpleBenchmarkEntity>())
foreach (var entity in _testContext.LocalInsertedEntities.OfType<SimpleBenchmarkEntity>())
{
_testContext.QueriedEntities.Add(DapperExtensions.Get<SimpleBenchmarkEntity>(dbConnection, entity.Id));
}
Expand All @@ -60,15 +60,15 @@ public void WhenISelectAllTheSingleIntKeyEntitiesThatIPreviouslyInsertedUsingDap
public void WhenIUpdateAllTheSingleIntKeyEntitiesThatIPreviouslyInsertedUsingDapperExtensions()
{
var dbConnection = _testContext.DatabaseConnection;
var entityCount = _testContext.LocalEntities.Count;
var entityCount = _testContext.LocalInsertedEntities.Count;

for (var entityIndex = 0; entityIndex < _testContext.LocalEntities.Count; entityIndex++)
for (var entityIndex = 0; entityIndex < _testContext.LocalInsertedEntities.Count; entityIndex++)
{
var oldEntity = _testContext.LocalEntities[entityIndex] as SimpleBenchmarkEntity;
var oldEntity = _testContext.LocalInsertedEntities[entityIndex] as SimpleBenchmarkEntity;
var newEntity = this.GenerateSimpleBenchmarkEntity(entityCount++);
newEntity.Id = oldEntity.Id;
DapperExtensions.Update(dbConnection, newEntity);
_testContext.LocalEntities[entityIndex] = newEntity;
_testContext.LocalInsertedEntities[entityIndex] = newEntity;
}
}

Expand All @@ -77,7 +77,7 @@ public void WhenIDeleteAllTheInsertedSingleIntKeyEntitiesUsingDapperExtensions()
{
var dbConnection = _testContext.DatabaseConnection;

foreach (var entity in _testContext.LocalEntities.OfType<SimpleBenchmarkEntity>())
foreach (var entity in _testContext.LocalInsertedEntities.OfType<SimpleBenchmarkEntity>())
{
DapperExtensions.Delete(dbConnection, entity);
}
Expand Down
16 changes: 8 additions & 8 deletions Dapper.FastCrud.Benchmarks/DapperSteps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public void WhenIInsertBenchmarkEntitiesUsingADONET(int entitiesCount)
}

Assert.Greater(generatedEntity.Id, 1); // the seed starts from 2 in the db to avoid confusion with the number of rows modified
_testContext.LocalEntities.Add(generatedEntity);
_testContext.LocalInsertedEntities.Add(generatedEntity);
}
}

Expand All @@ -55,7 +55,7 @@ public void WhenIInsertSingleIntKeyEntitiesUsingDapper(int entitiesCount)
var generatedEntity = this.GenerateSimpleBenchmarkEntity(entityIndex);
generatedEntity.Id = dbConnection.ExecuteScalar<int>(_insertSql, generatedEntity);
Assert.Greater(generatedEntity.Id, 1); // the seed starts from 2 in the db to avoid confusion with the number of rows modified
_testContext.LocalEntities.Add(generatedEntity);
_testContext.LocalInsertedEntities.Add(generatedEntity);
}
}

Expand All @@ -71,7 +71,7 @@ public void WhenISelectAllTheSingleIntKeyEntitiesThatIPreviouslyInsertedUsingDap
{
var dbConnection = _testContext.DatabaseConnection;

foreach (var entity in _testContext.LocalEntities.OfType<SimpleBenchmarkEntity>())
foreach (var entity in _testContext.LocalInsertedEntities.OfType<SimpleBenchmarkEntity>())
{
_testContext.QueriedEntities.AddRange(dbConnection.Query<SimpleBenchmarkEntity>(_selectByIdSql,new { Id = entity.Id }));
}
Expand All @@ -81,15 +81,15 @@ public void WhenISelectAllTheSingleIntKeyEntitiesThatIPreviouslyInsertedUsingDap
public void WhenIUpdateAllTheSingleIntKeyEntitiesThatIPreviouslyInsertedUsingDapper()
{
var dbConnection = _testContext.DatabaseConnection;
var entityCount = _testContext.LocalEntities.Count;
var entityCount = _testContext.LocalInsertedEntities.Count;

for (var entityIndex = 0; entityIndex < _testContext.LocalEntities.Count; entityIndex++)
for (var entityIndex = 0; entityIndex < _testContext.LocalInsertedEntities.Count; entityIndex++)
{
var oldEntity = _testContext.LocalEntities[entityIndex] as SimpleBenchmarkEntity;
var oldEntity = _testContext.LocalInsertedEntities[entityIndex] as SimpleBenchmarkEntity;
var newEntity = this.GenerateSimpleBenchmarkEntity(entityCount++);
newEntity.Id = oldEntity.Id;
dbConnection.Execute(_updateSql, newEntity);
_testContext.LocalEntities[entityIndex] = newEntity;
_testContext.LocalInsertedEntities[entityIndex] = newEntity;
}
}

Expand All @@ -98,7 +98,7 @@ public void WhenIDeleteAllTheInsertedSingleIntKeyEntitiesUsingDapper()
{
var dbConnection = _testContext.DatabaseConnection;

foreach (var entity in _testContext.LocalEntities.OfType<SimpleBenchmarkEntity>())
foreach (var entity in _testContext.LocalInsertedEntities.OfType<SimpleBenchmarkEntity>())
{
dbConnection.Execute(_deleteByIdSql, new {Id=entity.Id});
}
Expand Down
14 changes: 7 additions & 7 deletions Dapper.FastCrud.Benchmarks/EfSteps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public void WhenIInsertSingleIntKeyEntitiesUsingEntityFramework(int entitiesCoun
}

Assert.Greater(generatedEntity.Id, 1); // the seed starts from 2 in the db to avoid confusion with the number of rows modified
_testContext.LocalEntities.Add(generatedEntity);
_testContext.LocalInsertedEntities.Add(generatedEntity);
}
}

Expand All @@ -53,7 +53,7 @@ public void WhenISelectAllTheSingleIntKeyEntitiesUsingEntityFramework()
[When(@"I delete all the inserted benchmark entities using Entity Framework")]
public void WhenIDeleteAllTheInsertedSingleIntKeyEntitiesUsingEntityFramework()
{
foreach (var entity in _testContext.LocalEntities.OfType<SimpleBenchmarkEntity>())
foreach (var entity in _testContext.LocalInsertedEntities.OfType<SimpleBenchmarkEntity>())
{
using (var dbContext = new EfDbContext(_testContext.DatabaseConnection, _compiledModel, false))
{
Expand All @@ -67,7 +67,7 @@ public void WhenIDeleteAllTheInsertedSingleIntKeyEntitiesUsingEntityFramework()
[When(@"I select all the benchmark entities that I previously inserted using Entity Framework")]
public void WhenISelectAllTheSingleIntKeyEntitiesThatIPreviouslyInsertedUsingEntityFramework()
{
foreach (var entity in _testContext.LocalEntities.OfType<SimpleBenchmarkEntity>())
foreach (var entity in _testContext.LocalInsertedEntities.OfType<SimpleBenchmarkEntity>())
{
using (var dbContext = new EfDbContext(_testContext.DatabaseConnection, _compiledModel, false))
{
Expand All @@ -79,11 +79,11 @@ public void WhenISelectAllTheSingleIntKeyEntitiesThatIPreviouslyInsertedUsingEnt
[When(@"I update all the benchmark entities that I previously inserted using Entity Framework")]
public void WhenIUpdateAllTheSingleIntKeyEntitiesThatIPreviouslyInsertedUsingEntityFramework()
{
var entityCount = _testContext.LocalEntities.Count;
var entityCount = _testContext.LocalInsertedEntities.Count;

for (var entityIndex = 0; entityIndex < _testContext.LocalEntities.Count; entityIndex++)
for (var entityIndex = 0; entityIndex < _testContext.LocalInsertedEntities.Count; entityIndex++)
{
var oldEntity = _testContext.LocalEntities[entityIndex] as SimpleBenchmarkEntity;
var oldEntity = _testContext.LocalInsertedEntities[entityIndex] as SimpleBenchmarkEntity;
var newEntity = new SimpleBenchmarkEntity();
newEntity.Id = oldEntity.Id;

Expand All @@ -93,7 +93,7 @@ public void WhenIUpdateAllTheSingleIntKeyEntitiesThatIPreviouslyInsertedUsingEnt
this.GenerateSimpleBenchmarkEntity(entityCount++, newEntity);
dbContext.SaveChanges();
}
_testContext.LocalEntities[entityIndex] = newEntity;
_testContext.LocalInsertedEntities[entityIndex] = newEntity;
}
}
}
Expand Down
14 changes: 7 additions & 7 deletions Dapper.FastCrud.Benchmarks/FastCrudSteps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public void WhenIInsertSingleIntKeyEntitiesUsingFastCrud(int entitiesCount)
// the entity already has the associated id set

Assert.Greater(generatedEntity.Id, 1); // the seed starts from 2 in the db to avoid confusion with the number of rows modified
_testContext.LocalEntities.Add(generatedEntity);
_testContext.LocalInsertedEntities.Add(generatedEntity);
}
}

Expand All @@ -53,7 +53,7 @@ public void WhenISelectAllTheSingleIntKeyEntitiesUsingFastCrud()
public void WhenISelectAllTheSingleIntKeyEntitiesThatIPreviouslyInsertedUsingFastCrud()
{
var dbConnection = _testContext.DatabaseConnection;
foreach (var entity in _testContext.LocalEntities.OfType<SimpleBenchmarkEntity>())
foreach (var entity in _testContext.LocalInsertedEntities.OfType<SimpleBenchmarkEntity>())
{
_testContext.QueriedEntities.Add(FastCrud.Get<SimpleBenchmarkEntity>(dbConnection, new SimpleBenchmarkEntity() {Id = entity.Id}));
}
Expand All @@ -63,15 +63,15 @@ public void WhenISelectAllTheSingleIntKeyEntitiesThatIPreviouslyInsertedUsingFas
public void WhenIUpdateAllTheSingleIntKeyEntitiesThatIPreviouslyInsertedUsingFastCrud()
{
var dbConnection = _testContext.DatabaseConnection;
var entityCount = _testContext.LocalEntities.Count;
var entityCount = _testContext.LocalInsertedEntities.Count;

for (var entityIndex = 0; entityIndex < _testContext.LocalEntities.Count; entityIndex++)
for (var entityIndex = 0; entityIndex < _testContext.LocalInsertedEntities.Count; entityIndex++)
{
var oldEntity = _testContext.LocalEntities[entityIndex] as SimpleBenchmarkEntity;
var oldEntity = _testContext.LocalInsertedEntities[entityIndex] as SimpleBenchmarkEntity;
var newEntity = this.GenerateSimpleBenchmarkEntity(entityCount++);
newEntity.Id = oldEntity.Id;
FastCrud.Update(dbConnection, newEntity);
_testContext.LocalEntities[entityIndex] = newEntity;
_testContext.LocalInsertedEntities[entityIndex] = newEntity;
}
}

Expand All @@ -80,7 +80,7 @@ public void WhenIDeleteAllTheInsertedSingleIntKeyEntitiesUsingFastCrud()
{
var dbConnection = _testContext.DatabaseConnection;

foreach (var entity in _testContext.LocalEntities.OfType<SimpleBenchmarkEntity>())
foreach (var entity in _testContext.LocalInsertedEntities.OfType<SimpleBenchmarkEntity>())
{
FastCrud.Delete(dbConnection, entity);
}
Expand Down
Loading

0 comments on commit 330f56b

Please sign in to comment.