This commit is contained in:
grzegorz.russek
2014-04-23 13:14:03 +00:00
parent 16c56adb8b
commit cfa38c290e
12 changed files with 374 additions and 119 deletions

View File

@@ -1,11 +1,12 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
# SharpDevelop 4.2.2.8818
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamORM", "DynamORM\DynamORM.csproj", "{63963ED7-9C78-4672-A4D4-339B6E825503}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamORM.Tests", "DynamORM.Tests\DynamORM.Tests.csproj", "{D5013B4E-8A1B-4DBB-8FB5-E09935F4F764}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AmalgamationTool", "AmalgamationTool\AmalgamationTool.csproj", "{A64D2052-D0CD-488E-BF05-E5952615D926}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -36,6 +37,16 @@ Global
{D5013B4E-8A1B-4DBB-8FB5-E09935F4F764}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{D5013B4E-8A1B-4DBB-8FB5-E09935F4F764}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{D5013B4E-8A1B-4DBB-8FB5-E09935F4F764}.Release|x86.ActiveCfg = Release|Any CPU
{A64D2052-D0CD-488E-BF05-E5952615D926}.Debug|Any CPU.ActiveCfg = Debug|x86
{A64D2052-D0CD-488E-BF05-E5952615D926}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
{A64D2052-D0CD-488E-BF05-E5952615D926}.Debug|Mixed Platforms.Build.0 = Debug|x86
{A64D2052-D0CD-488E-BF05-E5952615D926}.Debug|x86.ActiveCfg = Debug|x86
{A64D2052-D0CD-488E-BF05-E5952615D926}.Debug|x86.Build.0 = Debug|x86
{A64D2052-D0CD-488E-BF05-E5952615D926}.Release|Any CPU.ActiveCfg = Release|x86
{A64D2052-D0CD-488E-BF05-E5952615D926}.Release|Mixed Platforms.ActiveCfg = Release|x86
{A64D2052-D0CD-488E-BF05-E5952615D926}.Release|Mixed Platforms.Build.0 = Release|x86
{A64D2052-D0CD-488E-BF05-E5952615D926}.Release|x86.ActiveCfg = Release|x86
{A64D2052-D0CD-488E-BF05-E5952615D926}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -48,11 +48,6 @@ namespace DynamORM.Builders
/// <returns>This instance to permit chaining.</returns>
IDynamicInsertQueryBuilder Insert(params Func<dynamic, object>[] func);
/// <summary>Add insert fields.</summary>
/// <param name="column">Insert column and value.</param>
/// <returns>Builder instance.</returns>
IDynamicInsertQueryBuilder Insert(DynamicColumn column);
/// <summary>Add insert fields.</summary>
/// <param name="column">Insert column.</param>
/// <param name="value">Insert value.</param>

View File

@@ -70,20 +70,5 @@ namespace DynamORM.Builders
/// <summary>Gets or sets the on create real parameter action.</summary>
/// <remarks>This is exposed to allow modification of parameter.</remarks>
Action<IParameter, IDbDataParameter> OnCreateParameter { get; set; }
/// <summary>Creates sub query.</summary>
/// <returns>Sub query builder.</returns>
IDynamicSelectQueryBuilder SubQuery();
/// <summary>Adds to the 'From' clause of sub query the contents obtained by
/// parsing the dynamic lambda expressions given. The supported formats are:
/// <para>- Resolve to a string: 'x => "Table AS Alias', where the alias part is optional.</para>
/// <para>- Resolve to an expression: 'x => x.Table.As( x.Alias )', where the alias part is optional.</para>
/// <para>- Generic expression: 'x => x( expression ).As( x.Alias )', where the alias part is mandatory. In this
/// case the alias is not annotated.</para>
/// </summary>
/// <param name="func">The specification.</param>
/// <returns>This instance to permit chaining.</returns>
IDynamicSelectQueryBuilder SubQuery(params Func<dynamic, object>[] func);
}
}

View File

