@@ -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());
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// Tests the where expression not equal.
|
||||
/// </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());
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// Tests select escaped case with sub query.
|
||||
/// </summary>
|
||||
|
||||
@@ -34,12 +34,87 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using DynamORM.Builders.Implementation;
|
||||
using DynamORM.Helpers;
|
||||
using DynamORM.Helpers.Dynamics;
|
||||
using DynamORM.Mapper;
|
||||
|
||||
namespace DynamORM.Builders.Extensions
|
||||
{
|
||||
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
|
||||
{
|
||||
var tuple = tableName.Validated("Table Name").SplitSomethingAndAlias();
|
||||
@@ -49,6 +124,9 @@ namespace DynamORM.Builders.Extensions
|
||||
|
||||
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.Add(new DynamicQueryBuilder.TableInfo(builder.Database,
|
||||
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
|
||||
{
|
||||
if (type.IsAnonymous())
|
||||
throw new InvalidOperationException(string.Format("Cant assign anonymous type as a table ({0}).", type.FullName));
|
||||
|
||||
var mapper = DynamicMapperCache.GetMapper(type);
|
||||
|
||||
if (mapper == null)
|
||||
|
||||
@@ -43,6 +43,11 @@ namespace DynamORM.Builders.Extensions
|
||||
#region Where
|
||||
|
||||
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.");
|
||||
|
||||
@@ -88,8 +93,15 @@ namespace DynamORM.Builders.Extensions
|
||||
condition = builder.Parse(result, pars: builder.Parameters).Validated("Where condition");
|
||||
}
|
||||
|
||||
if (builder.WhereCondition == null) builder.WhereCondition = condition;
|
||||
else builder.WhereCondition = string.Format("{0} {1} {2}", builder.WhereCondition, and ? "AND" : "OR", condition);
|
||||
if (addBeginBrace) builder.OpenBracketsCount++;
|
||||
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;
|
||||
@@ -97,54 +109,43 @@ namespace DynamORM.Builders.Extensions
|
||||
|
||||
internal static T InternalWhere<T>(this T builder, DynamicColumn column) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
|
||||
{
|
||||
builder.VirtualMode = column.VirtualColumn;
|
||||
bool prepend = false;
|
||||
|
||||
if (column.BeginBlock)
|
||||
{
|
||||
if (string.IsNullOrEmpty(builder.WhereCondition))
|
||||
prepend = true;
|
||||
else
|
||||
builder.WhereCondition += " (";
|
||||
}
|
||||
bool virt = builder.VirtualMode;
|
||||
if (column.VirtualColumn.HasValue)
|
||||
builder.VirtualMode = column.VirtualColumn.Value;
|
||||
|
||||
// It's kind of uglu, but... well it works.
|
||||
if (column.Or)
|
||||
switch (column.Operator)
|
||||
{
|
||||
default:
|
||||
case DynamicColumn.CompareOperator.Eq: builder.InternalWhere(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.Like: builder.InternalWhere(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.In: builder.InternalWhere(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.Lte: builder.InternalWhere(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.Gte: builder.InternalWhere(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.Eq: builder.InternalWhere(column.BeginBlock, column.EndBlock, 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(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).Like(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(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).In(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(column.BeginBlock, column.EndBlock, 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(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) >= 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
|
||||
switch (column.Operator)
|
||||
{
|
||||
default:
|
||||
case DynamicColumn.CompareOperator.Eq: builder.InternalWhere(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.Like: builder.InternalWhere(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.In: builder.InternalWhere(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.Lte: builder.InternalWhere(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.Gte: builder.InternalWhere(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.Eq: builder.InternalWhere(column.BeginBlock, column.EndBlock, 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(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).Like(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(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).In(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(column.BeginBlock, column.EndBlock, 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(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) >= 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.WhereCondition = string.Format("({0}", builder.WhereCondition);
|
||||
|
||||
if (column.EndBlock)
|
||||
builder.WhereCondition += ")";
|
||||
builder.VirtualMode = virt;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,9 @@ namespace DynamORM.Builders
|
||||
/// <summary>Gets the tables used in this builder.</summary>
|
||||
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>
|
||||
bool SupportSchema { get; }
|
||||
|
||||
@@ -60,6 +63,14 @@ namespace DynamORM.Builders
|
||||
/// <remarks>This method must be override by derived classes.</remarks>
|
||||
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>
|
||||
/// <returns>Sub query builder.</returns>
|
||||
IDynamicSelectQueryBuilder SubQuery();
|
||||
|
||||
@@ -31,16 +31,23 @@ namespace DynamORM.Builders
|
||||
/// <summary>Interface describing parameter info.</summary>
|
||||
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>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>Gets or sets the parameter value.</summary>
|
||||
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>
|
||||
bool Virtual { get; set; }
|
||||
|
||||
/// <summary>Gets the parameter schema information.</summary>
|
||||
DynamicSchemaColumn? Schema { get; }
|
||||
/// <summary>Gets or sets the parameter schema information.</summary>
|
||||
DynamicSchemaColumn? Schema { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -63,9 +63,11 @@ namespace DynamORM.Builders.Implementation
|
||||
public override string CommandText()
|
||||
{
|
||||
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)),
|
||||
Database.DecorateName(info.Name), WhereCondition);
|
||||
Database.DecorateName(info.Name),
|
||||
string.IsNullOrEmpty(WhereCondition) ? string.Empty : " WHERE ",
|
||||
WhereCondition);
|
||||
}
|
||||
|
||||
#region Where
|
||||
|
||||
@@ -45,13 +45,17 @@ namespace DynamORM.Builders.Implementation
|
||||
internal abstract class DynamicQueryBuilder : IDynamicQueryBuilder
|
||||
{
|
||||
/// <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;
|
||||
|
||||
/// <summary>Gets or sets a value indicating whether add virtual.</summary>
|
||||
internal bool VirtualMode { get; set; }
|
||||
|
||||
#region TableInfo
|
||||
|
||||
/// <summary>Table information.</summary>
|
||||
@@ -137,34 +141,29 @@ namespace DynamORM.Builders.Implementation
|
||||
/// <summary>Interface describing parameter info.</summary>
|
||||
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>
|
||||
public string Name { get; internal set; }
|
||||
|
||||
/// <summary>Gets or sets the parameter value.</summary>
|
||||
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>
|
||||
public bool Virtual { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the parameter schema information.</summary>
|
||||
public DynamicSchemaColumn? Schema { get; internal set; }
|
||||
public DynamicSchemaColumn? Schema { get; set; }
|
||||
}
|
||||
|
||||
#endregion Parameter
|
||||
|
||||
internal string WhereCondition { get; set; }
|
||||
|
||||
/// <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; }
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DynamicQueryBuilder"/> class.
|
||||
@@ -176,6 +175,9 @@ namespace DynamORM.Builders.Implementation
|
||||
Tables = new List<ITableInfo>();
|
||||
Parameters = new Dictionary<string, IParameter>();
|
||||
|
||||
WhereCondition = null;
|
||||
OpenBracketsCount = 0;
|
||||
|
||||
Database = db;
|
||||
SupportSchema = (db.Options & DynamicDatabaseOptions.SupportSchema) == DynamicDatabaseOptions.SupportSchema;
|
||||
}
|
||||
@@ -189,37 +191,85 @@ namespace DynamORM.Builders.Implementation
|
||||
_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;
|
||||
|
||||
while (builder != null)
|
||||
// End not ended where statement
|
||||
if (this is IQueryWithWhere)
|
||||
{
|
||||
if (builder.Tables.Any(t => t.Alias == name))
|
||||
return true;
|
||||
|
||||
builder = builder._parent;
|
||||
while (OpenBracketsCount > 0)
|
||||
{
|
||||
WhereCondition += ")";
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
#region SubQuery
|
||||
|
||||
/// <summary>Creates sub query.</summary>
|
||||
/// <returns>Sub query builder.</returns>
|
||||
@@ -242,71 +292,9 @@ namespace DynamORM.Builders.Implementation
|
||||
return SubQuery().From(func);
|
||||
}
|
||||
|
||||
/// <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();
|
||||
#endregion SubQuery
|
||||
|
||||
/// <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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
#endregion IDynamicQueryBuilder
|
||||
|
||||
#region Parser
|
||||
|
||||
@@ -333,14 +321,14 @@ namespace DynamORM.Builders.Implementation
|
||||
if (!nulls)
|
||||
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...
|
||||
if (node is string)
|
||||
{
|
||||
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...
|
||||
@@ -689,23 +677,30 @@ namespace DynamORM.Builders.Implementation
|
||||
|
||||
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)
|
||||
{
|
||||
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
|
||||
var name = Guid.NewGuid().ToString();
|
||||
var par = new Parameter()
|
||||
{
|
||||
Name = string.Format("[${0}]", name),
|
||||
Value = node,
|
||||
Name = wellKnownName ? ((String)node).Substring(2, ((String)node).Length - 3) : Guid.NewGuid().ToString(),
|
||||
Value = wellKnownName ? null : node,
|
||||
WellKnown = wellKnownName,
|
||||
Virtual = VirtualMode,
|
||||
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
|
||||
@@ -716,6 +711,42 @@ namespace DynamORM.Builders.Implementation
|
||||
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)
|
||||
{
|
||||
if (main.IndexOf("(") > 0 && main.IndexOf(")") > 0)
|
||||
@@ -739,6 +770,47 @@ namespace DynamORM.Builders.Implementation
|
||||
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>
|
||||
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;
|
||||
foreach (var f in func)
|
||||
@@ -266,6 +267,19 @@ namespace DynamORM.Builders.Implementation
|
||||
tuple.Item2.Validated("Alias", canbeNull: true),
|
||||
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)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
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);
|
||||
|
||||
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) ?
|
||||
mapper.Type.Name : mapper.Table.Name;
|
||||
@@ -1235,4 +1252,4 @@ namespace DynamORM.Builders.Implementation
|
||||
|
||||
#endregion Helpers
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,9 +58,11 @@ namespace DynamORM.Builders.Implementation
|
||||
public override string CommandText()
|
||||
{
|
||||
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)),
|
||||
Database.DecorateName(info.Name), _columns, WhereCondition);
|
||||
Database.DecorateName(info.Name), _columns,
|
||||
string.IsNullOrEmpty(WhereCondition) ? string.Empty : " WHERE ",
|
||||
WhereCondition);
|
||||
}
|
||||
|
||||
#region Update
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
<Compile Include="DynamicTransaction.cs" />
|
||||
<Compile Include="Helpers\CollectionComparer.cs" />
|
||||
<Compile Include="Helpers\Dynamics\DynamicParser.cs" />
|
||||
<Compile Include="Helpers\Dynamics\DynamicProxy.cs" />
|
||||
<Compile Include="Helpers\IExtendedDisposable.cs" />
|
||||
<Compile Include="Helpers\FrameworkTools.cs" />
|
||||
<Compile Include="Helpers\StringExtensions.cs" />
|
||||
|
||||
@@ -145,7 +145,7 @@ namespace DynamORM
|
||||
public bool EndBlock { get; set; }
|
||||
|
||||
/// <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>
|
||||
/// <remarks>Workaround to providers issues which sometimes pass wrong
|
||||
@@ -370,7 +370,7 @@ namespace DynamORM
|
||||
/// <summary>Sets the virtual column.</summary>
|
||||
/// <param name="virt">Set virtual column value.</param>
|
||||
/// <returns>Returns self.</returns>
|
||||
public DynamicColumn SetVirtualColumn(bool virt)
|
||||
public DynamicColumn SetVirtualColumn(bool? virt)
|
||||
{
|
||||
VirtualColumn = virt;
|
||||
return this;
|
||||
|
||||
@@ -33,6 +33,7 @@ using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using DynamORM.Builders;
|
||||
using DynamORM.Builders.Extensions;
|
||||
using DynamORM.Builders.Implementation;
|
||||
using DynamORM.Helpers;
|
||||
using DynamORM.Mapper;
|
||||
@@ -237,14 +238,14 @@ namespace DynamORM
|
||||
|
||||
#endregion Table
|
||||
|
||||
#region From
|
||||
#region From/Insert/Update/Delete
|
||||
|
||||
/// <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:
|
||||
/// <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
|
||||
/// <para>- Resolve to a string: <code>x => "owner.Table AS Alias"</code>, 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: <code>x => x( expression ).As( x.Alias )</code>, where the alias part is mandatory. In this
|
||||
/// case the alias is not annotated.</para>
|
||||
/// </summary>
|
||||
/// <param name="func">The specification.</param>
|
||||
@@ -254,7 +255,87 @@ namespace DynamORM
|
||||
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
|
||||
|
||||
|
||||
@@ -795,6 +795,40 @@ namespace DynamORM
|
||||
|
||||
#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
|
||||
|
||||
/// <summary>Turns an <see cref="IDataReader"/> to a Dynamic list of things.</summary>
|
||||
@@ -810,14 +844,6 @@ namespace DynamORM
|
||||
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>
|
||||
/// <typeparam name="T">Type of object to map on.</typeparam>
|
||||
/// <param name="b">Ready to execute builder.</param>
|
||||
|
||||
@@ -36,6 +36,7 @@ using DynamORM.Builders;
|
||||
using DynamORM.Builders.Extensions;
|
||||
using DynamORM.Builders.Implementation;
|
||||
using DynamORM.Helpers;
|
||||
using DynamORM.Helpers.Dynamics;
|
||||
using DynamORM.Mapper;
|
||||
|
||||
namespace DynamORM
|
||||
@@ -222,7 +223,7 @@ namespace DynamORM
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.IsNullOrEmpty(OwnerName) ?
|
||||
return string.IsNullOrEmpty(TableName) ? null : string.IsNullOrEmpty(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);
|
||||
|
||||
if (!string.IsNullOrEmpty(this.TableName))
|
||||
builder.From(x => this.TableName);
|
||||
var name = this.FullName;
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
builder.From(x => name);
|
||||
|
||||
return builder;
|
||||
}
|
||||
@@ -532,9 +534,9 @@ namespace DynamORM
|
||||
|
||||
/// <summary>Create new <see cref="DynamicInsertQueryBuilder"/>.</summary>
|
||||
/// <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"/>,
|
||||
@@ -555,9 +557,9 @@ namespace DynamORM
|
||||
|
||||
/// <summary>Create new <see cref="DynamicUpdateQueryBuilder"/>.</summary>
|
||||
/// <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,
|
||||
@@ -593,9 +595,9 @@ namespace DynamORM
|
||||
|
||||
/// <summary>Create new <see cref="DynamicDeleteQueryBuilder"/>.</summary>
|
||||
/// <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"/>,
|
||||
|
||||
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
|
||||
{
|
||||
/// <summary>Class contains unclassified extensions.</summary>
|
||||
public static class UnclassifiedExtensions
|
||||
internal static class UnclassifiedExtensions
|
||||
{
|
||||
/// <summary>Easy way to use conditional value.</summary>
|
||||
/// <remarks>Includes <see cref="DBNull.Value"/>.</remarks>
|
||||
|
||||
@@ -35,6 +35,9 @@ namespace DynamORM.Mapper
|
||||
/// <summary>Dynamic property invoker.</summary>
|
||||
public class DynamicPropertyInvoker
|
||||
{
|
||||
/// <summary>Gets the type of property.</summary>
|
||||
public Type Type { get; private set; }
|
||||
|
||||
/// <summary>Gets value getter.</summary>
|
||||
public Func<object, object> Get { get; private set; }
|
||||
|
||||
@@ -56,6 +59,7 @@ namespace DynamORM.Mapper
|
||||
public DynamicPropertyInvoker(PropertyInfo property, ColumnAttribute attr)
|
||||
{
|
||||
Name = property.Name;
|
||||
Type = property.PropertyType;
|
||||
|
||||
var ignore = property.GetCustomAttributes(typeof(IgnoreAttribute), false);
|
||||
|
||||
@@ -63,8 +67,11 @@ namespace DynamORM.Mapper
|
||||
|
||||
Column = attr;
|
||||
|
||||
Get = CreateGetter(property);
|
||||
Set = CreateSetter(property);
|
||||
if (property.CanRead)
|
||||
Get = CreateGetter(property);
|
||||
|
||||
if (property.CanWrite)
|
||||
Set = CreateSetter(property);
|
||||
}
|
||||
|
||||
private Func<object, object> CreateGetter(PropertyInfo property)
|
||||
|
||||
Reference in New Issue
Block a user