Click here to Skip to main content
15,881,882 members
Articles / Hosted Services / Azure

Kerosene ORM: a dynamic, configuration-less and self-adaptive ORM for POCO objects supporting a SQL-like syntax from C#

Rate me:
Please Sign up or sign in to vote.
4.96/5 (71 votes)
1 Mar 2015CPOL35 min read 540.6K   4.6K   212  
The seventh version of the dynamic, configuration-less and self-adaptive Kerosene ORM library, that provides full real support for POCO objects, natural SQL-like syntax from C#, and advanced capabilities while being extremely easy to use.
// ======================================================== IDataQuery.cs
namespace Kerosene.ORM.Maps
{
	using Kerosene.Tools;
	using System;
	using System.Collections;
	using System.Collections.Generic;

	// ==================================================== 
	/// <summary>
	/// Represents a query operation to retrieve entities of the the type managed by its
	/// associated map.
	/// </summary>
	public interface IDataQuery : IMetaCommand, IEnumerable
	{
		/// <summary>
		/// Gets a new object able to execute this command and to enumerate through the entities
		/// produced by its execution.
		/// </summary>
		/// <returns>A new enumerator.</returns>
		new IDataQueryEnumerator GetEnumerator();

		/// <summary>
		/// Defines the contents of the TOP clause.
		/// </summary>
		/// <param name="top">An integer with the value to set for the TOP clause. A value of cero
		/// or negative merely removes this clause.</param>
		/// <returns>This instance to permit a fluent syntax chaining.</returns>
		IDataQuery Top(int top);

		/// <summary>
		/// Defines the contents of the WHERE clause, or appends the new ones to any previous
		/// specification.
		/// </summary>
		/// <param name="where">The dynamic lambda expression that resolves into the contents of
		/// this clause.
		/// <para>- By default, if any previous contents exist the new ones are appended using an
		/// AND logical operator. However, the virtual extension methods 'x => x.And(...)' and
		/// 'x => x.Or(...)' can be used to specify the concrete logical operator to use for
		/// concatenation purposes.</para>
		/// </param>
		/// <returns>This instance to permit a fluent syntax chaining.</returns>
		IDataQuery Where(Func<dynamic, object> where);

		/// <summary>
		/// Defines the contents of the ORDER BY one, or appends the new ones to any previous
		/// specification.
		/// </summary>
		/// <param name="orderbys">The collection of dynamic lambda expressions that resolve into
		/// the contents of this clause:
		/// <para>- A string as in 'x => x.Table.Column ORDER' where both the table and order
		/// parts are optional. If no order part is present then a default ASCENDING one is used.</para>
		/// <para>- A string as in 'x => "Table.Column.Order()"', where both the table and order
		/// parts are optional. The order part can be any among the 'Asc()', 'Ascending()',
		/// 'Desc()' or 'Descending()' ones.</para>
		/// </param>
		/// <returns>This instance to permit a fluent syntax chaining.</returns>
		IDataQuery OrderBy(params Func<dynamic, object>[] orderbys);

		/// <summary>
		/// Defines the contents of the SKIP clause.
		/// </summary>
		/// <param name="skip">An integer with the value to set for the SKIP clause. A value of
		/// cero or negative merely removes this clause.</param>
		/// <returns>This instance to permit a fluent syntax chaining.</returns>
		IDataQuery Skip(int skip);

		/// <summary>
		/// Defines the contents of the TAKE clause.
		/// </summary>
		/// <param name="take">An integer with the value to set for the SKIP clause. A value of
		/// cero or negative value merely removes this clause.</param>
		/// <returns>This instance to permit a fluent syntax chaining.</returns>
		IDataQuery Take(int take);

		/// <summary>
		/// Defines the alias to be used with the master table in case extended logic for
		/// this command is used.
		/// </summary>
		/// <param name="alias">If not null a dynamic lambda expression that resolves into the
		/// alias of the master table to support extended logic for this command.</param>
		/// <returns>This instance to permit a fluent syntax chaining.</returns>
		IDataQuery MasterAlias(Func<dynamic, object> alias);

		/// <summary>
		/// Defines additional contents for the FROM clause to support extended logic for
		/// this command.
		/// </summary>
		/// <param name="froms">The collection of lambda expressions that resolve into the
		/// elements to include in this clause:
		/// <para>- A string, as in 'x => "name AS alias"', where the alias part is optional.</para>
		/// <para>- A table specification, as in 'x => x.Table.As(alias)', where both the alias part
		/// is optional.</para>
		/// <para>- Any expression that can be parsed into a valid SQL sentence for this clause.</para>
		/// </param>
		/// <returns>This instance to permit a fluent syntax chaining.</returns>
		IDataQuery From(params Func<dynamic, object>[] froms);