@@ -40,11 +40,6 @@ namespace DynamORM.Builders
#region Update
/// <summary>Add update value or where condition using schema.</summary>
/// <param name="column">Update or where column name and value.</param>
/// <returns>Builder instance.</returns>
IDynamicUpdateQueryBuilder Update(DynamicColumn column);
/// <summary>Add update value or where condition using schema.</summary>
/// <param name="column">Update or where column name.</param>
/// <param name="value">Column value.</param>
@@ -70,11 +65,6 @@ namespace DynamORM.Builders
/// <returns>This instance to permit chaining.</returns>
IDynamicUpdateQueryBuilder Values(params Func<dynamic, object>[] func);
/// <summary>Add insert fields.</summary>
/// <param name="column">Insert column and value.</param>
/// <returns>Builder instance.</returns>
IDynamicUpdateQueryBuilder Values(DynamicColumn column);
/// <summary>Add insert fields.</summary>
/// <param name="column">Insert column.</param>
/// <param name="value">Insert value.</param>

View File

@@ -136,22 +136,6 @@ namespace DynamORM.Builders.Implementation
return this;
}
/// <summary>Add insert fields.</summary>
/// <param name="column">Insert column and value.</param>
/// <returns>Builder instance.</returns>
public virtual IDynamicInsertQueryBuilder Insert(DynamicColumn column)
{
DynamicSchemaColumn? col = column.Schema ?? GetColumnFromSchema(column.ColumnName);
string main = FixObjectName(column.ColumnName, onlyColumn: true);
string value = Parse(column.Value, pars: Parameters, nulls: true, columnSchema: col);
_columns = _columns == null ? main : string.Format("{0}, {1}", _columns, main);
_values = _values == null ? value : string.Format("{0}, {1}", _values, value);
return this;
}
/// <summary>Add insert fields.</summary>
/// <param name="column">Insert column.</param>
/// <param name="value">Insert value.</param>
@@ -180,6 +164,20 @@ namespace DynamORM.Builders.Implementation
/// <returns>Builder instance.</returns>
public virtual IDynamicInsertQueryBuilder Insert(object o)
{
if (o is DynamicColumn)
{
var column = (DynamicColumn)o;
DynamicSchemaColumn? col = column.Schema ?? GetColumnFromSchema(column.ColumnName);
string main = FixObjectName(column.ColumnName, onlyColumn: true);
string value = Parse(column.Value, pars: Parameters, nulls: true, columnSchema: col);
_columns = _columns == null ? main : string.Format("{0}, {1}", _columns, main);
_values = _values == null ? value : string.Format("{0}, {1}", _values, value);
return this;
}
var dict = o.ToDictionary();
var mapper = DynamicMapperCache.GetMapper(o.GetType());

View File

@@ -269,31 +269,6 @@ namespace DynamORM.Builders.Implementation
}));
}
#region SubQuery
/// <summary>Creates sub query.</summary>
/// <returns>Sub query builder.</returns>
public IDynamicSelectQueryBuilder SubQuery()
{
return new DynamicSelectQueryBuilder(Database, this);
}
/// <summary>Adds to the 'From' clause of sub query the contents obtained by
/// parsing the dynamic lambda expressions given. The supported formats are:
/// <para>- Resolve to a string: 'x => "Table AS Alias', where the alias part is optional.</para>
/// <para>- Resolve to an expression: 'x => x.Table.As( x.Alias )', where the alias part is optional.</para>
/// <para>- Generic expression: 'x => x( expression ).As( x.Alias )', where the alias part is mandatory. In this
/// case the alias is not annotated.</para>
/// </summary>
/// <param name="func">The specification.</param>
/// <returns>This instance to permit chaining.</returns>
public IDynamicSelectQueryBuilder SubQuery(params Func<dynamic, object>[] func)
{
return SubQuery().From(func);
}
#endregion SubQuery
#endregion IDynamicQueryBuilder
#region Parser

View File

