Select improvements and alias parsing.

Insert and updates evolved.
Written more tests that intended to break parser.
Added basic exception handling, telling user what went wrong.

TODO:
 * INSERT INTO ... SELECT ...
 * Extend and normalize documentation.
This commit is contained in:
grzegorz.russek
2013-06-05 11:53:49 +00:00
parent de58df8c60
commit 3aa20eb50b
13 changed files with 423 additions and 44 deletions

View File

@@ -1,4 +1,32 @@
using System;
/*
* 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;
namespace DynamORM.Builders

View File

@@ -1,8 +1,30 @@
// -----------------------------------------------------------------------
// <copyright file="ITableInfo.cs" company="Microsoft">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
/*
* 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.Collections.Generic;

View File

@@ -66,6 +66,7 @@
<Compile Include="DynamicDatabase.cs" />
<Compile Include="DynamicDatabaseOptions.cs" />
<Compile Include="DynamicExtensions.cs" />
<Compile Include="DynamicQueryException.cs" />
<Compile Include="DynamicSchemaColumn.cs" />
<Compile Include="DynamicTable.cs" />
<Compile Include="DynamicTransaction.cs" />

View File

@@ -28,11 +28,12 @@
using System;
using System.Data;
using DynamORM.Helpers;
namespace DynamORM
{
/// <summary>Helper class to easy manage command.</summary>
public class DynamicCommand : IDbCommand
public class DynamicCommand : IDbCommand, IExtendedDisposable
{
private IDbCommand _command;
private int? _commandTimeout = null;
@@ -45,6 +46,7 @@ namespace DynamORM
/// <param name="db">The database manager.</param>
internal DynamicCommand(DynamicConnection con, DynamicDatabase db)
{
IsDisposed = false;
_con = con;
_db = db;
@@ -65,6 +67,7 @@ namespace DynamORM
/// <remarks>Used internally to create command without context.</remarks>
internal DynamicCommand(DynamicDatabase db)
{
IsDisposed = false;
_db = db;
_command = db.Provider.CreateCommand();
}
@@ -152,7 +155,14 @@ namespace DynamORM
/// <returns>The number of rows affected.</returns>
public int ExecuteNonQuery()
{
return PrepareForExecution().ExecuteNonQuery();
try
{
return PrepareForExecution().ExecuteNonQuery();
}
catch (Exception ex)
{
throw new DynamicQueryException(ex, this);
}
}
/// <summary>Executes the <see cref="P:System.Data.IDbCommand.CommandText"/>
@@ -164,7 +174,14 @@ namespace DynamORM
/// <returns>An <see cref="T:System.Data.IDataReader"/> object.</returns>
public IDataReader ExecuteReader(CommandBehavior behavior)
{
return PrepareForExecution().ExecuteReader(behavior);
try
{
return PrepareForExecution().ExecuteReader(behavior);
}
catch (Exception ex)
{
throw new DynamicQueryException(ex, this);
}
}
/// <summary>Executes the <see cref="P:System.Data.IDbCommand.CommandText"/>
@@ -173,7 +190,14 @@ namespace DynamORM
/// <returns>An <see cref="T:System.Data.IDataReader"/> object.</returns>
public IDataReader ExecuteReader()
{
return PrepareForExecution().ExecuteReader();
try
{
return PrepareForExecution().ExecuteReader();
}
catch (Exception ex)
{
throw new DynamicQueryException(ex, this);
}
}
/// <summary>Executes the query, and returns the first column of the
@@ -182,7 +206,14 @@ namespace DynamORM
/// <returns>The first column of the first row in the result set.</returns>
public object ExecuteScalar()
{
return PrepareForExecution().ExecuteScalar();
try
{
return PrepareForExecution().ExecuteScalar();
}
catch (Exception ex)
{
throw new DynamicQueryException(ex, this);
}
}
/// <summary>Gets the <see cref="T:System.Data.IDataParameterCollection"/>.</summary>
@@ -194,7 +225,14 @@ namespace DynamORM
/// <summary>Creates a prepared (or compiled) version of the command on the data source.</summary>
public void Prepare()
{
_command.Prepare();
try
{
_command.Prepare();
}
catch (Exception ex)
{
throw new DynamicQueryException("Error preparing command.", ex, this);
}
}
/// <summary>Gets or sets the transaction within which the Command
@@ -214,7 +252,7 @@ namespace DynamORM
#endregion IDbCommand Members
#region IDisposable Members
#region IExtendedDisposable Members
/// <summary>Performs application-defined tasks associated with
/// freeing, releasing, or resetting unmanaged resources.</summary>
@@ -231,9 +269,14 @@ namespace DynamORM
}
_command.Dispose();
IsDisposed = true;
}
}
#endregion IDisposable Members
/// <summary>Gets a value indicating whether this instance is disposed.</summary>
public bool IsDisposed { get; private set; }
#endregion IExtendedDisposable Members
}
}

View File

@@ -28,13 +28,14 @@
using System;
using System.Data;
using DynamORM.Helpers;
namespace DynamORM
{
/// <summary>Connection wrapper.</summary>
/// <remarks>This class is only connection holder, connection is managed by
/// <see cref="DynamicDatabase"/> instance.</remarks>
public class DynamicConnection : IDbConnection, IDisposable
public class DynamicConnection : IDbConnection, IExtendedDisposable
{
private DynamicDatabase _db;
private bool _singleTransaction;
@@ -48,6 +49,7 @@ namespace DynamORM
/// <param name="singleTransaction">Are we using single transaction mode? I so... act correctly.</param>
internal DynamicConnection(DynamicDatabase db, IDbConnection con, bool singleTransaction)
{
IsDisposed = false;
_db = db;
Connection = con;
_singleTransaction = singleTransaction;
@@ -147,11 +149,19 @@ namespace DynamORM
#endregion IDbConnection Members
#region IExtendedDisposable Members
/// <summary>Performs application-defined tasks associated with freeing,
/// releasing, or resetting unmanaged resources.</summary>
public void Dispose()
{
_db.Close(Connection);
IsDisposed = true;
}
/// <summary>Gets a value indicating whether this instance is disposed.</summary>
public bool IsDisposed { get; private set; }
#endregion IExtendedDisposable Members
}
}

View File

@@ -40,7 +40,7 @@ using DynamORM.Mapper;
namespace DynamORM
{
/// <summary>Dynamic database is a class responsible for managing database.</summary>
public class DynamicDatabase : IDisposable
public class DynamicDatabase : IExtendedDisposable
{
#region Internal fields and properties
@@ -127,6 +127,7 @@ namespace DynamORM
/// <param name="options">Connection options.</param>
public DynamicDatabase(DbProviderFactory provider, string connectionString, DynamicDatabaseOptions options)
{
IsDisposed = false;
_provider = provider;
InitCommon(connectionString, options);
@@ -137,6 +138,7 @@ namespace DynamORM
/// <param name="options">Connection options. <see cref="DynamicDatabaseOptions.SingleConnection"/> required.</param>
public DynamicDatabase(IDbConnection connection, DynamicDatabaseOptions options)
{
IsDisposed = false;
InitCommon(connection.ConnectionString, options);
TransactionPool.Add(connection, new Stack<IDbTransaction>());
@@ -643,7 +645,7 @@ namespace DynamORM
#endregion Transaction
#region IDisposable Members
#region IExtendedDisposable Members
/// <summary>Performs application-defined tasks associated with freeing,
/// releasing, or resetting unmanaged resources.</summary>
@@ -684,9 +686,13 @@ namespace DynamORM
// Clear pools
TransactionPool.Clear();
CommandsPool.Clear();
IsDisposed = true;
}
}
#endregion IDisposable Members
/// <summary>Gets a value indicating whether this instance is disposed.</summary>
public bool IsDisposed { get; private set; }
#endregion IExtendedDisposable Members
}
}

View File

@@ -798,9 +798,9 @@ namespace DynamORM
#region Dynamic extensions
/// <summary>Turns an <see cref="IDataReader"/> to a Dynamic list of things.</summary>
/// <param name="r">Reader from which read data..</param>
/// <param name="r">Reader from which read data.</param>
/// <returns>List of things.</returns>
public static List<dynamic> ToDynamicList(this IDataReader r)
public static List<dynamic> ToList(this IDataReader r)
{
var result = new List<dynamic>();
@@ -810,6 +810,23 @@ 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>
/// <returns>List of things.</returns>
public static List<T> ToList<T>(this IDynamicSelectQueryBuilder b) where T : class
{
return b.Execute<T>().ToList();
}
/// <summary>Turns the dictionary into an ExpandoObject.</summary>
/// <param name="d">Dictionary to convert.</param>
/// <returns>Converted dictionary.</returns>

View File

@@ -0,0 +1,128 @@
/*
* 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.Data;
using System.Runtime.Serialization;
using System.Text;
namespace DynamORM
{
/// <summary>Dynamic query exception.</summary>
public class DynamicQueryException : Exception, ISerializable
{
/// <summary>Gets the dumped command which failed.</summary>
public string Command { get; private set; }
/// <summary>Initializes a new instance of the
/// <see cref="DynamicQueryException"/> class.</summary>
/// <param name="command">The command which failed.</param>
public DynamicQueryException(IDbCommand command = null)
: base("Error executing command.")
{
if (command != null)
{
var sb = new StringBuilder();
command.Dump(sb);
Command = sb.ToString();
}
}
/// <summary>Initializes a new instance of the
/// <see cref="DynamicQueryException"/> class.</summary>
/// <param name="message">The message.</param>
/// <param name="command">The command which failed.</param>
public DynamicQueryException(string message, IDbCommand command = null)
: base(message)
{
SetCommand(command);
}
/// <summary>Initializes a new instance of the
/// <see cref="DynamicQueryException"/> class.</summary>
/// <param name="innerException">The inner exception.</param>
/// <param name="command">The command which failed.</param>
public DynamicQueryException(Exception innerException, IDbCommand command = null)
: base("Error executing command.", innerException)
{
SetCommand(command);
}
/// <summary>Initializes a new instance of the
/// <see cref="DynamicQueryException"/> class.</summary>
/// <param name="message">The message.</param>
/// <param name="innerException">The inner exception.</param>
/// <param name="command">The command which failed.</param>
public DynamicQueryException(string message, Exception innerException, IDbCommand command = null)
: base(message, innerException)
{
SetCommand(command);
}
/// <summary>Initializes a new instance of the
/// <see cref="DynamicQueryException"/> class.</summary>
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" />
/// that holds the serialized object data about the exception being thrown.</param>
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" />
/// that contains contextual information about the source or destination.</param>
public DynamicQueryException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
Command = info.GetString("Command");
}
/// <summary>When overridden in a derived class, sets the
/// <see cref="T:System.Runtime.Serialization.SerializationInfo" />
/// with information about the exception.</summary>
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" />
/// that holds the serialized object data about the exception being thrown.</param>
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" />
/// that contains contextual information about the source or destination.</param>
/// <PermissionSet>
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Read="*AllFiles*" PathDiscovery="*AllFiles*" />
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="SerializationFormatter" />
/// </PermissionSet>
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
if (!string.IsNullOrEmpty(Command))
info.AddValue("Command", Command);
}
private void SetCommand(IDbCommand command)
{
if (command != null && (!(command is DynamicCommand) || ((command is DynamicCommand) && !(command as DynamicCommand).IsDisposed)))
{
var sb = new StringBuilder();
command.Dump(sb);
Command = sb.ToString();
}
}
}
}

View File

@@ -194,7 +194,7 @@ namespace DynamORM
/// });</code>
/// <code>x.Delete(where: new { id = 14, code = 14 });</code>
/// </example>
public class DynamicTable : DynamicObject, IDisposable, ICloneable
public class DynamicTable : DynamicObject, IExtendedDisposable, ICloneable
{
private static HashSet<string> _allowedCommands = new HashSet<string>
{
@@ -243,6 +243,7 @@ namespace DynamORM
/// <param name="keys">Override keys in schema.</param>
public DynamicTable(DynamicDatabase database, string table = "", string owner = "", string[] keys = null)
{
IsDisposed = false;
Database = database;
TableName = Database.StripName(table);
OwnerName = Database.StripName(owner);
@@ -260,15 +261,20 @@ namespace DynamORM
if (type == null)
throw new ArgumentNullException("type", "Type can't be null.");
Database = database;
IsDisposed = false;
Database = database;
TableType = type;
var mapper = DynamicMapperCache.GetMapper(type);
if (mapper != null)
{
TableName = mapper.Table == null || string.IsNullOrEmpty(mapper.Table.Name) ?
type.Name : mapper.Table.Name;
OwnerName = mapper.Table == null || string.IsNullOrEmpty(mapper.Table.Name) ?
type.Name : mapper.Table.Name;
}
BuildAndCacheSchema(keys);
}
@@ -995,7 +1001,7 @@ namespace DynamORM
#endregion Universal Dynamic Invoker
#region IDisposable Members
#region IExtendedDisposable Members
/// <summary>Performs application-defined tasks associated with
/// freeing, releasing, or resetting unmanaged resources.</summary>
@@ -1007,9 +1013,14 @@ namespace DynamORM
Database.RemoveFromCache(this);
Database = null;
}
IsDisposed = true;
}
#endregion IDisposable Members
/// <summary>Gets a value indicating whether this instance is disposed.</summary>
public bool IsDisposed { get; private set; }
#endregion IExtendedDisposable Members
#region ICloneable Members

View File

@@ -28,11 +28,12 @@
using System;
using System.Data;
using DynamORM.Helpers;
namespace DynamORM
{
/// <summary>Helper class to easy manage transaction.</summary>
public class DynamicTransaction : IDbTransaction, IDisposable
public class DynamicTransaction : IDbTransaction, IExtendedDisposable
{
private DynamicDatabase _db;
private DynamicConnection _con;
@@ -48,6 +49,7 @@ namespace DynamORM
/// <param name="disposed">This action is invoked when transaction is disposed.</param>
internal DynamicTransaction(DynamicDatabase db, DynamicConnection con, bool singleTransaction, IsolationLevel? il, Action disposed)
{
IsDisposed = false;
_db = db;
_con = con;
_singleTransaction = singleTransaction;
@@ -125,6 +127,8 @@ namespace DynamORM
/// <summary>Gets <see cref="System.Data.IsolationLevel"/> for this transaction.</summary>
public IsolationLevel IsolationLevel { get; private set; }
#region IExtendedDisposable Members
/// <summary>Performs application-defined tasks associated with
/// freeing, releasing, or resetting unmanaged resources.</summary>
public void Dispose()
@@ -133,6 +137,13 @@ namespace DynamORM
if (_disposed != null)
_disposed();
IsDisposed = true;
}
/// <summary>Gets a value indicating whether this instance is disposed.</summary>
public bool IsDisposed { get; private set; }
#endregion IExtendedDisposable Members
}
}