		/// <summary>
		/// Defines additional contents for the JOIN clause to support extended logic for
		/// this command.
		/// </summary>
		/// <param name="join">The dynamic lambda expression that resolves into the contents of
		/// this clause:
		/// <para>- A string, as in 'x => "jointype table AS alias ON condition"', where both the
		/// jointype and the alias parts are optional. If no jointype is used then a default JOIN
		/// one is used.</para>
		/// <para>- A dynamic specification as in 'x => x.Table.As(Alias).On(condition)' where
		/// the alias part is optional.</para>
		/// <para>- A dynamic specification containing a non-default join operation can be
		/// specified using the 'x => x(jointype).Table...' syntax, where the orphan invocation
		/// must be the first one in the chain.</para>
		/// </param>
		/// <returns>This instance to permit a fluent syntax chaining.</returns>
		IDataQuery Join(Func<dynamic, object> join);

		/// <summary>
		/// Defines the contents of the GROUP BY clause to support extended logic for this
		/// command.
		/// </summary>
		/// <param name="groupbys">The collection of dynamic lambda expressions that resolve into
		/// the contents of this clause:
		/// <para>- A string as in 'x => "Table.Column"', where the table part is optional.</para>
		/// </param>
		/// <returns>This instance to permit a fluent syntax chaining.</returns>
		IDataQuery GroupBy(params Func<dynamic, object>[] groupbys);

		/// <summary>
		/// Defines the contents of the HAVING clause of the GROUP BY one to support extended
		/// logic for this command.
		/// </summary>
		/// <param name="having">The dynamic lambda expression that resolves into the contents of
		/// this clause.
		/// <para>- By default, if any previous contents exist the new ones are appended using an
		/// AND logical operator. However, the virtual extension methods 'x => x.And(...)' and
		/// 'x => x.Or(...)' can be used to specify the concrete logical operator to use for
		/// concatenation purposes.</para>
		/// </param>
		/// <returns>This instance to permit a fluent syntax chaining.</returns>
		IDataQuery Having(Func<dynamic, object> having);

		/// <summary>
		/// Executes this command and returns a list containing the results.
		/// </summary>
		/// <returns>A list containing the results requested.</returns>
		IList ToList();

		/// <summary>
		/// Executes this command and returns aa arrays containing the results.
		/// </summary>
		/// <returns>An array containing the results requested.</returns>
		object[] ToArray();

		/// <summary>
		/// Executes this command and returns the first instance produced, or null if no one
		/// was produced.
		/// </summary>
		/// <returns>The first instance produced, or null.</returns>
		object First();

		/// <summary>
		/// Executes this command and returns the first instance produced, or null if no one
		/// was produced.
		/// <para>
		/// This method is provided as a fall-back mechanism because it retrieves all possible
		/// results discarding them until the last one is found. Client applications have to
		/// reconsider the logic of their command to avoid using this method if possible.
		/// </para>
		/// </summary>
		/// <returns>The first instance produced, or null.</returns>
		object Last();
	}

	// ==================================================== 
	/// <summary>
	/// Represents a query operation to retrieve entities of the the type managed by its
	/// associated map.
	/// </summary>
	public interface IDataQuery<T> : IDataQuery, IMetaCommand<T>, IEnumerable<T> where T : class
	{
		/// <summary>
		/// Gets a new object able to execute this command and to enumerate through the entities
		/// produced by its execution.
		/// </summary>
		/// <returns>A new enumerator.</returns>
		new IDataQueryEnumerator<T> GetEnumerator();

		/// <summary>
		/// Defines the contents of the TOP clause.
		/// </summary>
		/// <param name="top">An integer with the value to set for the TOP clause. A value of cero
		/// or negative merely removes this clause.</param>
		/// <returns>This instance to permit a fluent syntax chaining.</returns>
		new IDataQuery<T> Top(int top);

		/// <summary>
		/// Defines the contents of the WHERE clause, or appends the new ones to any previous
		/// specification.
		/// </summary>
		/// <param name="where">The dynamic lambda expression that resolves into the contents of
		/// this clause.
		/// <para>- By default, if any previous contents exist the new ones are appended using an
		/// AND logical operator. However, the virtual extension methods 'x => x.And(...)' and
		/// 'x => x.Or(...)' can be used to specify the concrete logical operator to use for
		/// concatenation purposes.</para>
		/// </param>
		/// <returns>This instance to permit a fluent syntax chaining.</returns>
		new IDataQuery<T> Where(Func<dynamic, object> where);

		/// <summary>
		/// Defines the contents of the ORDER BY one, or appends the new ones to any previous
		/// specification.
		/// </summary>
		/// <param name="orderbys">The collection of dynamic lambda expressions that resolve into
		/// the contents of this clause:
		/// <para>- A string as in 'x => x.Table.Column ORDER' where both the table and order
		/// parts are optional. If no order part is present then a default ASCENDING one is used.</para>
		/// <para>- A string as in 'x => "Table.Column.Order()"', where both the table and order
		/// parts are optional. The order part can be any among the 'Asc()', 'Ascending()',
		/// 'Desc()' or 'Descending()' ones.</para>
		/// </param>
		/// <returns>This instance to permit a fluent syntax chaining.</returns>
		new IDataQuery<T> OrderBy(params Func<dynamic, object>[] orderbys);

		/// <summary>
		/// Defines the contents of the SKIP clause.
		/// </summary>
		/// <param name="skip">An integer with the value to set for the SKIP clause. A value of
		/// cero or negative merely removes this clause.</param>
		/// <returns>This instance to permit a fluent syntax chaining.</returns>
		new IDataQuery<T> Skip(int skip);