@@ -67,24 +67,6 @@ namespace DynamORM.Builders.Implementation
#region Update
/// <summary>Add update value or where condition using schema.</summary>
/// <param name="column">Update or where column name and value.</param>
/// <returns>Builder instance.</returns>
public virtual IDynamicUpdateQueryBuilder Update(DynamicColumn column)
{
DynamicSchemaColumn? col = column.Schema ?? GetColumnFromSchema(column.ColumnName);
if (!col.HasValue && SupportSchema)
throw new InvalidOperationException(string.Format("Column '{0}' not found in schema, can't use universal approach.", column));
if (col.HasValue && col.Value.IsKey)
Where(column);
else
Values(column.ColumnName, column.Value);
return this;
}
/// <summary>Add update value or where condition using schema.</summary>
/// <param name="column">Update or where column name.</param>
/// <param name="value">Column value.</param>
@@ -110,7 +92,21 @@ namespace DynamORM.Builders.Implementation
public virtual IDynamicUpdateQueryBuilder Update(object conditions)
{
if (conditions is DynamicColumn)
return Update((DynamicColumn)conditions);
{
var column = (DynamicColumn)conditions;
DynamicSchemaColumn? col = column.Schema ?? GetColumnFromSchema(column.ColumnName);
if (!col.HasValue && SupportSchema)
throw new InvalidOperationException(string.Format("Column '{0}' not found in schema, can't use universal approach.", column));
if (col.HasValue && col.Value.IsKey)
Where(column);
else
Values(column.ColumnName, column.Value);
return this;
}
var dict = conditions.ToDictionary();
var mapper = DynamicMapperCache.GetMapper(conditions.GetType());
@@ -205,22 +201,6 @@ namespace DynamORM.Builders.Implementation
return this;
}
/// <summary>Add insert fields.</summary>
/// <param name="column">Insert column and value.</param>
/// <returns>Builder instance.</returns>
public virtual IDynamicUpdateQueryBuilder Values(DynamicColumn column)
{
DynamicSchemaColumn? col = column.Schema ?? GetColumnFromSchema(column.ColumnName);
string main = FixObjectName(column.ColumnName, onlyColumn: true);
string value = Parse(column.Value, pars: Parameters, nulls: true, columnSchema: col);
var str = string.Format("{0} = {1}", main, value);
_columns = _columns == null ? str : string.Format("{0}, {1}", _columns, str);
return this;
}
/// <summary>Add insert fields.</summary>
/// <param name="column">Insert column.</param>
/// <param name="value">Insert value.</param>
@@ -249,6 +229,20 @@ namespace DynamORM.Builders.Implementation
/// <returns>Builder instance.</returns>
public virtual IDynamicUpdateQueryBuilder Values(object o)
{
if (o is DynamicColumn)
{
var column = (DynamicColumn)o;
DynamicSchemaColumn? col = column.Schema ?? GetColumnFromSchema(column.ColumnName);
string main = FixObjectName(column.ColumnName, onlyColumn: true);
string value = Parse(column.Value, pars: Parameters, nulls: true, columnSchema: col);
var str = string.Format("{0} = {1}", main, value);
_columns = _columns == null ? str : string.Format("{0}, {1}", _columns, str);
return this;
}
var dict = o.ToDictionary();
var mapper = DynamicMapperCache.GetMapper(o.GetType());

View File

@@ -378,10 +378,10 @@ namespace DynamORM
affected = 0;
var exCmd = new StringBuilder();
cmd.Dump(exCmd);
var problematicCommand = new StringBuilder();
cmd.Dump(problematicCommand);
throw new InvalidOperationException(exCmd.ToString(), ex);
throw new InvalidOperationException(problematicCommand.ToString(), ex);
}
}
}
@@ -413,7 +413,7 @@ namespace DynamORM
return new DynamicUpdateQueryBuilder(this).Table(typeof(T));
}
/// <summary>Bulk update objects into database.</summary>
/// <summary>Bulk update objects in database.</summary>
/// <typeparam name="T">Type of objects to update.</typeparam>
/// <param name="e">Enumerable containing instances of objects to update.</param>
/// <returns>Number of updated rows.</returns>
@@ -509,10 +509,192 @@ namespace DynamORM
affected = 0;
var exCmd = new StringBuilder();
cmd.Dump(exCmd);
var problematicCommand = new StringBuilder();
cmd.Dump(problematicCommand);
throw new InvalidOperationException(exCmd.ToString(), ex);
throw new InvalidOperationException(problematicCommand.ToString(), ex);
}
}
}
return affected;
}
/// <summary>Bulk update or insert objects into database.</summary>
/// <typeparam name="T">Type of objects to update or insert.</typeparam>
/// <param name="e">Enumerable containing instances of objects to update or insert.</param>
/// <returns>Number of updated or inserted rows.</returns>
public virtual int UpdateOrInsert<T>(IEnumerable<T> e) where T : class
{
int affected = 0;
var mapper = DynamicMapperCache.GetMapper(typeof(T));
if (mapper != null)
{
using (var con = Open())
using (var tra = con.BeginTransaction())
using (var cmdUp = con.CreateCommand())
using (var cmdIn = con.CreateCommand())
{
try
{
#region Update
var parametersUp = new Dictionary<IDbDataParameter, DynamicPropertyInvoker>();
if (!string.IsNullOrEmpty(mapper.UpdateCommandText))
{
cmdUp.CommandText = mapper.UpdateCommandText;
foreach (var col in mapper.ColumnsMap.Values
.Where(di => !di.Ignore && di.UpdateCommandParameter != null)
.OrderBy(di => di.UpdateCommandParameter.Ordinal))
{
var para = cmdUp.CreateParameter();
para.ParameterName = col.UpdateCommandParameter.Name;
para.DbType = col.UpdateCommandParameter.Type;
cmdUp.Parameters.Add(para);
parametersUp[para] = col;
}
}
else
{
DynamicPropertyInvoker currentprop = null;
var temp = new Dictionary<string, DynamicPropertyInvoker>();
int ord = 0;
var ib = Update<T>()
.SetVirtualMode(true)
.CreateTemporaryParameterAction(p => temp[p.Name] = currentprop)
.CreateParameterAction((p, cp) =>
{
parametersUp[cp] = temp[p.Name];
parametersUp[cp].UpdateCommandParameter = new DynamicPropertyInvoker.ParameterSpec
{
Name = cp.ParameterName,
Type = cp.DbType,
Ordinal = ord++,
};
});
foreach (var prop in mapper.PropertyMap)
if (!mapper.Ignored.Contains(prop.Key))
{
var col = mapper.PropertyMap.TryGetValue(prop.Key) ?? prop.Key;
currentprop = mapper.ColumnsMap.TryGetValue(col.ToLower());
if (currentprop.Ignore)
continue;
if (currentprop.Get != null)
{
if (currentprop.Column != null && currentprop.Column.IsKey)
ib.Where(col, null);
else
ib.Values(col, null);
}
}
ib.FillCommand(cmdUp);
// Cache command
mapper.UpdateCommandText = cmdUp.CommandText;
}
#endregion Update
#region Insert
var parametersIn = new Dictionary<IDbDataParameter, DynamicPropertyInvoker>();
if (!string.IsNullOrEmpty(mapper.InsertCommandText))
{
cmdIn.CommandText = mapper.InsertCommandText;
foreach (var col in mapper.ColumnsMap.Values
.Where(di => !di.Ignore && di.InsertCommandParameter != null)
.OrderBy(di => di.InsertCommandParameter.Ordinal))
{
var para = cmdIn.CreateParameter();
para.ParameterName = col.InsertCommandParameter.Name;
para.DbType = col.InsertCommandParameter.Type;
cmdIn.Parameters.Add(para);
parametersIn[para] = col;
}
}
else
{
DynamicPropertyInvoker currentprop = null;
var temp = new Dictionary<string, DynamicPropertyInvoker>();
int ord = 0;
var ib = Insert<T>()
.SetVirtualMode(true)
.CreateTemporaryParameterAction(p => temp[p.Name] = currentprop)
.CreateParameterAction((p, cp) =>
{
parametersIn[cp] = temp[p.Name];
parametersIn[cp].InsertCommandParameter = new DynamicPropertyInvoker.ParameterSpec
{
Name = cp.ParameterName,
Type = cp.DbType,
Ordinal = ord++,
};
});
foreach (var prop in mapper.PropertyMap)
if (!mapper.Ignored.Contains(prop.Key))
{
var col = mapper.PropertyMap.TryGetValue(prop.Key) ?? prop.Key;
currentprop = mapper.ColumnsMap.TryGetValue(col.ToLower());
if (currentprop.Ignore)
continue;
if (currentprop.Get != null)
ib.Insert(col, null);
}
ib.FillCommand(cmdIn);
// Cache command
mapper.InsertCommandText = cmdIn.CommandText;
}
#endregion Insert
foreach (var o in e)
{
foreach (var m in parametersUp)
m.Key.Value = m.Value.Get(o);
int a = cmdUp.ExecuteNonQuery();
if (a == 0)
{
foreach (var m in parametersIn)
m.Key.Value = m.Value.Get(o);
a = cmdIn.ExecuteNonQuery();
}
affected += a;
}
tra.Commit();
}
catch (Exception ex)
{
if (tra != null)
tra.Rollback();
affected = 0;
var problematicCommand = new StringBuilder();
cmdUp.Dump(problematicCommand);
throw new InvalidOperationException(problematicCommand.ToString(), ex);
}
}
}
@@ -544,6 +726,111 @@ namespace DynamORM
return new DynamicDeleteQueryBuilder(this).Table(typeof(T));
}
/// <summary>Bulk delete objects in database.</summary>
/// <typeparam name="T">Type of objects to delete.</typeparam>
/// <param name="e">Enumerable containing instances of objects to delete.</param>
/// <returns>Number of deleted rows.</returns>
public virtual int Delete<T>(IEnumerable<T> e) where T : class
{
int affected = 0;
var mapper = DynamicMapperCache.GetMapper(typeof(T));
if (mapper != null)
{
using (var con = Open())
using (var tra = con.BeginTransaction())
using (var cmd = con.CreateCommand())
{
try
{
var parameters = new Dictionary<IDbDataParameter, DynamicPropertyInvoker>();
if (!string.IsNullOrEmpty(mapper.DeleteCommandText))
{
cmd.CommandText = mapper.DeleteCommandText;
foreach (var col in mapper.ColumnsMap.Values
.Where(di => !di.Ignore && di.DeleteCommandParameter != null)
.OrderBy(di => di.DeleteCommandParameter.Ordinal))
{
var para = cmd.CreateParameter();
para.ParameterName = col.DeleteCommandParameter.Name;
para.DbType = col.DeleteCommandParameter.Type;
cmd.Parameters.Add(para);
parameters[para] = col;
}
}
else
{
DynamicPropertyInvoker currentprop = null;
var temp = new Dictionary<string, DynamicPropertyInvoker>();
int ord = 0;
var ib = Delete<T>()
.SetVirtualMode(true)
.CreateTemporaryParameterAction(p => temp[p.Name] = currentprop)
.CreateParameterAction((p, cp) =>
{
parameters[cp] = temp[p.Name];
parameters[cp].DeleteCommandParameter = new DynamicPropertyInvoker.ParameterSpec
{
Name = cp.ParameterName,
Type = cp.DbType,
Ordinal = ord++,
};
});
foreach (var prop in mapper.PropertyMap)
if (!mapper.Ignored.Contains(prop.Key))
{
var col = mapper.PropertyMap.TryGetValue(prop.Key) ?? prop.Key;
currentprop = mapper.ColumnsMap.TryGetValue(col.ToLower());
if (currentprop.Ignore)
continue;
if (currentprop.Get != null)
{
if (currentprop.Column != null && currentprop.Column.IsKey)
ib.Where(col, null);
}
}
ib.FillCommand(cmd);
// Cache command
mapper.DeleteCommandText = cmd.CommandText;
}
foreach (var o in e)
{
foreach (var m in parameters)
m.Key.Value = m.Value.Get(o);
affected += cmd.ExecuteNonQuery();
}
tra.Commit();
}
catch (Exception ex)
{
if (tra != null)
tra.Rollback();
affected = 0;
var problematicCommand = new StringBuilder();
cmd.Dump(problematicCommand);
throw new InvalidOperationException(problematicCommand.ToString(), ex);
}
}
}
return affected;
}
#endregion From/Insert/Update/Delete
#region Schema

