@@ -99,6 +99,42 @@ namespace DynamORM.Tests.Select
|
|||||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS u WHERE ((u.\"Deleted\" = [${0}]) OR (u.\"IsActive\" = [${1}]))", cmd.Parameters.Keys.First(), cmd.Parameters.Keys.Last()), cmd.CommandText());
|
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS u WHERE ((u.\"Deleted\" = [${0}]) OR (u.\"IsActive\" = [${1}]))", cmd.Parameters.Keys.First(), cmd.Parameters.Keys.Last()), cmd.CommandText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests the where expression equal with brackets.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestWhereBracketsOrEq2()
|
||||||
|
{
|
||||||
|
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||||
|
|
||||||
|
cmd.From(x => x.dbo.Users.As(x.u))
|
||||||
|
.Where(new DynamicColumn("u.Id_User").Greater(1))
|
||||||
|
.Where(new DynamicColumn("u.Deleted").Eq(0).SetBeginBlock())
|
||||||
|
.Where(new DynamicColumn("u.IsActive").Eq(1).SetOr().SetEndBlock());
|
||||||
|
|
||||||
|
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS u WHERE (u.\"Id_User\" > [${0}]) AND ((u.\"Deleted\" = [${1}]) OR (u.\"IsActive\" = [${2}]))",
|
||||||
|
cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2]), cmd.CommandText());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests the where expression equal with brackets.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestWhereBracketsOrEqForgotToEnd()
|
||||||
|
{
|
||||||
|
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||||
|
|
||||||
|
cmd.From(x => x.dbo.Users.As(x.u))
|
||||||
|
.Where(new DynamicColumn("u.Id_User").Greater(1))
|
||||||
|
.Where(new DynamicColumn("u.Deleted").Eq(0).SetBeginBlock())
|
||||||
|
.Where(new DynamicColumn("u.IsActive").Eq(1).SetOr());
|
||||||
|
|
||||||
|
using (var con = Database.Open())
|
||||||
|
using (var c = con.CreateCommand())
|
||||||
|
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS u WHERE (u.\"Id_User\" > @0) AND ((u.\"Deleted\" = @1) OR (u.\"IsActive\" = @2))"),
|
||||||
|
c.SetCommand(cmd).CommandText);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tests the where expression not equal.
|
/// Tests the where expression not equal.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -616,6 +616,21 @@ namespace DynamORM.Tests.Select
|
|||||||
cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2]), cmd.CommandText());
|
cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2]), cmd.CommandText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests select escaped case.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestCoalesceEscaped()
|
||||||
|
{
|
||||||
|
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||||
|
|
||||||
|
cmd.From(u => u.dbo.Users.As(u.c))
|
||||||
|
.Select(u => u("COALESCE(", Database.DecorateName("ServerHash"), ", ", new byte[16] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, ")").As(u.Hash));
|
||||||
|
|
||||||
|
Assert.AreEqual(string.Format("SELECT COALESCE(\"ServerHash\", [${0}]) AS \"Hash\" FROM \"dbo\".\"Users\" AS c",
|
||||||
|
cmd.Parameters.Keys.ToArray()[0]), cmd.CommandText());
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tests select escaped case with sub query.
|
/// Tests select escaped case with sub query.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -34,12 +34,87 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using DynamORM.Builders.Implementation;
|
using DynamORM.Builders.Implementation;
|
||||||
using DynamORM.Helpers;
|
using DynamORM.Helpers;
|
||||||
|
using DynamORM.Helpers.Dynamics;
|
||||||
using DynamORM.Mapper;
|
using DynamORM.Mapper;
|
||||||
|
|
||||||
namespace DynamORM.Builders.Extensions
|
namespace DynamORM.Builders.Extensions
|
||||||
{
|
{
|
||||||
internal static class DynamicModifyBuilderExtensions
|
internal static class DynamicModifyBuilderExtensions
|
||||||
{
|
{
|
||||||
|
internal static T Table<T>(this T builder, Func<dynamic, object> func) where T : DynamicModifyBuilder
|
||||||
|
{
|
||||||
|
if (func == null)
|
||||||
|
throw new ArgumentNullException("Function cannot be null.");
|
||||||
|
|
||||||
|
using (var parser = DynamicParser.Parse(func))
|
||||||
|
{
|
||||||
|
var result = parser.Result;
|
||||||
|
|
||||||
|
// If the expression result is string.
|
||||||
|
if (result is string)
|
||||||
|
return builder.Table((string)result);
|
||||||
|
else if (result is Type)
|
||||||
|
return builder.Table((Type)result);
|
||||||
|
else if (result is DynamicParser.Node)
|
||||||
|
{
|
||||||
|
// Or if it resolves to a dynamic node
|
||||||
|
var node = (DynamicParser.Node)result;
|
||||||
|
|
||||||
|
string owner = null;
|
||||||
|
string main = null;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// Deny support for the AS() virtual method...
|
||||||
|
if (node is DynamicParser.Node.Method && ((DynamicParser.Node.Method)node).Name.ToUpper() == "AS")
|
||||||
|
throw new ArgumentException(string.Format("Alias is not supported on modification builders. (Parsing: {0})", result));
|
||||||
|
|
||||||
|
// Support for table specifications...
|
||||||
|
if (node is DynamicParser.Node.GetMember)
|
||||||
|
{
|
||||||
|
if (owner != null)
|
||||||
|
throw new ArgumentException(string.Format("Owner '{0}.{1}' is already set when parsing '{2}'.", owner, main, result));
|
||||||
|
|
||||||
|
if (main != null)
|
||||||
|
owner = ((DynamicParser.Node.GetMember)node).Name;
|
||||||
|
else
|
||||||
|
main = ((DynamicParser.Node.GetMember)node).Name;
|
||||||
|
|
||||||
|
node = node.Host;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Support for generic sources...
|
||||||
|
if (node is DynamicParser.Node.Invoke)
|
||||||
|
{
|
||||||
|
if (owner == null && main == null)
|
||||||
|
{
|
||||||
|
var invoke = (DynamicParser.Node.Invoke)node;
|
||||||
|
|
||||||
|
if (invoke.Arguments.Length == 1 && invoke.Arguments[0] is Type)
|
||||||
|
return builder.Table((Type)invoke.Arguments[0]);
|
||||||
|
else if (invoke.Arguments.Length == 1 && invoke.Arguments[0] is String)
|
||||||
|
return builder.Table((string)invoke.Arguments[0]);
|
||||||
|
else
|
||||||
|
throw new ArgumentException(string.Format("Invalid argument count or type when parsing '{2}'. Invoke supports only one argument of type Type or String", owner, main, result));
|
||||||
|
}
|
||||||
|
else if (owner != null)
|
||||||
|
throw new ArgumentException(string.Format("Owner '{0}.{1}' is already set when parsing '{2}'.", owner, main, result));
|
||||||
|
else if (main != null)
|
||||||
|
throw new ArgumentException(string.Format("Main '{0}' is already set when parsing '{1}'.", main, result));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(main))
|
||||||
|
return builder.Table(string.Format("{0}{1}",
|
||||||
|
string.IsNullOrEmpty(owner) ? string.Empty : string.Format("{0}.", owner),
|
||||||
|
main));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException(string.Format("Unable to set table parsing '{0}'", result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal static T Table<T>(this T builder, string tableName, Dictionary<string, DynamicSchemaColumn> schema = null) where T : DynamicModifyBuilder
|
internal static T Table<T>(this T builder, string tableName, Dictionary<string, DynamicSchemaColumn> schema = null) where T : DynamicModifyBuilder
|
||||||
{
|
{
|
||||||
var tuple = tableName.Validated("Table Name").SplitSomethingAndAlias();
|
var tuple = tableName.Validated("Table Name").SplitSomethingAndAlias();
|
||||||
@@ -49,6 +124,9 @@ namespace DynamORM.Builders.Extensions
|
|||||||
|
|
||||||
var parts = tuple.Item1.Split('.');
|
var parts = tuple.Item1.Split('.');
|
||||||
|
|
||||||
|
if (parts.Length > 2)
|
||||||
|
throw new ArgumentException(string.Format("Table name can consist only from name or owner and name. ({0})", tableName), "tableName");
|
||||||
|
|
||||||
builder.Tables.Clear();
|
builder.Tables.Clear();
|
||||||
builder.Tables.Add(new DynamicQueryBuilder.TableInfo(builder.Database,
|
builder.Tables.Add(new DynamicQueryBuilder.TableInfo(builder.Database,
|
||||||
builder.Database.StripName(parts.Last()).Validated("Table"), null,
|
builder.Database.StripName(parts.Last()).Validated("Table"), null,
|
||||||
@@ -62,6 +140,9 @@ namespace DynamORM.Builders.Extensions
|
|||||||
|
|
||||||
internal static T Table<T>(this T builder, Type type) where T : DynamicQueryBuilder
|
internal static T Table<T>(this T builder, Type type) where T : DynamicQueryBuilder
|
||||||
{
|
{
|
||||||
|
if (type.IsAnonymous())
|
||||||
|
throw new InvalidOperationException(string.Format("Cant assign anonymous type as a table ({0}).", type.FullName));
|
||||||
|
|
||||||
var mapper = DynamicMapperCache.GetMapper(type);
|
var mapper = DynamicMapperCache.GetMapper(type);
|
||||||
|
|
||||||
if (mapper == null)
|
if (mapper == null)
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ namespace DynamORM.Builders.Extensions
|
|||||||
#region Where
|
#region Where
|
||||||
|
|
||||||
internal static T InternalWhere<T>(this T builder, Func<dynamic, object> func) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
|
internal static T InternalWhere<T>(this T builder, Func<dynamic, object> func) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
|
||||||
|
{
|
||||||
|
return builder.InternalWhere(false, false, func);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static T InternalWhere<T>(this T builder, bool addBeginBrace, bool addEndBrace, Func<dynamic, object> func) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
|
||||||
{
|
{
|
||||||
if (func == null) throw new ArgumentNullException("Array of functions cannot be null.");
|
if (func == null) throw new ArgumentNullException("Array of functions cannot be null.");
|
||||||
|
|
||||||
@@ -88,8 +93,15 @@ namespace DynamORM.Builders.Extensions
|
|||||||
condition = builder.Parse(result, pars: builder.Parameters).Validated("Where condition");
|
condition = builder.Parse(result, pars: builder.Parameters).Validated("Where condition");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (builder.WhereCondition == null) builder.WhereCondition = condition;
|
if (addBeginBrace) builder.OpenBracketsCount++;
|
||||||
else builder.WhereCondition = string.Format("{0} {1} {2}", builder.WhereCondition, and ? "AND" : "OR", condition);
|
if (addEndBrace) builder.OpenBracketsCount--;
|
||||||
|
|
||||||
|
if (builder.WhereCondition == null)
|
||||||
|
builder.WhereCondition = string.Format("{0}{1}{2}",
|
||||||
|
addBeginBrace ? "(" : string.Empty, condition, addEndBrace ? ")" : string.Empty);
|
||||||
|
else
|
||||||
|
builder.WhereCondition = string.Format("{0} {1} {2}{3}{4}", builder.WhereCondition, and ? "AND" : "OR",
|
||||||
|
addBeginBrace ? "(" : string.Empty, condition, addEndBrace ? ")" : string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
@@ -97,54 +109,43 @@ namespace DynamORM.Builders.Extensions
|
|||||||
|
|
||||||
internal static T InternalWhere<T>(this T builder, DynamicColumn column) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
|
internal static T InternalWhere<T>(this T builder, DynamicColumn column) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
|
||||||
{
|
{
|
||||||
builder.VirtualMode = column.VirtualColumn;
|
bool virt = builder.VirtualMode;
|
||||||
bool prepend = false;
|
if (column.VirtualColumn.HasValue)
|
||||||
|
builder.VirtualMode = column.VirtualColumn.Value;
|
||||||
if (column.BeginBlock)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(builder.WhereCondition))
|
|
||||||
prepend = true;
|
|
||||||
else
|
|
||||||
builder.WhereCondition += " (";
|
|
||||||
}
|
|
||||||
|
|
||||||
// It's kind of uglu, but... well it works.
|
// It's kind of uglu, but... well it works.
|
||||||
if (column.Or)
|
if (column.Or)
|
||||||
switch (column.Operator)
|
switch (column.Operator)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
case DynamicColumn.CompareOperator.Eq: builder.InternalWhere(x => x.Or(x(builder.FixObjectName(column.ColumnName)) == column.Value)); break;
|
case DynamicColumn.CompareOperator.Eq: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) == column.Value)); break;
|
||||||
case DynamicColumn.CompareOperator.Not: builder.InternalWhere(x => x.Or(x(builder.FixObjectName(column.ColumnName)) != column.Value)); break;
|
case DynamicColumn.CompareOperator.Not: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) != column.Value)); break;
|
||||||
case DynamicColumn.CompareOperator.Like: builder.InternalWhere(x => x.Or(x(builder.FixObjectName(column.ColumnName)).Like(column.Value))); break;
|
case DynamicColumn.CompareOperator.Like: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).Like(column.Value))); break;
|
||||||
case DynamicColumn.CompareOperator.NotLike: builder.InternalWhere(x => x.Or(x(builder.FixObjectName(column.ColumnName)).NotLike(column.Value))); break;
|
case DynamicColumn.CompareOperator.NotLike: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).NotLike(column.Value))); break;
|
||||||
case DynamicColumn.CompareOperator.In: builder.InternalWhere(x => x.Or(x(builder.FixObjectName(column.ColumnName)).In(column.Value))); break;
|
case DynamicColumn.CompareOperator.In: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).In(column.Value))); break;
|
||||||
case DynamicColumn.CompareOperator.Lt: builder.InternalWhere(x => x.Or(x(builder.FixObjectName(column.ColumnName)) < column.Value)); break;
|
case DynamicColumn.CompareOperator.Lt: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) < column.Value)); break;
|
||||||
case DynamicColumn.CompareOperator.Lte: builder.InternalWhere(x => x.Or(x(builder.FixObjectName(column.ColumnName)) <= column.Value)); break;
|
case DynamicColumn.CompareOperator.Lte: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) <= column.Value)); break;
|
||||||
case DynamicColumn.CompareOperator.Gt: builder.InternalWhere(x => x.Or(x(builder.FixObjectName(column.ColumnName)) > column.Value)); break;
|
case DynamicColumn.CompareOperator.Gt: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) > column.Value)); break;
|
||||||
case DynamicColumn.CompareOperator.Gte: builder.InternalWhere(x => x.Or(x(builder.FixObjectName(column.ColumnName)) >= column.Value)); break;
|
case DynamicColumn.CompareOperator.Gte: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) >= column.Value)); break;
|
||||||
case DynamicColumn.CompareOperator.Between: builder.InternalWhere(x => x.Or(x(builder.FixObjectName(column.ColumnName)).Between(column.Value))); break;
|
case DynamicColumn.CompareOperator.Between: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).Between(column.Value))); break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
switch (column.Operator)
|
switch (column.Operator)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
case DynamicColumn.CompareOperator.Eq: builder.InternalWhere(x => x(builder.FixObjectName(column.ColumnName)) == column.Value); break;
|
case DynamicColumn.CompareOperator.Eq: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) == column.Value); break;
|
||||||
case DynamicColumn.CompareOperator.Not: builder.InternalWhere(x => x(builder.FixObjectName(column.ColumnName)) != column.Value); break;
|
case DynamicColumn.CompareOperator.Not: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) != column.Value); break;
|
||||||
case DynamicColumn.CompareOperator.Like: builder.InternalWhere(x => x(builder.FixObjectName(column.ColumnName)).Like(column.Value)); break;
|
case DynamicColumn.CompareOperator.Like: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).Like(column.Value)); break;
|
||||||
case DynamicColumn.CompareOperator.NotLike: builder.InternalWhere(x => x(builder.FixObjectName(column.ColumnName)).NotLike(column.Value)); break;
|
case DynamicColumn.CompareOperator.NotLike: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).NotLike(column.Value)); break;
|
||||||
case DynamicColumn.CompareOperator.In: builder.InternalWhere(x => x(builder.FixObjectName(column.ColumnName)).In(column.Value)); break;
|
case DynamicColumn.CompareOperator.In: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).In(column.Value)); break;
|
||||||
case DynamicColumn.CompareOperator.Lt: builder.InternalWhere(x => x(builder.FixObjectName(column.ColumnName)) < column.Value); break;
|
case DynamicColumn.CompareOperator.Lt: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) < column.Value); break;
|
||||||
case DynamicColumn.CompareOperator.Lte: builder.InternalWhere(x => x(builder.FixObjectName(column.ColumnName)) <= column.Value); break;
|
case DynamicColumn.CompareOperator.Lte: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) <= column.Value); break;
|
||||||
case DynamicColumn.CompareOperator.Gt: builder.InternalWhere(x => x(builder.FixObjectName(column.ColumnName)) > column.Value); break;
|
case DynamicColumn.CompareOperator.Gt: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) > column.Value); break;
|
||||||
case DynamicColumn.CompareOperator.Gte: builder.InternalWhere(x => x(builder.FixObjectName(column.ColumnName)) >= column.Value); break;
|
case DynamicColumn.CompareOperator.Gte: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) >= column.Value); break;
|
||||||
case DynamicColumn.CompareOperator.Between: builder.InternalWhere(x => x(builder.FixObjectName(column.ColumnName)).Between(column.Value)); break;
|
case DynamicColumn.CompareOperator.Between: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).Between(column.Value)); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prepend)
|
builder.VirtualMode = virt;
|
||||||
builder.WhereCondition = string.Format("({0}", builder.WhereCondition);
|
|
||||||
|
|
||||||
if (column.EndBlock)
|
|
||||||
builder.WhereCondition += ")";
|
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ namespace DynamORM.Builders
|
|||||||
/// <summary>Gets the tables used in this builder.</summary>
|
/// <summary>Gets the tables used in this builder.</summary>
|
||||||
IDictionary<string, IParameter> Parameters { get; }
|
IDictionary<string, IParameter> Parameters { get; }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets a value indicating whether add virtual parameters.</summary>
|
||||||
|
bool VirtualMode { get; set; }
|
||||||
|
|
||||||
/// <summary>Gets a value indicating whether database supports standard schema.</summary>
|
/// <summary>Gets a value indicating whether database supports standard schema.</summary>
|
||||||
bool SupportSchema { get; }
|
bool SupportSchema { get; }
|
||||||
|
|
||||||
@@ -60,6 +63,14 @@ namespace DynamORM.Builders
|
|||||||
/// <remarks>This method must be override by derived classes.</remarks>
|
/// <remarks>This method must be override by derived classes.</remarks>
|
||||||
string CommandText();
|
string CommandText();
|
||||||
|
|
||||||
|
/// <summary>Gets or sets the on create temporary parameter action.</summary>
|
||||||
|
/// <remarks>This is exposed to allow setting schema of column.</remarks>
|
||||||
|
Action<IParameter> OnCreateTemporaryParameter { get; set; }
|
||||||
|
|
||||||
|
/// <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>
|
/// <summary>Creates sub query.</summary>
|
||||||
/// <returns>Sub query builder.</returns>
|
/// <returns>Sub query builder.</returns>
|
||||||
IDynamicSelectQueryBuilder SubQuery();
|
IDynamicSelectQueryBuilder SubQuery();
|
||||||
|
|||||||
@@ -31,16 +31,23 @@ namespace DynamORM.Builders
|
|||||||
/// <summary>Interface describing parameter info.</summary>
|
/// <summary>Interface describing parameter info.</summary>
|
||||||
public interface IParameter
|
public interface IParameter
|
||||||
{
|
{
|
||||||
|
/// <summary>Gets the parameter position in command.</summary>
|
||||||
|
/// <remarks>Available after filling the command.</remarks>
|
||||||
|
int Ordinal { get; }
|
||||||
|
|
||||||
/// <summary>Gets the parameter temporary name.</summary>
|
/// <summary>Gets the parameter temporary name.</summary>
|
||||||
string Name { get; }
|
string Name { get; }
|
||||||
|
|
||||||
/// <summary>Gets or sets the parameter value.</summary>
|
/// <summary>Gets or sets the parameter value.</summary>
|
||||||
object Value { get; set; }
|
object Value { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets a value indicating whether name of temporary parameter is well known.</summary>
|
||||||
|
bool WellKnown { get; set; }
|
||||||
|
|
||||||
/// <summary>Gets or sets a value indicating whether this <see cref="Parameter"/> is virtual.</summary>
|
/// <summary>Gets or sets a value indicating whether this <see cref="Parameter"/> is virtual.</summary>
|
||||||
bool Virtual { get; set; }
|
bool Virtual { get; set; }
|
||||||
|
|
||||||
/// <summary>Gets the parameter schema information.</summary>
|
/// <summary>Gets or sets the parameter schema information.</summary>
|
||||||
DynamicSchemaColumn? Schema { get; }
|
DynamicSchemaColumn? Schema { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,9 +63,11 @@ namespace DynamORM.Builders.Implementation
|
|||||||
public override string CommandText()
|
public override string CommandText()
|
||||||
{
|
{
|
||||||
var info = Tables.Single();
|
var info = Tables.Single();
|
||||||
return string.Format("DELETE FROM {0}{1} WHERE {2}",
|
return string.Format("DELETE FROM {0}{1}{2}{3}",
|
||||||
string.IsNullOrEmpty(info.Owner) ? string.Empty : string.Format("{0}.", Database.DecorateName(info.Owner)),
|
string.IsNullOrEmpty(info.Owner) ? string.Empty : string.Format("{0}.", Database.DecorateName(info.Owner)),
|
||||||
Database.DecorateName(info.Name), WhereCondition);
|
Database.DecorateName(info.Name),
|
||||||
|
string.IsNullOrEmpty(WhereCondition) ? string.Empty : " WHERE ",
|
||||||
|
WhereCondition);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Where
|
#region Where
|
||||||
|
|||||||
@@ -45,13 +45,17 @@ namespace DynamORM.Builders.Implementation
|
|||||||
internal abstract class DynamicQueryBuilder : IDynamicQueryBuilder
|
internal abstract class DynamicQueryBuilder : IDynamicQueryBuilder
|
||||||
{
|
{
|
||||||
/// <summary>Empty interface to allow where query builder implementation use universal approach.</summary>
|
/// <summary>Empty interface to allow where query builder implementation use universal approach.</summary>
|
||||||
internal interface IQueryWithWhere { }
|
internal interface IQueryWithWhere
|
||||||
|
{
|
||||||
|
/// <summary>Gets or sets the where condition.</summary>
|
||||||
|
string WhereCondition { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets the amount of not closed brackets in where statement.</summary>
|
||||||
|
int OpenBracketsCount { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
private DynamicQueryBuilder _parent = null;
|
private DynamicQueryBuilder _parent = null;
|
||||||
|
|
||||||
/// <summary>Gets or sets a value indicating whether add virtual.</summary>
|
|
||||||
internal bool VirtualMode { get; set; }
|
|
||||||
|
|
||||||
#region TableInfo
|
#region TableInfo
|
||||||
|
|
||||||
/// <summary>Table information.</summary>
|
/// <summary>Table information.</summary>
|
||||||
@@ -137,34 +141,29 @@ namespace DynamORM.Builders.Implementation
|
|||||||
/// <summary>Interface describing parameter info.</summary>
|
/// <summary>Interface describing parameter info.</summary>
|
||||||
internal class Parameter : IParameter
|
internal class Parameter : IParameter
|
||||||
{
|
{
|
||||||
|
/// <summary>Gets or sets the parameter position in command.</summary>
|
||||||
|
/// <remarks>Available after filling the command.</remarks>
|
||||||
|
public int Ordinal { get; internal set; }
|
||||||
|
|
||||||
/// <summary>Gets or sets the parameter temporary name.</summary>
|
/// <summary>Gets or sets the parameter temporary name.</summary>
|
||||||
public string Name { get; internal set; }
|
public string Name { get; internal set; }
|
||||||
|
|
||||||
/// <summary>Gets or sets the parameter value.</summary>
|
/// <summary>Gets or sets the parameter value.</summary>
|
||||||
public object Value { get; set; }
|
public object Value { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets a value indicating whether name of temporary parameter is well known.</summary>
|
||||||
|
public bool WellKnown { get; set; }
|
||||||
|
|
||||||
/// <summary>Gets or sets a value indicating whether this <see cref="Parameter"/> is virtual.</summary>
|
/// <summary>Gets or sets a value indicating whether this <see cref="Parameter"/> is virtual.</summary>
|
||||||
public bool Virtual { get; set; }
|
public bool Virtual { get; set; }
|
||||||
|
|
||||||
/// <summary>Gets or sets the parameter schema information.</summary>
|
/// <summary>Gets or sets the parameter schema information.</summary>
|
||||||
public DynamicSchemaColumn? Schema { get; internal set; }
|
public DynamicSchemaColumn? Schema { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Parameter
|
#endregion Parameter
|
||||||
|
|
||||||
internal string WhereCondition { get; set; }
|
#region Constructor
|
||||||
|
|
||||||
/// <summary>Gets <see cref="DynamicDatabase"/> instance.</summary>
|
|
||||||
public DynamicDatabase Database { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>Gets the tables used in this builder.</summary>
|
|
||||||
public IList<ITableInfo> Tables { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>Gets the tables used in this builder.</summary>
|
|
||||||
public IDictionary<string, IParameter> Parameters { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>Gets a value indicating whether database supports standard schema.</summary>
|
|
||||||
public bool SupportSchema { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="DynamicQueryBuilder"/> class.
|
/// Initializes a new instance of the <see cref="DynamicQueryBuilder"/> class.
|
||||||
@@ -176,6 +175,9 @@ namespace DynamORM.Builders.Implementation
|
|||||||
Tables = new List<ITableInfo>();
|
Tables = new List<ITableInfo>();
|
||||||
Parameters = new Dictionary<string, IParameter>();
|
Parameters = new Dictionary<string, IParameter>();
|
||||||
|
|
||||||
|
WhereCondition = null;
|
||||||
|
OpenBracketsCount = 0;
|
||||||
|
|
||||||
Database = db;
|
Database = db;
|
||||||
SupportSchema = (db.Options & DynamicDatabaseOptions.SupportSchema) == DynamicDatabaseOptions.SupportSchema;
|
SupportSchema = (db.Options & DynamicDatabaseOptions.SupportSchema) == DynamicDatabaseOptions.SupportSchema;
|
||||||
}
|
}
|
||||||
@@ -189,37 +191,85 @@ namespace DynamORM.Builders.Implementation
|
|||||||
_parent = parent;
|
_parent = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal bool IsTableAlias(string name)
|
#endregion Constructor
|
||||||
|
|
||||||
|
#region IQueryWithWhere
|
||||||
|
|
||||||
|
/// <summary>Gets or sets the where condition.</summary>
|
||||||
|
public string WhereCondition { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets the amount of not closed brackets in where statement.</summary>
|
||||||
|
public int OpenBracketsCount { get; set; }
|
||||||
|
|
||||||
|
#endregion IQueryWithWhere
|
||||||
|
|
||||||
|
#region IDynamicQueryBuilder
|
||||||
|
|
||||||
|
/// <summary>Gets <see cref="DynamicDatabase"/> instance.</summary>
|
||||||
|
public DynamicDatabase Database { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets the tables used in this builder.</summary>
|
||||||
|
public IList<ITableInfo> Tables { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets the tables used in this builder.</summary>
|
||||||
|
public IDictionary<string, IParameter> Parameters { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets a value indicating whether add virtual parameters.</summary>
|
||||||
|
public bool VirtualMode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets the on create temporary parameter action.</summary>
|
||||||
|
/// <remarks>This is exposed to allow setting schema of column.</remarks>
|
||||||
|
public Action<IParameter> OnCreateTemporaryParameter { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Gets or sets the on create real parameter action.</summary>
|
||||||
|
/// <remarks>This is exposed to allow modification of parameter.</remarks>
|
||||||
|
public Action<IParameter, IDbDataParameter> OnCreateParameter { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Gets a value indicating whether database supports standard schema.</summary>
|
||||||
|
public bool SupportSchema { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates the text this command will execute against the underlying database.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The text to execute against the underlying database.</returns>
|
||||||
|
/// <remarks>This method must be override by derived classes.</remarks>
|
||||||
|
public abstract string CommandText();
|
||||||
|
|
||||||
|
/// <summary>Fill command with query.</summary>
|
||||||
|
/// <param name="command">Command to fill.</param>
|
||||||
|
/// <returns>Filled instance of <see cref="IDbCommand"/>.</returns>
|
||||||
|
public virtual IDbCommand FillCommand(IDbCommand command)
|
||||||
{
|
{
|
||||||
DynamicQueryBuilder builder = this;
|
// End not ended where statement
|
||||||
|
if (this is IQueryWithWhere)
|
||||||
while (builder != null)
|
|
||||||
{
|
{
|
||||||
if (builder.Tables.Any(t => t.Alias == name))
|
while (OpenBracketsCount > 0)
|
||||||
return true;
|
{
|
||||||
|
WhereCondition += ")";
|
||||||
builder = builder._parent;
|
OpenBracketsCount--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return command.SetCommand(CommandText()
|
||||||
|
.FillStringWithVariables(s =>
|
||||||
|
{
|
||||||
|
return Parameters.TryGetValue(s).NullOr(p =>
|
||||||
|
{
|
||||||
|
IDbDataParameter param = (IDbDataParameter)command
|
||||||
|
.AddParameter(this, p.Schema, p.Value)
|
||||||
|
.Parameters[command.Parameters.Count - 1];
|
||||||
|
|
||||||
|
(p as Parameter).Ordinal = command.Parameters.Count - 1;
|
||||||
|
|
||||||
|
if (OnCreateParameter != null)
|
||||||
|
OnCreateParameter(p, param);
|
||||||
|
|
||||||
|
return param.ParameterName;
|
||||||
|
}, s);
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal bool IsTable(string name, string owner)
|
#region SubQuery
|
||||||
{
|
|
||||||
DynamicQueryBuilder builder = this;
|
|
||||||
|
|
||||||
while (builder != null)
|
|
||||||
{
|
|
||||||
if ((string.IsNullOrEmpty(owner) && builder.Tables.Any(t => t.Name.ToLower() == name.ToLower())) ||
|
|
||||||
(!string.IsNullOrEmpty(owner) && builder.Tables.Any(t => t.Name.ToLower() == name.ToLower() &&
|
|
||||||
!string.IsNullOrEmpty(t.Owner) && t.Owner.ToLower() == owner.ToLower())))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
builder = builder._parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Creates sub query.</summary>
|
/// <summary>Creates sub query.</summary>
|
||||||
/// <returns>Sub query builder.</returns>
|
/// <returns>Sub query builder.</returns>
|
||||||
@@ -242,71 +292,9 @@ namespace DynamORM.Builders.Implementation
|
|||||||
return SubQuery().From(func);
|
return SubQuery().From(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
#endregion SubQuery
|
||||||
/// Generates the text this command will execute against the underlying database.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The text to execute against the underlying database.</returns>
|
|
||||||
/// <remarks>This method must be override by derived classes.</remarks>
|
|
||||||
public abstract string CommandText();
|
|
||||||
|
|
||||||
/// <summary>Fill command with query.</summary>
|
#endregion IDynamicQueryBuilder
|
||||||
/// <param name="command">Command to fill.</param>
|
|
||||||
/// <returns>Filled instance of <see cref="IDbCommand"/>.</returns>
|
|
||||||
public virtual IDbCommand FillCommand(IDbCommand command)
|
|
||||||
{
|
|
||||||
return command.SetCommand(CommandText()
|
|
||||||
.FillStringWithVariables(s =>
|
|
||||||
{
|
|
||||||
return Parameters.TryGetValue(s).NullOr(p =>
|
|
||||||
{
|
|
||||||
return ((IDbDataParameter)command
|
|
||||||
.AddParameter(this, p.Schema, p.Value)
|
|
||||||
.Parameters[command.Parameters.Count - 1])
|
|
||||||
.ParameterName;
|
|
||||||
}, s);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
internal DynamicSchemaColumn? GetColumnFromSchema(string colName, DynamicTypeMap mapper = null, string table = null)
|
|
||||||
{
|
|
||||||
// This is tricky and will not always work unfortunetly.
|
|
||||||
if (colName.ContainsAny(StringExtensions.InvalidMultipartMemberChars))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// First we need to get real column name and it's owner if exist.
|
|
||||||
var parts = colName.Split('.')
|
|
||||||
.Select(c => Database.StripName(c))
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
var columnName = parts.Last();
|
|
||||||
|
|
||||||
// Get table name from mapper
|
|
||||||
string tableName = table;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(tableName))
|
|
||||||
{
|
|
||||||
tableName = (mapper != null && mapper.Table != null) ? mapper.Table.Name : string.Empty;
|
|
||||||
|
|
||||||
if (parts.Length > 1 && string.IsNullOrEmpty(tableName))
|
|
||||||
{
|
|
||||||
// OK, we have a multi part identifier, that's good, we can get table name
|
|
||||||
tableName = string.Join(".", parts.Take(parts.Length - 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to get table info from cache
|
|
||||||
var tableInfo = !string.IsNullOrEmpty(tableName) ?
|
|
||||||
Tables.FirstOrDefault(x => !string.IsNullOrEmpty(x.Alias) && x.Alias.ToLower() == tableName) ??
|
|
||||||
Tables.FirstOrDefault(x => x.Name.ToLower() == tableName.ToLower()) ?? Tables.FirstOrDefault() :
|
|
||||||
this is DynamicModifyBuilder ? Tables.FirstOrDefault() : null;
|
|
||||||
|
|
||||||
// Try to get column from schema
|
|
||||||
if (tableInfo != null && tableInfo.Schema != null)
|
|
||||||
return tableInfo.Schema.TryGetNullable(columnName.ToLower());
|
|
||||||
|
|
||||||
// Well, we failed to find a column
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Parser
|
#region Parser
|
||||||
|
|
||||||
@@ -333,14 +321,14 @@ namespace DynamORM.Builders.Implementation
|
|||||||
if (!nulls)
|
if (!nulls)
|
||||||
throw new ArgumentNullException("node", "Null nodes are not accepted.");
|
throw new ArgumentNullException("node", "Null nodes are not accepted.");
|
||||||
|
|
||||||
return Dispatch(node, pars, decorate);
|
return Dispatch(node, pars, decorate, columnSchema: columnSchema);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nodes that are strings are parametrized or not depending the "rawstr" flag...
|
// Nodes that are strings are parametrized or not depending the "rawstr" flag...
|
||||||
if (node is string)
|
if (node is string)
|
||||||
{
|
{
|
||||||
if (rawstr) return (string)node;
|
if (rawstr) return (string)node;
|
||||||
else return Dispatch(node, pars, decorate);
|
else return Dispatch(node, pars, decorate, columnSchema: columnSchema);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If node is a delegate, parse it to create the logical tree...
|
// If node is a delegate, parse it to create the logical tree...
|
||||||
@@ -689,23 +677,30 @@ namespace DynamORM.Builders.Implementation
|
|||||||
|
|
||||||
protected virtual string ParseConstant(object node, IDictionary<string, IParameter> pars = null, DynamicSchemaColumn? columnSchema = null)
|
protected virtual string ParseConstant(object node, IDictionary<string, IParameter> pars = null, DynamicSchemaColumn? columnSchema = null)
|
||||||
{
|
{
|
||||||
if (node == null) return ParseNull();
|
if (node == null && !VirtualMode)
|
||||||
|
return ParseNull();
|
||||||
|
|
||||||
if (pars != null)
|
if (pars != null)
|
||||||
{
|
{
|
||||||
|
bool wellKnownName = VirtualMode && node is String && ((String)node).StartsWith("[$") && ((String)node).EndsWith("]") && ((String)node).Length > 4;
|
||||||
|
|
||||||
// If we have a list of parameters to store it, let's parametrize it
|
// If we have a list of parameters to store it, let's parametrize it
|
||||||
var name = Guid.NewGuid().ToString();
|
|
||||||
var par = new Parameter()
|
var par = new Parameter()
|
||||||
{
|
{
|
||||||
Name = string.Format("[${0}]", name),
|
Name = wellKnownName ? ((String)node).Substring(2, ((String)node).Length - 3) : Guid.NewGuid().ToString(),
|
||||||
Value = node,
|
Value = wellKnownName ? null : node,
|
||||||
|
WellKnown = wellKnownName,
|
||||||
Virtual = VirtualMode,
|
Virtual = VirtualMode,
|
||||||
Schema = columnSchema,
|
Schema = columnSchema,
|
||||||
};
|
};
|
||||||
|
|
||||||
pars.Add(name, par);
|
// If we are adding parameter we inform external sources about this.
|
||||||
|
if (OnCreateTemporaryParameter != null)
|
||||||
|
OnCreateTemporaryParameter(par);
|
||||||
|
|
||||||
return par.Name;
|
pars.Add(par.Name, par);
|
||||||
|
|
||||||
|
return string.Format("[${0}]", par.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return node.ToString(); // Last resort case
|
return node.ToString(); // Last resort case
|
||||||
@@ -716,6 +711,42 @@ namespace DynamORM.Builders.Implementation
|
|||||||
return "NULL"; // Override if needed
|
return "NULL"; // Override if needed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion Parser
|
||||||
|
|
||||||
|
#region Helpers
|
||||||
|
|
||||||
|
internal bool IsTableAlias(string name)
|
||||||
|
{
|
||||||
|
DynamicQueryBuilder builder = this;
|
||||||
|
|
||||||
|
while (builder != null)
|
||||||
|
{
|
||||||
|
if (builder.Tables.Any(t => t.Alias == name))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
builder = builder._parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool IsTable(string name, string owner)
|
||||||
|
{
|
||||||
|
DynamicQueryBuilder builder = this;
|
||||||
|
|
||||||
|
while (builder != null)
|
||||||
|
{
|
||||||
|
if ((string.IsNullOrEmpty(owner) && builder.Tables.Any(t => t.Name.ToLower() == name.ToLower())) ||
|
||||||
|
(!string.IsNullOrEmpty(owner) && builder.Tables.Any(t => t.Name.ToLower() == name.ToLower() &&
|
||||||
|
!string.IsNullOrEmpty(t.Owner) && t.Owner.ToLower() == owner.ToLower())))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
builder = builder._parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
internal string FixObjectName(string main, bool onlyColumn = false)
|
internal string FixObjectName(string main, bool onlyColumn = false)
|
||||||
{
|
{
|
||||||
if (main.IndexOf("(") > 0 && main.IndexOf(")") > 0)
|
if (main.IndexOf("(") > 0 && main.IndexOf(")") > 0)
|
||||||
@@ -739,6 +770,47 @@ namespace DynamORM.Builders.Implementation
|
|||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Parser
|
internal DynamicSchemaColumn? GetColumnFromSchema(string colName, DynamicTypeMap mapper = null, string table = null)
|
||||||
|
{
|
||||||
|
// This is tricky and will not always work unfortunetly.
|
||||||
|
if (colName.ContainsAny(StringExtensions.InvalidMultipartMemberChars))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// First we need to get real column name and it's owner if exist.
|
||||||
|
var parts = colName.Split('.')
|
||||||
|
.Select(c => Database.StripName(c))
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
var columnName = parts.Last();
|
||||||
|
|
||||||
|
// Get table name from mapper
|
||||||
|
string tableName = table;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(tableName))
|
||||||
|
{
|
||||||
|
tableName = (mapper != null && mapper.Table != null) ? mapper.Table.Name : string.Empty;
|
||||||
|
|
||||||
|
if (parts.Length > 1 && string.IsNullOrEmpty(tableName))
|
||||||
|
{
|
||||||
|
// OK, we have a multi part identifier, that's good, we can get table name
|
||||||
|
tableName = string.Join(".", parts.Take(parts.Length - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to get table info from cache
|
||||||
|
var tableInfo = !string.IsNullOrEmpty(tableName) ?
|
||||||
|
Tables.FirstOrDefault(x => !string.IsNullOrEmpty(x.Alias) && x.Alias.ToLower() == tableName) ??
|
||||||
|
Tables.FirstOrDefault(x => x.Name.ToLower() == tableName.ToLower()) ?? Tables.FirstOrDefault() :
|
||||||
|
this is DynamicModifyBuilder ? Tables.FirstOrDefault() : null;
|
||||||
|
|
||||||
|
// Try to get column from schema
|
||||||
|
if (tableInfo != null && tableInfo.Schema != null)
|
||||||
|
return tableInfo.Schema.TryGetNullable(columnName.ToLower());
|
||||||
|
|
||||||
|
// Well, we failed to find a column
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Helpers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -244,7 +244,8 @@ namespace DynamORM.Builders.Implementation
|
|||||||
/// <returns>This instance to permit chaining.</returns>
|
/// <returns>This instance to permit chaining.</returns>
|
||||||
public virtual IDynamicSelectQueryBuilder From(params Func<dynamic, object>[] func)
|
public virtual IDynamicSelectQueryBuilder From(params Func<dynamic, object>[] func)
|
||||||
{
|
{
|
||||||
if (func == null) throw new ArgumentNullException("Array of functions cannot be null.");
|
if (func == null)
|
||||||
|
throw new ArgumentNullException("Array of functions cannot be null.");
|
||||||
|
|
||||||
int index = -1;
|
int index = -1;
|
||||||
foreach (var f in func)
|
foreach (var f in func)
|
||||||
@@ -266,6 +267,19 @@ namespace DynamORM.Builders.Implementation
|
|||||||
tuple.Item2.Validated("Alias", canbeNull: true),
|
tuple.Item2.Validated("Alias", canbeNull: true),
|
||||||
parts.Length == 2 ? Database.StripName(parts.First()).Validated("Owner", canbeNull: true) : null);
|
parts.Length == 2 ? Database.StripName(parts.First()).Validated("Owner", canbeNull: true) : null);
|
||||||
}
|
}
|
||||||
|
else if (result is Type)
|
||||||
|
{
|
||||||
|
Type type = (Type)result;
|
||||||
|
if (type.IsAnonymous())
|
||||||
|
throw new InvalidOperationException(string.Format("Cant assign anonymous type as a table ({0}). Parsing {1}", type.FullName, result));
|
||||||
|
|
||||||
|
var mapper = DynamicMapperCache.GetMapper(type);
|
||||||
|
|
||||||
|
if (mapper == null)
|
||||||
|
throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}). Parsing {1}", type.FullName, result));
|
||||||
|
|
||||||
|
tableInfo = new TableInfo(Database, type);
|
||||||
|
}
|
||||||
else if (result is DynamicParser.Node)
|
else if (result is DynamicParser.Node)
|
||||||
{
|
{
|
||||||
// Or if it resolves to a dynamic node
|
// Or if it resolves to a dynamic node
|
||||||
@@ -327,10 +341,13 @@ namespace DynamORM.Builders.Implementation
|
|||||||
if (invoke.Arguments.Length == 1 && invoke.Arguments[0] is Type)
|
if (invoke.Arguments.Length == 1 && invoke.Arguments[0] is Type)
|
||||||
{
|
{
|
||||||
type = (Type)invoke.Arguments[0];
|
type = (Type)invoke.Arguments[0];
|
||||||
|
if (type.IsAnonymous())
|
||||||
|
throw new InvalidOperationException(string.Format("Cant assign anonymous type as a table ({0}). Parsing {1}", type.FullName, result));
|
||||||
|
|
||||||
var mapper = DynamicMapperCache.GetMapper(type);
|
var mapper = DynamicMapperCache.GetMapper(type);
|
||||||
|
|
||||||
if (mapper == null)
|
if (mapper == null)
|
||||||
throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", type.FullName));
|
throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}). Parsing {1}", type.FullName, result));
|
||||||
|
|
||||||
main = mapper.Table == null || string.IsNullOrEmpty(mapper.Table.Name) ?
|
main = mapper.Table == null || string.IsNullOrEmpty(mapper.Table.Name) ?
|
||||||
mapper.Type.Name : mapper.Table.Name;
|
mapper.Type.Name : mapper.Table.Name;
|
||||||
|
|||||||
@@ -58,9 +58,11 @@ namespace DynamORM.Builders.Implementation
|
|||||||
public override string CommandText()
|
public override string CommandText()
|
||||||
{
|
{
|
||||||
var info = Tables.Single();
|
var info = Tables.Single();
|
||||||
return string.Format("UPDATE {0}{1} SET {2} WHERE {3}",
|
return string.Format("UPDATE {0}{1} SET {2}{3}{4}",
|
||||||
string.IsNullOrEmpty(info.Owner) ? string.Empty : string.Format("{0}.", Database.DecorateName(info.Owner)),
|
string.IsNullOrEmpty(info.Owner) ? string.Empty : string.Format("{0}.", Database.DecorateName(info.Owner)),
|
||||||
Database.DecorateName(info.Name), _columns, WhereCondition);
|
Database.DecorateName(info.Name), _columns,
|
||||||
|
string.IsNullOrEmpty(WhereCondition) ? string.Empty : " WHERE ",
|
||||||
|
WhereCondition);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Update
|
#region Update
|
||||||
|
|||||||
@@ -72,6 +72,7 @@
|
|||||||
<Compile Include="DynamicTransaction.cs" />
|
<Compile Include="DynamicTransaction.cs" />
|
||||||
<Compile Include="Helpers\CollectionComparer.cs" />
|
<Compile Include="Helpers\CollectionComparer.cs" />
|
||||||
<Compile Include="Helpers\Dynamics\DynamicParser.cs" />
|
<Compile Include="Helpers\Dynamics\DynamicParser.cs" />
|
||||||
|
<Compile Include="Helpers\Dynamics\DynamicProxy.cs" />
|
||||||
<Compile Include="Helpers\IExtendedDisposable.cs" />
|
<Compile Include="Helpers\IExtendedDisposable.cs" />
|
||||||
<Compile Include="Helpers\FrameworkTools.cs" />
|
<Compile Include="Helpers\FrameworkTools.cs" />
|
||||||
<Compile Include="Helpers\StringExtensions.cs" />
|
<Compile Include="Helpers\StringExtensions.cs" />
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ namespace DynamORM
|
|||||||
public bool EndBlock { get; set; }
|
public bool EndBlock { get; set; }
|
||||||
|
|
||||||
/// <summary>Gets or sets a value indicating whether set parameters for null values.</summary>
|
/// <summary>Gets or sets a value indicating whether set parameters for null values.</summary>
|
||||||
public bool VirtualColumn { get; set; }
|
public bool? VirtualColumn { get; set; }
|
||||||
|
|
||||||
/// <summary>Gets or sets schema representation of a column.</summary>
|
/// <summary>Gets or sets schema representation of a column.</summary>
|
||||||
/// <remarks>Workaround to providers issues which sometimes pass wrong
|
/// <remarks>Workaround to providers issues which sometimes pass wrong
|
||||||
@@ -370,7 +370,7 @@ namespace DynamORM
|
|||||||
/// <summary>Sets the virtual column.</summary>
|
/// <summary>Sets the virtual column.</summary>
|
||||||
/// <param name="virt">Set virtual column value.</param>
|
/// <param name="virt">Set virtual column value.</param>
|
||||||
/// <returns>Returns self.</returns>
|
/// <returns>Returns self.</returns>
|
||||||
public DynamicColumn SetVirtualColumn(bool virt)
|
public DynamicColumn SetVirtualColumn(bool? virt)
|
||||||
{
|
{
|
||||||
VirtualColumn = virt;
|
VirtualColumn = virt;
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ using System.Data.Common;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using DynamORM.Builders;
|
using DynamORM.Builders;
|
||||||
|
using DynamORM.Builders.Extensions;
|
||||||
using DynamORM.Builders.Implementation;
|
using DynamORM.Builders.Implementation;
|
||||||
using DynamORM.Helpers;
|
using DynamORM.Helpers;
|
||||||
using DynamORM.Mapper;
|
using DynamORM.Mapper;
|
||||||
@@ -237,14 +238,14 @@ namespace DynamORM
|
|||||||
|
|
||||||
#endregion Table
|
#endregion Table
|
||||||
|
|
||||||
#region From
|
#region From/Insert/Update/Delete
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds to the 'From' clause the contents obtained by parsing the dynamic lambda expressions given. The supported
|
/// Adds to the <code>FROM</code> clause the contents obtained by parsing the dynamic lambda expressions given. The supported
|
||||||
/// formats are:
|
/// formats are:
|
||||||
/// <para>- Resolve to a string: 'x => "Table AS Alias', where the alias part is optional.</para>
|
/// <para>- Resolve to a string: <code>x => "owner.Table AS Alias"</code>, 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>- Resolve to an expression: <code>x => x.owner.Table.As( x.Alias )</code>, where the alias part is optional.</para>
|
||||||
/// <para>- Generic expression: 'x => x( expression ).As( x.Alias )', where the alias part is mandatory. In this
|
/// <para>- Generic expression: <code>x => x( expression ).As( x.Alias )</code>, where the alias part is mandatory. In this
|
||||||
/// case the alias is not annotated.</para>
|
/// case the alias is not annotated.</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="func">The specification.</param>
|
/// <param name="func">The specification.</param>
|
||||||
@@ -254,7 +255,87 @@ namespace DynamORM
|
|||||||
return new DynamicSelectQueryBuilder(this).From(func);
|
return new DynamicSelectQueryBuilder(this).From(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion From
|
/// <summary>Adds to the <code>FROM</code> clause using <see cref="Type"/>.</summary>
|
||||||
|
/// <typeparam name="T">Type which can be represented in database.</typeparam>
|
||||||
|
/// <returns>This instance to permit chaining.</returns>
|
||||||
|
public virtual IDynamicSelectQueryBuilder From<T>()
|
||||||
|
{
|
||||||
|
return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds to the <code>INSERT INTO</code> clause the contents obtained by parsing the dynamic lambda expressions given. The supported
|
||||||
|
/// formats are:
|
||||||
|
/// <para>- Resolve to a string: <code>x => "owner.Table"</code>.</para>
|
||||||
|
/// <para>- Resolve to a type: <code>x => typeof(SomeClass)</code>.</para>
|
||||||
|
/// <para>- Resolve to an expression: <code>x => x.owner.Table</code>.</para>
|
||||||
|
/// <para>- Generic expression: <code>x => x( expression )</code>. Expression can
|
||||||
|
/// be <see cref="string"/> or <see cref="Type"/>.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="func">The specification.</param>
|
||||||
|
/// <returns>This instance to permit chaining.</returns>
|
||||||
|
public virtual IDynamicInsertQueryBuilder Insert(Func<dynamic, object> func)
|
||||||
|
{
|
||||||
|
return new DynamicInsertQueryBuilder(this).Table(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Adds to the <code>INSERT INTO</code> clause using <see cref="Type"/>.</summary>
|
||||||
|
/// <typeparam name="T">Type which can be represented in database.</typeparam>
|
||||||
|
/// <returns>This instance to permit chaining.</returns>
|
||||||
|
public virtual IDynamicInsertQueryBuilder Insert<T>()
|
||||||
|
{
|
||||||
|
return new DynamicInsertQueryBuilder(this).Table(typeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds to the <code>UPDATE</code> clause the contents obtained by parsing the dynamic lambda expressions given. The supported
|
||||||
|
/// formats are:
|
||||||
|
/// <para>- Resolve to a string: <code>x => "owner.Table"</code>.</para>
|
||||||
|
/// <para>- Resolve to a type: <code>x => typeof(SomeClass)</code>.</para>
|
||||||
|
/// <para>- Resolve to an expression: <code>x => x.owner.Table</code>.</para>
|
||||||
|
/// <para>- Generic expression: <code>x => x( expression )</code>. Expression can
|
||||||
|
/// be <see cref="string"/> or <see cref="Type"/>.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="func">The specification.</param>
|
||||||
|
/// <returns>This instance to permit chaining.</returns>
|
||||||
|
public virtual IDynamicUpdateQueryBuilder Update(Func<dynamic, object> func)
|
||||||
|
{
|
||||||
|
return new DynamicUpdateQueryBuilder(this).Table(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Adds to the <code>UPDATE</code> clause using <see cref="Type"/>.</summary>
|
||||||
|
/// <typeparam name="T">Type which can be represented in database.</typeparam>
|
||||||
|
/// <returns>This instance to permit chaining.</returns>
|
||||||
|
public virtual IDynamicUpdateQueryBuilder Update<T>()
|
||||||
|
{
|
||||||
|
return new DynamicUpdateQueryBuilder(this).Table(typeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds to the <code>DELETE FROM</code> clause the contents obtained by parsing the dynamic lambda expressions given. The supported
|
||||||
|
/// formats are:
|
||||||
|
/// <para>- Resolve to a string: <code>x => "owner.Table"</code>.</para>
|
||||||
|
/// <para>- Resolve to a type: <code>x => typeof(SomeClass)</code>.</para>
|
||||||
|
/// <para>- Resolve to an expression: <code>x => x.owner.Table</code>.</para>
|
||||||
|
/// <para>- Generic expression: <code>x => x( expression )</code>. Expression can
|
||||||
|
/// be <see cref="string"/> or <see cref="Type"/>.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="func">The specification.</param>
|
||||||
|
/// <returns>This instance to permit chaining.</returns>
|
||||||
|
public virtual IDynamicDeleteQueryBuilder Delete(Func<dynamic, object> func)
|
||||||
|
{
|
||||||
|
return new DynamicDeleteQueryBuilder(this).Table(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Adds to the <code>DELETE FROM</code> clause using <see cref="Type"/>.</summary>
|
||||||
|
/// <typeparam name="T">Type which can be represented in database.</typeparam>
|
||||||
|
/// <returns>This instance to permit chaining.</returns>
|
||||||
|
public virtual IDynamicDeleteQueryBuilder Delete<T>()
|
||||||
|
{
|
||||||
|
return new DynamicDeleteQueryBuilder(this).Table(typeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion From/Insert/Update/Delete
|
||||||
|
|
||||||
#region Schema
|
#region Schema
|
||||||
|
|
||||||
|
|||||||
@@ -795,6 +795,40 @@ namespace DynamORM
|
|||||||
|
|
||||||
#endregion Command extensions
|
#endregion Command extensions
|
||||||
|
|
||||||
|
#region Dynamic builders extensions
|
||||||
|
|
||||||
|
/// <summary>Turns an <see cref="IDynamicSelectQueryBuilder"/> to a Dynamic list of things.</summary>
|
||||||
|
/// <param name="b">Ready to execute builder.</param>
|
||||||
|
/// <returns>List of things.</returns>
|
||||||
|
public static List<dynamic> ToList(this IDynamicSelectQueryBuilder b)
|
||||||
|
{
|
||||||
|
return b.Execute().ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Sets the on create temporary parameter action.</summary>
|
||||||
|
/// <typeparam name="T">Class implementing <see cref="IDynamicQueryBuilder"/> interface.</typeparam>
|
||||||
|
/// <param name="b">The builder on which set delegate.</param>
|
||||||
|
/// <param name="a">Action to invoke.</param>
|
||||||
|
/// <returns>Returns instance of builder on which action is set.</returns>
|
||||||
|
public static T CreateTemporaryParameterAction<T>(this T b, Action<IParameter> a) where T : IDynamicQueryBuilder
|
||||||
|
{
|
||||||
|
b.OnCreateTemporaryParameter = a;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Sets the on create real parameter action.</summary>
|
||||||
|
/// <typeparam name="T">Class implementing <see cref="IDynamicQueryBuilder"/> interface.</typeparam>
|
||||||
|
/// <param name="b">The builder on which set delegate.</param>
|
||||||
|
/// <param name="a">Action to invoke.</param>
|
||||||
|
/// <returns>Returns instance of builder on which action is set.</returns>
|
||||||
|
public static T CreateParameterAction<T>(this T b, Action<IParameter, IDbDataParameter> a) where T : IDynamicQueryBuilder
|
||||||
|
{
|
||||||
|
b.OnCreateParameter = a;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Dynamic builders extensions
|
||||||
|
|
||||||
#region Dynamic extensions
|
#region Dynamic extensions
|
||||||
|
|
||||||
/// <summary>Turns an <see cref="IDataReader"/> to a Dynamic list of things.</summary>
|
/// <summary>Turns an <see cref="IDataReader"/> to a Dynamic list of things.</summary>
|
||||||
@@ -810,14 +844,6 @@ namespace DynamORM
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Turns an <see cref="IDynamicSelectQueryBuilder"/> to a Dynamic list of things.</summary>
|
|
||||||
/// <param name="b">Ready to execute builder.</param>
|
|
||||||
/// <returns>List of things.</returns>
|
|
||||||
public static List<dynamic> ToList(this IDynamicSelectQueryBuilder b)
|
|
||||||
{
|
|
||||||
return b.Execute().ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Turns an <see cref="IDynamicSelectQueryBuilder"/> to a Dynamic list of things with specified type.</summary>
|
/// <summary>Turns an <see cref="IDynamicSelectQueryBuilder"/> to a Dynamic list of things with specified type.</summary>
|
||||||
/// <typeparam name="T">Type of object to map on.</typeparam>
|
/// <typeparam name="T">Type of object to map on.</typeparam>
|
||||||
/// <param name="b">Ready to execute builder.</param>
|
/// <param name="b">Ready to execute builder.</param>
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ using DynamORM.Builders;
|
|||||||
using DynamORM.Builders.Extensions;
|
using DynamORM.Builders.Extensions;
|
||||||
using DynamORM.Builders.Implementation;
|
using DynamORM.Builders.Implementation;
|
||||||
using DynamORM.Helpers;
|
using DynamORM.Helpers;
|
||||||
|
using DynamORM.Helpers.Dynamics;
|
||||||
using DynamORM.Mapper;
|
using DynamORM.Mapper;
|
||||||
|
|
||||||
namespace DynamORM
|
namespace DynamORM
|
||||||
@@ -222,7 +223,7 @@ namespace DynamORM
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return string.IsNullOrEmpty(OwnerName) ?
|
return string.IsNullOrEmpty(TableName) ? null : string.IsNullOrEmpty(OwnerName) ?
|
||||||
Database.DecorateName(TableName) :
|
Database.DecorateName(TableName) :
|
||||||
string.Format("{0}.{1}", Database.DecorateName(OwnerName), Database.DecorateName(TableName));
|
string.Format("{0}.{1}", Database.DecorateName(OwnerName), Database.DecorateName(TableName));
|
||||||
}
|
}
|
||||||
@@ -412,8 +413,9 @@ namespace DynamORM
|
|||||||
{
|
{
|
||||||
var builder = new DynamicSelectQueryBuilder(this.Database);
|
var builder = new DynamicSelectQueryBuilder(this.Database);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(this.TableName))
|
var name = this.FullName;
|
||||||
builder.From(x => this.TableName);
|
if (!string.IsNullOrEmpty(name))
|
||||||
|
builder.From(x => name);
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
@@ -532,9 +534,9 @@ namespace DynamORM
|
|||||||
|
|
||||||
/// <summary>Create new <see cref="DynamicInsertQueryBuilder"/>.</summary>
|
/// <summary>Create new <see cref="DynamicInsertQueryBuilder"/>.</summary>
|
||||||
/// <returns>New <see cref="DynamicInsertQueryBuilder"/> instance.</returns>
|
/// <returns>New <see cref="DynamicInsertQueryBuilder"/> instance.</returns>
|
||||||
public IDynamicInsertQueryBuilder Insert()
|
public dynamic Insert()
|
||||||
{
|
{
|
||||||
return new DynamicInsertQueryBuilder(this.Database, this.TableName);
|
return new DynamicProxy<IDynamicInsertQueryBuilder>(new DynamicInsertQueryBuilder(this.Database, this.FullName));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Adds a record to the database. You can pass in an Anonymous object, an <see cref="ExpandoObject"/>,
|
/// <summary>Adds a record to the database. You can pass in an Anonymous object, an <see cref="ExpandoObject"/>,
|
||||||
@@ -555,9 +557,9 @@ namespace DynamORM
|
|||||||
|
|
||||||
/// <summary>Create new <see cref="DynamicUpdateQueryBuilder"/>.</summary>
|
/// <summary>Create new <see cref="DynamicUpdateQueryBuilder"/>.</summary>
|
||||||
/// <returns>New <see cref="DynamicUpdateQueryBuilder"/> instance.</returns>
|
/// <returns>New <see cref="DynamicUpdateQueryBuilder"/> instance.</returns>
|
||||||
public IDynamicUpdateQueryBuilder Update()
|
public dynamic Update()
|
||||||
{
|
{
|
||||||
return new DynamicUpdateQueryBuilder(this.Database, this.TableName);
|
return new DynamicProxy<IDynamicUpdateQueryBuilder>(new DynamicUpdateQueryBuilder(this.Database, this.FullName));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Updates a record in the database. You can pass in an Anonymous object, an ExpandoObject,
|
/// <summary>Updates a record in the database. You can pass in an Anonymous object, an ExpandoObject,
|
||||||
@@ -593,9 +595,9 @@ namespace DynamORM
|
|||||||
|
|
||||||
/// <summary>Create new <see cref="DynamicDeleteQueryBuilder"/>.</summary>
|
/// <summary>Create new <see cref="DynamicDeleteQueryBuilder"/>.</summary>
|
||||||
/// <returns>New <see cref="DynamicDeleteQueryBuilder"/> instance.</returns>
|
/// <returns>New <see cref="DynamicDeleteQueryBuilder"/> instance.</returns>
|
||||||
public IDynamicDeleteQueryBuilder Delete()
|
public dynamic Delete()
|
||||||
{
|
{
|
||||||
return new DynamicDeleteQueryBuilder(this.Database, this.TableName);
|
return new DynamicProxy<IDynamicDeleteQueryBuilder>(new DynamicDeleteQueryBuilder(this.Database, this.FullName));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Removes a record from the database. You can pass in an Anonymous object, an <see cref="ExpandoObject"/>,
|
/// <summary>Removes a record from the database. You can pass in an Anonymous object, an <see cref="ExpandoObject"/>,
|
||||||
|
|||||||
270
DynamORM/Helpers/Dynamics/DynamicProxy.cs
Normal file
270
DynamORM/Helpers/Dynamics/DynamicProxy.cs
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
/*
|
||||||
|
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||||
|
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Dynamic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
using DynamORM.Mapper;
|
||||||
|
|
||||||
|
namespace DynamORM.Helpers.Dynamics
|
||||||
|
{
|
||||||
|
/// <summary>Class that allows to use interfaces as dynamic objects.</summary>
|
||||||
|
/// <typeparam name="T">Type of class to proxy.</typeparam>
|
||||||
|
/// <remarks>This is temporary solution. Which allows to use builders as a dynamic type.</remarks>
|
||||||
|
public class DynamicProxy<T> : DynamicObject
|
||||||
|
{
|
||||||
|
private T _proxy;
|
||||||
|
private Type _type;
|
||||||
|
private Dictionary<string, DynamicPropertyInvoker> _properties;
|
||||||
|
private Dictionary<MethodInfo, Delegate> _methods;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DynamicProxy{T}" /> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="proxiedObject">The object to which proxy should be created.</param>
|
||||||
|
/// <exception cref="System.ArgumentNullException">The object to which proxy should be created is null.</exception>
|
||||||
|
public DynamicProxy(T proxiedObject)
|
||||||
|
{
|
||||||
|
if (proxiedObject == null)
|
||||||
|
throw new ArgumentNullException("proxiedObject");
|
||||||
|
|
||||||
|
_proxy = proxiedObject;
|
||||||
|
_type = typeof(T);
|
||||||
|
|
||||||
|
_properties = _type.GetProperties()
|
||||||
|
.ToDictionary(
|
||||||
|
k => k.Name,
|
||||||
|
v => new DynamicPropertyInvoker(v, null));
|
||||||
|
|
||||||
|
_methods = _type.GetMethods()
|
||||||
|
.Where(m => !m.IsStatic && !m.IsGenericMethod)
|
||||||
|
.ToDictionary(
|
||||||
|
k => k,
|
||||||
|
v =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Delegate.CreateDelegate(Expression.GetDelegateType(v.GetParameters().Select(t => t.ParameterType).Concat(new[] { v.ReflectedType }).ToArray()), _proxy, v.Name);
|
||||||
|
}
|
||||||
|
catch (ArgumentException)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Provides implementation for type conversion operations.
|
||||||
|
/// Classes derived from the <see cref="T:System.Dynamic.DynamicObject" />
|
||||||
|
/// class can override this method to specify dynamic behavior for
|
||||||
|
/// operations that convert an object from one type to another.</summary>
|
||||||
|
/// <param name="binder">Provides information about the conversion operation.
|
||||||
|
/// The binder.Type property provides the type to which the object must be
|
||||||
|
/// converted. For example, for the statement (String)sampleObject in C#
|
||||||
|
/// (CType(sampleObject, Type) in Visual Basic), where sampleObject is an
|
||||||
|
/// instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject" />
|
||||||
|
/// class, binder.Type returns the <see cref="T:System.String" /> type.
|
||||||
|
/// The binder.Explicit property provides information about the kind of
|
||||||
|
/// conversion that occurs. It returns true for explicit conversion and
|
||||||
|
/// false for implicit conversion.</param>
|
||||||
|
/// <param name="result">The result of the type conversion operation.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if the operation is successful; otherwise, <c>false</c>.
|
||||||
|
/// If this method returns false, the run-time binder of the language determines the
|
||||||
|
/// behavior. (In most cases, a language-specific run-time exception is thrown).</returns>
|
||||||
|
public override bool TryConvert(ConvertBinder binder, out object result)
|
||||||
|
{
|
||||||
|
if (binder.Type == typeof(T))
|
||||||
|
{
|
||||||
|
result = _proxy;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_proxy != null &&
|
||||||
|
binder.Type.IsAssignableFrom(_proxy.GetType()))
|
||||||
|
{
|
||||||
|
result = _proxy;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.TryConvert(binder, out result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Provides the implementation for operations that get member
|
||||||
|
/// values. Classes derived from the <see cref="T:System.Dynamic.DynamicObject" />
|
||||||
|
/// class can override this method to specify dynamic behavior for
|
||||||
|
/// operations such as getting a value for a property.</summary>
|
||||||
|
/// <param name="binder">Provides information about the object that
|
||||||
|
/// called the dynamic operation. The binder.Name property provides
|
||||||
|
/// the name of the member on which the dynamic operation is performed.
|
||||||
|
/// For example, for the Console.WriteLine(sampleObject.SampleProperty)
|
||||||
|
/// statement, where sampleObject is an instance of the class derived
|
||||||
|
/// from the <see cref="T:System.Dynamic.DynamicObject" /> class,
|
||||||
|
/// binder.Name returns "SampleProperty". The binder.IgnoreCase property
|
||||||
|
/// specifies whether the member name is case-sensitive.</param>
|
||||||
|
/// <param name="result">The result of the get operation. For example,
|
||||||
|
/// if the method is called for a property, you can assign the property
|
||||||
|
/// value to <paramref name="result" />.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if the operation is successful; otherwise,
|
||||||
|
/// <c>false</c>. If this method returns false, the run-time binder of the
|
||||||
|
/// language determines the behavior. (In most cases, a run-time exception
|
||||||
|
/// is thrown).</returns>
|
||||||
|
public override bool TryGetMember(GetMemberBinder binder, out object result)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var prop = _properties.TryGetValue(binder.Name);
|
||||||
|
|
||||||
|
result = prop.NullOr(p => p.Get.NullOr(g => g(_proxy), null), null);
|
||||||
|
|
||||||
|
return prop != null && prop.Get != null;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(string.Format("Cannot get member {0}", binder.Name), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Provides the implementation for operations that set member
|
||||||
|
/// values. Classes derived from the <see cref="T:System.Dynamic.DynamicObject" />
|
||||||
|
/// class can override this method to specify dynamic behavior for operations
|
||||||
|
/// such as setting a value for a property.</summary>
|
||||||
|
/// <param name="binder">Provides information about the object that called
|
||||||
|
/// the dynamic operation. The binder.Name property provides the name of
|
||||||
|
/// the member to which the value is being assigned. For example, for the
|
||||||
|
/// statement sampleObject.SampleProperty = "Test", where sampleObject is
|
||||||
|
/// an instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject" />
|
||||||
|
/// class, binder.Name returns "SampleProperty". The binder.IgnoreCase
|
||||||
|
/// property specifies whether the member name is case-sensitive.</param>
|
||||||
|
/// <param name="value">The value to set to the member. For example, for
|
||||||
|
/// sampleObject.SampleProperty = "Test", where sampleObject is an instance
|
||||||
|
/// of the class derived from the <see cref="T:System.Dynamic.DynamicObject" />
|
||||||
|
/// class, the <paramref name="value" /> is "Test".</param>
|
||||||
|
/// <returns>Returns <c>true</c> if the operation is successful; otherwise,
|
||||||
|
/// <c>false</c>. If this method returns false, the run-time binder of the
|
||||||
|
/// language determines the behavior. (In most cases, a language-specific
|
||||||
|
/// run-time exception is thrown).</returns>
|
||||||
|
public override bool TrySetMember(SetMemberBinder binder, object value)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var prop = _properties.TryGetValue(binder.Name);
|
||||||
|
|
||||||
|
if (prop != null && prop.Set != null)
|
||||||
|
{
|
||||||
|
prop.Set(_proxy, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(string.Format("Cannot set member {0} to '{1}'", binder.Name, value), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Provides the implementation for operations that invoke a member.
|
||||||
|
/// Classes derived from the <see cref="T:System.Dynamic.DynamicObject" />
|
||||||
|
/// class can override this method to specify dynamic behavior for
|
||||||
|
/// operations such as calling a method.</summary>
|
||||||
|
/// <param name="binder">Provides information about the dynamic operation.
|
||||||
|
/// The binder.Name property provides the name of the member on which the
|
||||||
|
/// dynamic operation is performed. For example, for the statement
|
||||||
|
/// sampleObject.SampleMethod(100), where sampleObject is an instance of
|
||||||
|
/// the class derived from the <see cref="T:System.Dynamic.DynamicObject" />
|
||||||
|
/// class, binder.Name returns "SampleMethod". The binder.IgnoreCase property
|
||||||
|
/// specifies whether the member name is case-sensitive.</param>
|
||||||
|
/// <param name="args">The arguments that are passed to the object member
|
||||||
|
/// during the invoke operation. For example, for the statement
|
||||||
|
/// sampleObject.SampleMethod(100), where sampleObject is derived from the
|
||||||
|
/// <see cref="T:System.Dynamic.DynamicObject" /> class,
|
||||||
|
/// <paramref name="args[0]" /> is equal to 100.</param>
|
||||||
|
/// <param name="result">The result of the member invocation.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if the operation is successful; otherwise,
|
||||||
|
/// <c>false</c>. If this method returns false, the run-time binder of the
|
||||||
|
/// language determines the behavior. (In most cases, a language-specific
|
||||||
|
/// run-time exception is thrown).</returns>
|
||||||
|
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
|
||||||
|
{
|
||||||
|
return TryInvokeMethod(binder.Name, out result, args) || base.TryInvokeMember(binder, args, out result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryInvokeMethod(string name, out object result, object[] args)
|
||||||
|
{
|
||||||
|
result = null;
|
||||||
|
|
||||||
|
MethodInfo mi = _methods.Keys
|
||||||
|
.Where(m => m.Name == name)
|
||||||
|
.FirstOrDefault(m =>
|
||||||
|
CompareTypes(m.GetParameters().ToArray(),
|
||||||
|
args.Select(a => a.GetType()).ToArray()));
|
||||||
|
|
||||||
|
Delegate d = _methods.TryGetValue(mi);
|
||||||
|
|
||||||
|
if (d != null)
|
||||||
|
{
|
||||||
|
result = d.DynamicInvoke(CompleteArguments(mi.GetParameters().ToArray(), args));
|
||||||
|
|
||||||
|
if (d.Method.ReturnType == _type && result is T)
|
||||||
|
result = new DynamicProxy<T>((T)result);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (mi != null)
|
||||||
|
{
|
||||||
|
result = mi.Invoke(_proxy, CompleteArguments(mi.GetParameters().ToArray(), args));
|
||||||
|
|
||||||
|
if (mi.ReturnType == _type && result is T)
|
||||||
|
result = new DynamicProxy<T>((T)result);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CompareTypes(ParameterInfo[] parameters, Type[] types)
|
||||||
|
{
|
||||||
|
if (parameters.Length < types.Length || parameters.Count(p => !p.IsOptional) > types.Length)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i = 0; i < types.Length; i++)
|
||||||
|
if (types[i] != parameters[i].ParameterType && !parameters[i].ParameterType.IsAssignableFrom(types[i]))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private object[] CompleteArguments(ParameterInfo[] parameters, object[] arguments)
|
||||||
|
{
|
||||||
|
return arguments.Concat(parameters.Skip(arguments.Length).Select(p => p.DefaultValue)).ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,7 +31,7 @@ using System;
|
|||||||
namespace DynamORM.Helpers
|
namespace DynamORM.Helpers
|
||||||
{
|
{
|
||||||
/// <summary>Class contains unclassified extensions.</summary>
|
/// <summary>Class contains unclassified extensions.</summary>
|
||||||
public static class UnclassifiedExtensions
|
internal static class UnclassifiedExtensions
|
||||||
{
|
{
|
||||||
/// <summary>Easy way to use conditional value.</summary>
|
/// <summary>Easy way to use conditional value.</summary>
|
||||||
/// <remarks>Includes <see cref="DBNull.Value"/>.</remarks>
|
/// <remarks>Includes <see cref="DBNull.Value"/>.</remarks>
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ namespace DynamORM.Mapper
|
|||||||
/// <summary>Dynamic property invoker.</summary>
|
/// <summary>Dynamic property invoker.</summary>
|
||||||
public class DynamicPropertyInvoker
|
public class DynamicPropertyInvoker
|
||||||
{
|
{
|
||||||
|
/// <summary>Gets the type of property.</summary>
|
||||||
|
public Type Type { get; private set; }
|
||||||
|
|
||||||
/// <summary>Gets value getter.</summary>
|
/// <summary>Gets value getter.</summary>
|
||||||
public Func<object, object> Get { get; private set; }
|
public Func<object, object> Get { get; private set; }
|
||||||
|
|
||||||
@@ -56,6 +59,7 @@ namespace DynamORM.Mapper
|
|||||||
public DynamicPropertyInvoker(PropertyInfo property, ColumnAttribute attr)
|
public DynamicPropertyInvoker(PropertyInfo property, ColumnAttribute attr)
|
||||||
{
|
{
|
||||||
Name = property.Name;
|
Name = property.Name;
|
||||||
|
Type = property.PropertyType;
|
||||||
|
|
||||||
var ignore = property.GetCustomAttributes(typeof(IgnoreAttribute), false);
|
var ignore = property.GetCustomAttributes(typeof(IgnoreAttribute), false);
|
||||||
|
|
||||||
@@ -63,8 +67,11 @@ namespace DynamORM.Mapper
|
|||||||
|
|
||||||
Column = attr;
|
Column = attr;
|
||||||
|
|
||||||
Get = CreateGetter(property);
|
if (property.CanRead)
|
||||||
Set = CreateSetter(property);
|
Get = CreateGetter(property);
|
||||||
|
|
||||||
|
if (property.CanWrite)
|
||||||
|
Set = CreateSetter(property);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Func<object, object> CreateGetter(PropertyInfo property)
|
private Func<object, object> CreateGetter(PropertyInfo property)
|
||||||
|
|||||||
Reference in New Issue
Block a user