Click here to Skip to main content
15,886,842 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 542.2K   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.
// ======================================================== 
namespace Kerosene.ORM.WCF
{
	using Kerosene.ORM.Core;
	using Kerosene.ORM.Core.Concrete;
	using Kerosene.Tools;
	using System;
	using System.ServiceModel;
	using System.Text;

	// ==================================================== 
	/// <summary>
	/// Represents the client side of a WCF connection, or data context, with an arbitrary underlying database
	/// managed by the specific server-side proxy this instance is associated with, and acts as a factory to create
	/// specific objects adapted to it.
	/// </summary>
	public class KLinkWCF : KLinkBase, IKLink
	{
		IKProxyWCF _Proxy = null; Guid _ProxyId = Guid.Empty;
		string _EndPoint = null;
		DeepObject _Package = null;

		/// <summary>
		/// Creates a new non-connected client instance.
		/// </summary>
		/// <param name="engine">The engine that describes the server side underlying database characteristics.</param>
		public KLinkWCF(IKEngine engine) :
			base(engine, KTransactionMode.Database) { }

		/// <summary>
		/// Creates a new client instance and connects it with the server side proxy.
		/// </summary>
		/// <param name="engine">The engine that describes the server side underlying database characteristics.</param>
		/// <param name="endpoint">The endpoint representing the server proxy to connect to.</param>
		/// <param name="package">An optional package of arbitrary information this client can send to the server
		/// side proxy in order it to tailor the details of the services it will provide to this client.</param>
		public KLinkWCF(IKEngine engine, string endpoint, DeepObject package = null)
			: this(engine)
		{
			Connect(endpoint, package);
		}

		/// <summary>
		/// Factory method to create a transaction of the appropriate type for this instance.
		/// </summary>
		/// <param name="mode">The initial mode of the transaction.</param>
		protected override IKTransaction CreateTransaction(KTransactionMode mode)
		{
			return new KTransactionWCF(this);
		}

		protected override void OnDispose(bool disposing)
		{
			if (!IsDisposed && disposing)
			{
				if (IsConnected) Disconnect();
				_Package = null;
				_Proxy = null;
			}
			base.OnDispose(disposing);
		}

		/// <summary>
		/// Returns the string representation of this instance.
		/// </summary>
		public override string ToString()
		{
			StringBuilder sb = new StringBuilder();
			if (IsDisposed) sb.Append("disposed::[");

			sb.AppendFormat("{0}({1}: {2})",
				GetType().EasyName(),
				Proxy == null ? "-" : ProxyId.TagString(),
				Engine == null ? "-" : Engine.ToString());

			if (IsDisposed) sb.Append("]");
			return sb.ToString();
		}

		/// <summary>
		/// Returns a new instance that is a copy of this original one.
		/// </summary>
		public KLinkWCF Clone()
		{
			if (IsDisposed) throw new ObjectDisposedException(this.ToString());
			var cloned = new KLinkWCF(Engine);
			OnClone(cloned);

			if (IsConnected) cloned.Connect(_EndPoint, _Package); // Connecting the clone if the original was...
			return cloned;
		}
		IKLink IKLink.Clone()
		{
			return this.Clone();
		}
		object ICloneable.Clone()
		{
			return this.Clone();
		}

		protected override void OnClone(object cloned)
		{
			var temp = (KLinkWCF)cloned;
			base.OnClone(temp);
		}

		/// <summary>
		/// Gets the transaction instance created for this link.
		/// </summary>
		public new KTransactionWCF Transaction
		{
			get { return (KTransactionWCF)base.Transaction; }
		}
		IKTransaction IKLink.Transaction
		{
			get { return this.Transaction; }
		}

		/// <summary>
		/// Connects this client with a server side proxy. If this instance was already connected an exception is
		/// thrown.
		/// </summary>
		/// <param name="endpoint">The endpoint representing the server proxy to connect to.</param>
		/// <param name="package">An optional package of arbitrary information this client can send to the server
		/// side proxy in order it to tailor the details of the services it will provide to this client.</param>
		public void Connect(string endpoint, DeepObject package = null)
		{
			if (IsDisposed) throw new ObjectDisposedException(this.ToString());
			endpoint = endpoint.Validated("EndPoint");

			if (IsConnected) throw new InvalidOperationException(string.Format("This '{0}' is already connected.", this));
			_EndPoint = endpoint;
			_Package = package;

			var channelFactory = new ChannelFactory<IKProxyWCF>(_EndPoint);
			_Proxy = channelFactory.CreateChannel();
			_ProxyId = _Proxy.OnProxyConnect(_Package);
		}