View File

@@ -838,14 +838,25 @@ namespace DynamORM
return b;
}
/// <summary>Creates sub query that can be used inside of from/join/expresions.</summary>
/// <summary>Creates sub query that can be used inside of from/join/expressions.</summary>
/// <typeparam name="T">Class implementing <see cref="IDynamicQueryBuilder"/> interface.</typeparam>
/// <param name="b">The builder that will be parent of new sub query.</param>
/// <param name="subquery">First argument is parent query, second one is a subquery.</param>
/// <param name="func">The specification for subquery.</param>
/// <param name="func">The specification for sub query.</param>
/// <returns>Instance of sub query.</returns>
public static IDynamicSelectQueryBuilder SubQuery<T>(this T b, params Func<dynamic, object>[] func) where T : IDynamicQueryBuilder
{
return func == null || func.Length == 0 ? new DynamicSelectQueryBuilder(b.Database, b as DynamicQueryBuilder) : new DynamicSelectQueryBuilder(b.Database, b as DynamicQueryBuilder).From(func);
}
/// <summary>Creates sub query that can be used inside of from/join/expressions.</summary>
/// <typeparam name="T">Class implementing <see cref="IDynamicQueryBuilder"/> interface.</typeparam>
/// <param name="b">The builder that will be parent of new sub query.</param>
/// <param name="subquery">First argument is parent query, second one is a sub query.</param>
/// <param name="func">The specification for sub query.</param>
/// <returns>This instance to permit chaining.</returns>
public static T SubQuery<T>(this T b, Action<T, IDynamicSelectQueryBuilder> subquery, params Func<dynamic, object>[] func) where T : IDynamicQueryBuilder
{
var sub = func == null || func.Length == 0 ? b.SubQuery() : b.SubQuery(func);
var sub = b.SubQuery(func);
subquery(b, sub);

View File

@@ -122,6 +122,8 @@ namespace DynamORM.Mapper
internal ParameterSpec UpdateCommandParameter { get; set; }
internal ParameterSpec DeleteCommandParameter { get; set; }
#endregion Type command cache
}
}

View File

@@ -142,6 +142,8 @@ namespace DynamORM.Mapper
internal string UpdateCommandText { get; set; }
internal string DeleteCommandText { get; set; }
#endregion Type command cache
}
}

View File

@@ -64,4 +64,9 @@ using System.Runtime.InteropServices;
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.1.0.1")]
[assembly: AssemblyFileVersion("1.1.0.1")]
[assembly: InternalsVisibleTo("DynamORM.Tests")]
[assembly: InternalsVisibleTo("DynamORM.Tests")]
[assembly: Obfuscation(Feature = "encrypt symbol names with password #dr4cul4#", Exclude = false)]
[assembly: Obfuscation(Feature = "code control flow obfuscation", Exclude = false)]
[assembly: Obfuscation(Feature = "rename serializable symbols", Exclude = false)]
[assembly: Obfuscation(Feature = "anonymous type properties renaming", Exclude = true)]
[assembly: Obfuscation(Feature = "optimization", Exclude = true)]