// ========================================================
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);
}
}
}
// ========================================================