		/// <summary>
		/// Disconnects this client from the server side proxy it is connected with. If this instance was not
		/// connected already, this method is just ignored.
		/// </summary>
		public void Disconnect()
		{
			if (_Proxy != null)
			{
				_Proxy.OnProxyDisconnect();
				_Proxy = null;
				_ProxyId = Guid.Empty;
				_EndPoint = null;
				_Package = null;
			}
		}

		/// <summary>
		/// Gets whether this client is currently connected with a server side proxy, or not.
		/// </summary>
		public bool IsConnected
		{
			get { return !(_Proxy == null); }
		}

		/// <summary>
		/// Gets the server side proxy this client is connected with, or null if this instance is not connected or
		/// if it was disposed.
		/// </summary>
		public IKProxyWCF Proxy
		{
			get { return _Proxy; }
		}

		/// <summary>
		/// Gets the cached server side proxy id. This value is meaningful only if this instance is currently
		/// connected.
		/// </summary>
		public Guid ProxyId
		{
			get { return _ProxyId; }
		}

		/// <summary>
		/// Gets the package or arbitrary information this client used to connect to the server side proxy, or
		/// null if no one was used, if this instance is not connected, or if it was disposed.
		/// </summary>
		public DeepObject Package
		{
			get { return _Package; }
		}

		/// <summary>
		/// If this instance is connected this property maintains the endpoint used to connect to
		/// the server side proxy service.
		/// </summary>
		public string EndPoint
		{
			get { return _EndPoint; }
		}

		/// <summary>
		/// Opens the connection with the underlying database.
		/// <para>If the connection was already opened this method throws an exception.</para>
		/// </summary>
		public override void Open()
		{

			if (IsDisposed) throw new ObjectDisposedException(this.ToString());
			if (IsOpen) throw new InvalidOperationException(string.Format("This '{0}' is already open.", this));
			if (!IsConnected) throw new InvalidOperationException(string.Format("This '{0}' is not connected.", this));

			_Proxy.OnLinkOpen();
		}

		/// <summary>
		/// Closes the connection with the underlying database.
		/// <para>If the connection was not opened this method is merely ignored.</para>
		/// </summary>
		public override void Close()
		{
			if (IsDisposed) throw new ObjectDisposedException(this.ToString());
			if (!IsOpen) return;
			if (!IsConnected) return;

			_Proxy.OnLinkClose();
		}

		/// <summary>
		/// Gets whether the connection with the underlying database is opened or not.
		/// </summary>
		public override bool IsOpen
		{
			get
			{
				if (IsDisposed) return false;
				if (!IsConnected) return false;

				return _Proxy.OnLinkIsOpen();
			}
		}

		/// <summary>
		/// Factory method to create an enumerator for the given command.
		/// <para>If the command's link is not the same as this one an exception is thrown.</para>
		/// </summary>
		/// <param name="command">The command.</param>
		public KEnumeratorWCF CreateEnumerator(IKEnumerable command)
		{
			if (command == null) throw new ArgumentNullException("command", "Command cannot be null.");
			if (command.Link == null) throw new ArgumentException(string.Format("Link of command '{0}' is null."));

			if (!object.ReferenceEquals(this, command.Link)) throw new InvalidOperationException(
				 string.Format("Link '{0}' of command '{1}' is not this link '{2}'", command.Link, command, this));

			return new KEnumeratorWCF(command);
		}
		IKEnumerator IKLink.CreateEnumerator(IKEnumerable command)
		{
			return this.CreateEnumerator(command);
		}

		/// <summary>
		/// Factory method to create an executor for the given command.
		/// <para>If the command's link is not the same as this one an exception is thrown.</para>
		/// </summary>
		/// <param name="command">The command.</param>
		public KExecutorWCF CreateExecutor(IKExecutable command)
		{
			if (command == null) throw new ArgumentNullException("command", "Command cannot be null.");
			if (command.Link == null) throw new ArgumentException(string.Format("Link of command '{0}' is null."));

			if (!object.ReferenceEquals(this, command.Link)) throw new InvalidOperationException(
				 string.Format("Link '{0}' of command '{1}' is not this link '{2}'", command.Link, command, this));

			return new KExecutorWCF(command);
		}
		IKExecutor IKLink.CreateExecutor(IKExecutable command)
		{
			return this.CreateExecutor(command);
		}
	}
}
// ======================================================== 

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