Skip to content
This repository has been archived by the owner on Dec 24, 2022. It is now read-only.

Commit

Permalink
Change Delete API to remove null conditions in parameterized queries …
Browse files Browse the repository at this point in the history
…and into filter
  • Loading branch information
mythz committed Jan 17, 2015
1 parent 7c9acc6 commit 2670b3c
Show file tree
Hide file tree
Showing 14 changed files with 154 additions and 74 deletions.
Expand Up @@ -114,10 +114,17 @@ private static Task<int> AssertRowsUpdatedAsync(IDbCommand dbCmd, bool hadRowVer
});
}

internal static Task<int> DeleteAsync<T>(this IDbCommand dbCmd, T filter, CancellationToken token)
{
return dbCmd.DeleteAsync<T>((object)filter, token);
}

internal static Task<int> DeleteAsync<T>(this IDbCommand dbCmd, object anonType, CancellationToken token)
{
var dialectProvider = dbCmd.GetDialectProvider();
var hadRowVersion = dialectProvider.PrepareParameterizedDeleteStatement<T>(dbCmd, anonType.AllFields<T>());

var hadRowVersion = dialectProvider.PrepareParameterizedDeleteStatement<T>(
dbCmd, anonType.AllFieldsMap<T>());

dialectProvider.SetParameterValues<T>(dbCmd, anonType);

Expand All @@ -127,30 +134,31 @@ internal static Task<int> DeleteAsync<T>(this IDbCommand dbCmd, object anonType,
internal static Task<int> DeleteNonDefaultsAsync<T>(this IDbCommand dbCmd, T filter, CancellationToken token)
{
var dialectProvider = dbCmd.GetDialectProvider();
var hadRowVersion = dialectProvider.PrepareParameterizedDeleteStatement<T>(dbCmd, filter.NonDefaultFields<T>());
var hadRowVersion = dialectProvider.PrepareParameterizedDeleteStatement<T>(
dbCmd, filter.AllFieldsMap<T>().NonDefaultsOnly());

dialectProvider.SetParameterValues<T>(dbCmd, filter);

return AssertRowsUpdatedAsync(dbCmd, hadRowVersion, token);
}

internal static Task<int> DeleteAsync<T>(this IDbCommand dbCmd, CancellationToken token, params object[] objs)
internal static Task<int> DeleteAsync<T>(this IDbCommand dbCmd, CancellationToken token, params T[] objs)
{
if (objs.Length == 0)
return TaskResult.Zero;

return DeleteAllAsync<T>(dbCmd, objs[0].AllFields<T>(), objs, token);
return DeleteAllAsync(dbCmd, objs, fieldValuesFn:null, token: token);
}

internal static Task<int> DeleteNonDefaultsAsync<T>(this IDbCommand dbCmd, CancellationToken token, params T[] filters)
{
if (filters.Length == 0)
return TaskResult.Zero;

return DeleteAllAsync<T>(dbCmd, filters[0].NonDefaultFields<T>(), filters.Map(x => (object)x), token);
return DeleteAllAsync(dbCmd, filters, o => o.AllFieldsMap<T>().NonDefaultsOnly(), token);
}

private static Task<int> DeleteAllAsync<T>(IDbCommand dbCmd, ICollection<string> deleteFields, IEnumerable<object> objs, CancellationToken token)
private static Task<int> DeleteAllAsync<T>(IDbCommand dbCmd, IEnumerable<T> objs, Func<object, Dictionary<string, object>> fieldValuesFn = null, CancellationToken token=default(CancellationToken))
{
IDbTransaction dbTrans = null;

Expand All @@ -159,11 +167,17 @@ private static Task<int> DeleteAllAsync<T>(IDbCommand dbCmd, ICollection<string>
dbCmd.Transaction = dbTrans = dbCmd.Connection.BeginTransaction();

var dialectProvider = dbCmd.GetDialectProvider();
dialectProvider.PrepareParameterizedDeleteStatement<T>(dbCmd, deleteFields);

return objs.EachAsync((obj, i) =>
{
var fieldValues = fieldValuesFn != null
? fieldValuesFn(obj)
: obj.AllFieldsMap<T>();
dialectProvider.PrepareParameterizedDeleteStatement<T>(dbCmd, fieldValues);
dialectProvider.SetParameterValues<T>(dbCmd, obj);
return dbCmd.ExecNonQueryAsync(token)
.Then(rowsAffected => count += rowsAffected);
})
Expand Down
2 changes: 1 addition & 1 deletion src/ServiceStack.OrmLite/IOrmLiteDialectProvider.cs
Expand Up @@ -84,7 +84,7 @@ public interface IOrmLiteDialectProvider

bool PrepareParameterizedUpdateStatement<T>(IDbCommand cmd, ICollection<string> updateFields = null);

bool PrepareParameterizedDeleteStatement<T>(IDbCommand cmd, ICollection<string> deleteFields = null);
bool PrepareParameterizedDeleteStatement<T>(IDbCommand cmd, IDictionary<string, object> delteFieldValues);

void PrepareStoredProcedureStatement<T>(IDbCommand cmd, T obj);

Expand Down
21 changes: 17 additions & 4 deletions src/ServiceStack.OrmLite/OrmLiteDialectProviderBase.cs
Expand Up @@ -628,12 +628,14 @@ public virtual void AppendFieldConditionFmt(StringBuilder sqlFilter, FieldDefini
fieldDef.GetQuotedValue(objWithProperties, this));
}

public virtual bool PrepareParameterizedDeleteStatement<T>(IDbCommand cmd, ICollection<string> deleteFields = null)
public virtual bool PrepareParameterizedDeleteStatement<T>(IDbCommand cmd, IDictionary<string, object> deleteFields)
{
if (deleteFields == null || deleteFields.Count == 0)
throw new ArgumentException("DELETE's must have at least 1 criteria");

var sqlFilter = new StringBuilder();
var modelDef = typeof(T).GetModelDefinition();
var hadRowVesion = false;
var hasSpecificFilter = deleteFields != null && deleteFields.Count > 0;

cmd.Parameters.Clear();
cmd.CommandTimeout = OrmLiteConfig.CommandTimeout;
Expand All @@ -643,7 +645,9 @@ public virtual bool PrepareParameterizedDeleteStatement<T>(IDbCommand cmd, IColl
if (fieldDef.ShouldSkipDelete())
continue;

if (!fieldDef.IsRowVersion && (hasSpecificFilter && !deleteFields.Contains(fieldDef.Name)))
object fieldValue;

if (!deleteFields.TryGetValue(fieldDef.Name, out fieldValue))
continue;

if (fieldDef.IsRowVersion)
Expand All @@ -654,7 +658,16 @@ public virtual bool PrepareParameterizedDeleteStatement<T>(IDbCommand cmd, IColl
if (sqlFilter.Length > 0)
sqlFilter.Append(" AND ");

AppendFieldCondition(sqlFilter, fieldDef, cmd);
if (fieldValue != null)
{
AppendFieldCondition(sqlFilter, fieldDef, cmd);
}
else
{
sqlFilter
.Append(GetQuotedColumnName(fieldDef.FieldName))
.Append(" IS NULL");
}
}
catch (Exception ex)
{
Expand Down
17 changes: 17 additions & 0 deletions src/ServiceStack.OrmLite/OrmLiteReadCommandExtensions.cs
Expand Up @@ -214,6 +214,23 @@ internal static List<string> AllFields<T>(this object anonType)
return ret;
}

internal static Dictionary<string, object> NonDefaultsOnly(this Dictionary<string, object> fieldValues)
{
var map = new Dictionary<string, object>();
foreach (var entry in fieldValues)
{
if (entry.Value != null)
{
var type = entry.Value.GetType();
if (!type.IsValueType || !entry.Value.Equals(type.GetDefaultValue()))
{
map[entry.Key] = entry.Value;
}
}
}
return map;
}

internal static List<string> NonDefaultFields<T>(this object anonType)
{
var ret = new List<string>();
Expand Down
13 changes: 2 additions & 11 deletions src/ServiceStack.OrmLite/OrmLiteWriteApi.cs
Expand Up @@ -93,23 +93,14 @@ public static int Delete<T>(this IDbConnection dbConn, object anonFilter)
return dbConn.Exec(dbCmd => dbCmd.Delete<T>(anonFilter));
}

/// <summary>
/// Delete 1 or more rows in a transaction using an anonymous type filter. E.g:
/// <para>db.Delete&lt;Person&gt;(new { FirstName = "Jimi", Age = 27 }, new { FirstName = "Janis", Age = 27 })</para>
/// </summary>
public static int Delete<T>(this IDbConnection dbConn, params object[] anonFilters)
{
return dbConn.Exec(dbCmd => dbCmd.Delete<T>(anonFilters));
}

/// <summary>
/// Delete 1 row using all fields in the filter. E.g:
/// <para>db.Delete(new Person { Id = 1, FirstName = "Jimi", LastName = "Hendrix", Age = 27 })</para>
/// </summary>
/// <returns>number of rows deleted</returns>
public static int Delete<T>(this IDbConnection dbConn, T allFieldsFilter)
{
return dbConn.Exec(dbCmd => dbCmd.Delete<T>(allFieldsFilter));
return dbConn.Exec(dbCmd => dbCmd.Delete(allFieldsFilter));
}

/// <summary>
Expand All @@ -118,7 +109,7 @@ public static int Delete<T>(this IDbConnection dbConn, T allFieldsFilter)
/// </summary>
public static int Delete<T>(this IDbConnection dbConn, params T[] allFieldsFilters)
{
return dbConn.Exec(dbCmd => dbCmd.Delete<T>(allFieldsFilters));
return dbConn.Exec(dbCmd => dbCmd.Delete(allFieldsFilters));
}

/// <summary>
Expand Down
23 changes: 7 additions & 16 deletions src/ServiceStack.OrmLite/OrmLiteWriteApiAsync.cs
Expand Up @@ -98,27 +98,14 @@ public static Task<int> DeleteAsync<T>(this IDbConnection dbConn, object anonFil
return dbConn.Exec(dbCmd => dbCmd.DeleteAsync<T>(anonFilter, token));
}

/// <summary>
/// Delete 1 or more rows in a transaction using an anonymous type filter. E.g:
/// <para>db.Delete&lt;Person&gt;(new { FirstName = "Jimi", Age = 27 }, new { FirstName = "Janis", Age = 27 })</para>
/// </summary>
public static Task<int> DeleteAsync<T>(this IDbConnection dbConn, CancellationToken token, params object[] anonFilters)
{
return dbConn.Exec(dbCmd => dbCmd.DeleteAsync<T>(token, anonFilters));
}
public static Task<int> DeleteAsync<T>(this IDbConnection dbConn, params object[] anonFilters)
{
return dbConn.Exec(dbCmd => dbCmd.DeleteAsync<T>(default(CancellationToken), anonFilters));
}

/// <summary>
/// Delete 1 row using all fields in the filter. E.g:
/// <para>db.Delete(new Person { Id = 1, FirstName = "Jimi", LastName = "Hendrix", Age = 27 })</para>
/// </summary>
/// <returns>number of rows deleted</returns>
public static Task<int> DeleteAsync<T>(this IDbConnection dbConn, T allFieldsFilter, CancellationToken token = default(CancellationToken))
{
return dbConn.Exec(dbCmd => dbCmd.DeleteAsync<T>(allFieldsFilter, token));
return dbConn.Exec(dbCmd => dbCmd.DeleteAsync(allFieldsFilter, token));
}

/// <summary>
Expand All @@ -127,7 +114,11 @@ public static Task<int> DeleteAsync<T>(this IDbConnection dbConn, T allFieldsFil
/// </summary>
public static Task<int> DeleteAsync<T>(this IDbConnection dbConn, CancellationToken token = default(CancellationToken), params T[] allFieldsFilters)
{
return dbConn.Exec(dbCmd => dbCmd.DeleteAsync<T>(token, allFieldsFilters));
return dbConn.Exec(dbCmd => dbCmd.DeleteAsync(token, allFieldsFilters));
}
public static Task<int> DeleteAsync<T>(this IDbConnection dbConn, params T[] allFieldsFilters)
{
return dbConn.Exec(dbCmd => dbCmd.DeleteAsync(default(CancellationToken), allFieldsFilters));
}

/// <summary>
Expand All @@ -137,7 +128,7 @@ public static Task<int> DeleteAsync<T>(this IDbConnection dbConn, CancellationTo
/// <returns>number of rows deleted</returns>
public static Task<int> DeleteNonDefaultsAsync<T>(this IDbConnection dbConn, T nonDefaultsFilter, CancellationToken token = default(CancellationToken))
{
return dbConn.Exec(dbCmd => dbCmd.DeleteNonDefaultsAsync(token, nonDefaultsFilter));
return dbConn.Exec(dbCmd => dbCmd.DeleteNonDefaultsAsync(nonDefaultsFilter, token));
}

/// <summary>
Expand Down
30 changes: 21 additions & 9 deletions src/ServiceStack.OrmLite/OrmLiteWriteCommandExtensions.cs
Expand Up @@ -469,10 +469,17 @@ private static int AssertRowsUpdated(IDbCommand dbCmd, bool hadRowVersion)
return rowsUpdated;
}

internal static int Delete<T>(this IDbCommand dbCmd, T anonType)
{
return dbCmd.Delete<T>((object)anonType);
}

internal static int Delete<T>(this IDbCommand dbCmd, object anonType)
{
var dialectProvider = dbCmd.GetDialectProvider();
var hadRowVersion = dialectProvider.PrepareParameterizedDeleteStatement<T>(dbCmd, anonType.AllFields<T>());

var hadRowVersion = dialectProvider.PrepareParameterizedDeleteStatement<T>(
dbCmd, anonType.AllFieldsMap<T>());

dialectProvider.SetParameterValues<T>(dbCmd, anonType);

Expand All @@ -482,28 +489,29 @@ internal static int Delete<T>(this IDbCommand dbCmd, object anonType)
internal static int DeleteNonDefaults<T>(this IDbCommand dbCmd, T filter)
{
var dialectProvider = dbCmd.GetDialectProvider();
var hadRowVersion = dialectProvider.PrepareParameterizedDeleteStatement<T>(dbCmd, filter.NonDefaultFields<T>());
var hadRowVersion = dialectProvider.PrepareParameterizedDeleteStatement<T>(
dbCmd, filter.AllFieldsMap<T>().NonDefaultsOnly());

dialectProvider.SetParameterValues<T>(dbCmd, filter);

return AssertRowsUpdated(dbCmd, hadRowVersion);
}

internal static int Delete<T>(this IDbCommand dbCmd, params object[] objs)
internal static int Delete<T>(this IDbCommand dbCmd, T[] objs)
{
if (objs.Length == 0) return 0;

return DeleteAll<T>(dbCmd, objs[0].AllFields<T>(), objs);
return DeleteAll(dbCmd, objs);
}

internal static int DeleteNonDefaults<T>(this IDbCommand dbCmd, params T[] filters)
internal static int DeleteNonDefaults<T>(this IDbCommand dbCmd, T[] filters)
{
if (filters.Length == 0) return 0;

return DeleteAll<T>(dbCmd, filters[0].NonDefaultFields<T>(), filters.Map(x => (object)x));
return DeleteAll(dbCmd, filters, o => o.AllFieldsMap<T>().NonDefaultsOnly());
}

private static int DeleteAll<T>(IDbCommand dbCmd, ICollection<string> deleteFields, IEnumerable<object> objs)
private static int DeleteAll<T>(IDbCommand dbCmd, IEnumerable<T> objs, Func<object,Dictionary<string,object>> fieldValuesFn=null)
{
IDbTransaction dbTrans = null;

Expand All @@ -515,10 +523,14 @@ private static int DeleteAll<T>(IDbCommand dbCmd, ICollection<string> deleteFiel

var dialectProvider = dbCmd.GetDialectProvider();

dialectProvider.PrepareParameterizedDeleteStatement<T>(dbCmd, deleteFields);

foreach (var obj in objs)
{
var fieldValues = fieldValuesFn != null
? fieldValuesFn(obj)
: obj.AllFieldsMap<T>();

dialectProvider.PrepareParameterizedDeleteStatement<T>(dbCmd, fieldValues);

dialectProvider.SetParameterValues<T>(dbCmd, obj);

count += dbCmd.ExecNonQuery();
Expand Down
4 changes: 0 additions & 4 deletions tests/ServiceStack.OrmLite.Tests/ApiSqlServerTests.cs
Expand Up @@ -303,10 +303,6 @@ public void API_SqlServer_Examples()
db.Delete<Person>(new { FirstName = "Jimi", Age = 27 });
Assert.That(db.GetLastSql(), Is.EqualTo("DELETE FROM \"Person\" WHERE \"FirstName\"=@FirstName AND \"Age\"=@Age"));

db.Delete<Person>(new { FirstName = "Jimi", Age = 27 },
new { FirstName = "Janis", Age = 27 });
Assert.That(db.GetLastSql(), Is.EqualTo("DELETE FROM \"Person\" WHERE \"FirstName\"=@FirstName AND \"Age\"=@Age"));

db.Delete(new Person { Id = 1, FirstName = "Jimi", LastName = "Hendrix", Age = 27 });
Assert.That(db.GetLastSql(), Is.EqualTo("DELETE FROM \"Person\" WHERE \"Id\"=@Id AND \"FirstName\"=@FirstName AND \"LastName\"=@LastName AND \"Age\"=@Age"));

Expand Down
4 changes: 0 additions & 4 deletions tests/ServiceStack.OrmLite.Tests/ApiSqliteTests.cs
Expand Up @@ -308,10 +308,6 @@ public void API_Sqlite_Examples()
db.Delete<Person>(new { FirstName = "Jimi", Age = 27 });
Assert.That(db.GetLastSql(), Is.EqualTo("DELETE FROM \"Person\" WHERE \"FirstName\"=@FirstName AND \"Age\"=@Age"));

db.Delete<Person>(new { FirstName = "Jimi", Age = 27 },
new { FirstName = "Janis", Age = 27 });
Assert.That(db.GetLastSql(), Is.EqualTo("DELETE FROM \"Person\" WHERE \"FirstName\"=@FirstName AND \"Age\"=@Age"));

db.Delete(new Person { Id = 1, FirstName = "Jimi", LastName = "Hendrix", Age = 27 });
Assert.That(db.GetLastSql(), Is.EqualTo("DELETE FROM \"Person\" WHERE \"Id\"=@Id AND \"FirstName\"=@FirstName AND \"LastName\"=@LastName AND \"Age\"=@Age"));

Expand Down
4 changes: 2 additions & 2 deletions tests/ServiceStack.OrmLite.Tests/Issues/JoinsWithSchemas.cs
Expand Up @@ -4,15 +4,15 @@

namespace ServiceStack.OrmLite.Tests.Issues
{
[Schema("schema1")]
[Schema("Schema")]
public class Entity1
{
public int Id { get; set; }

public int Entity2Fk { get; set; }
}

[Schema("schema1")]
[Schema("Schema")]
public class Entity2
{
public int Id { get; set; }
Expand Down

0 comments on commit 2670b3c

Please sign in to comment.