		/// <summary>
		/// Defines the contents of the TAKE clause.
		/// </summary>
		/// <param name="take">An integer with the value to set for the SKIP clause. A value of
		/// cero or negative value merely removes this clause.</param>
		/// <returns>This instance to permit a fluent syntax chaining.</returns>
		new IDataQuery<T> Take(int take);

		/// <summary>
		/// Defines the alias to be used with the master table in case extended logic for
		/// this command is used.
		/// </summary>
		/// <param name="alias">If not null a dynamic lambda expression that resolves into the
		/// alias of the master table to support extended logic for this command.</param>
		/// <returns>This instance to permit a fluent syntax chaining.</returns>
		new IDataQuery<T> MasterAlias(Func<dynamic, object> alias);

		/// <summary>
		/// Defines additional contents for the FROM clause to support extended logic for
		/// this command.
		/// </summary>
		/// <param name="froms">The collection of lambda expressions that resolve into the
		/// elements to include in this clause:
		/// <para>- A string, as in 'x => "name AS alias"', where the alias part is optional.</para>
		/// <para>- A table specification, as in 'x => x.Table.As(alias)', where both the alias part
		/// is optional.</para>
		/// <para>- Any expression that can be parsed into a valid SQL sentence for this clause.</para>
		/// </param>
		/// <returns>This instance to permit a fluent syntax chaining.</returns>
		new IDataQuery<T> From(params Func<dynamic, object>[] froms);

		/// <summary>
		/// Defines additional contents for the JOIN clause to support extended logic for
		/// this command.
		/// </summary>
		/// <param name="join">The dynamic lambda expression that resolves into the contents of
		/// this clause:
		/// <para>- A string, as in 'x => "jointype table AS alias ON condition"', where both the
		/// jointype and the alias parts are optional. If no jointype is used then a default JOIN
		/// one is used.</para>
		/// <para>- A dynamic specification as in 'x => x.Table.As(Alias).On(condition)' where
		/// the alias part is optional.</para>
		/// <para>- A dynamic specification containing a non-default join operation can be
		/// specified using the 'x => x(jointype).Table...' syntax, where the orphan invocation
		/// must be the first one in the chain.</para>
		/// </param>
		/// <returns>This instance to permit a fluent syntax chaining.</returns>
		new IDataQuery<T> Join(Func<dynamic, object> join);

		/// <summary>
		/// Defines the contents of the GROUP BY clause to support extended logic for this
		/// command.
		/// </summary>
		/// <param name="groupbys">The collection of dynamic lambda expressions that resolve into
		/// the contents of this clause:
		/// <para>- A string as in 'x => "Table.Column"', where the table part is optional.</para>
		/// </param>
		/// <returns>This instance to permit a fluent syntax chaining.</returns>
		new IDataQuery<T> GroupBy(params Func<dynamic, object>[] groupbys);

		/// <summary>
		/// Defines the contents of the HAVING clause of the GROUP BY one to support extended
		/// logic for this command.
		/// </summary>
		/// <param name="having">The dynamic lambda expression that resolves into the contents of
		/// this clause.
		/// <para>- By default, if any previous contents exist the new ones are appended using an
		/// AND logical operator. However, the virtual extension methods 'x => x.And(...)' and
		/// 'x => x.Or(...)' can be used to specify the concrete logical operator to use for
		/// concatenation purposes.</para>
		/// </param>
		/// <returns>This instance to permit a fluent syntax chaining.</returns>
		new IDataQuery<T> Having(Func<dynamic, object> having);

		/// <summary>
		/// Executes this command and returns a list containing the results.
		/// </summary>
		/// <returns>A list containing the results requested.</returns>
		new List<T> ToList();

		/// <summary>
		/// Executes this command and returns aa arrays containing the results.
		/// </summary>
		/// <returns>An array containing the results requested.</returns>
		new T[] ToArray();

		/// <summary>
		/// Executes this command and returns the first instance produced, or null if no one
		/// was produced.
		/// </summary>
		/// <returns>The first instance produced, or null.</returns>
		new T First();

		/// <summary>
		/// Executes this command and returns the first instance produced, or null if no one
		/// was produced.
		/// <para>
		/// This method is provided as a fall-back mechanism because it retrieves all possible
		/// results discarding them until the last one is found. Client applications have to
		/// reconsider the logic of their command to avoid using this method if possible.
		/// </para>
		/// </summary>
		/// <returns>The first instance produced, or null.</returns>
		new T Last();
	}
}
// ======================================================== 

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Spain Spain
mbarbac has worked in start-ups, multinational tech companies, and consulting ones, serving as CIO, CTO, SW Development Director, and Consulting Director, among many other roles.

Solving complex puzzles and getting out of them business value has ever been among his main interests - and that's why he has spent his latest 25 years trying to combine his degree in Theoretical Physics with his MBA... and he is still trying to figure out how all these things can fit together.

Even if flying a lot across many countries, along with the long working days that are customary in IT management and Consultancy, he can say that, after all, he lives in Spain (at least the weekends).

Comments and Discussions