/********************************************************************
* FulcrumWeb RAD Framework - Fulcrum of your business *
* Copyright (c) 2002-2009 FulcrumWeb, ALL RIGHTS RESERVED *
* *
* THE SOURCE CODE CONTAINED WITHIN THIS FILE AND ALL RELATED *
* FILES OR ANY PORTION OF ITS CONTENTS SHALL AT NO TIME BE *
* COPIED, TRANSFERRED, SOLD, DISTRIBUTED, OR OTHERWISE MADE *
* AVAILABLE TO OTHER INDIVIDUALS WITHOUT EXPRESS WRITTEN CONSENT *
* AND PERMISSION FROM FULCRUMWEB. CONSULT THE END USER LICENSE *
* AGREEMENT FOR INFORMATION ON ADDITIONAL RESTRICTIONS. *
********************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Framework.Silverlight.Client.AppServer;
namespace Framework.Silverlight.Client
{
/// <summary>
/// Represents base features for Entity
/// </summary>
public class _CxBaseEntity : IxDataObject, IxValueProvider
{
/// <summary>
/// Occurs when Attribute with depended attributes had changed.
/// </summary>
public event DxDependencyAttributeChanged DependencyAttributeChanged;
//----------------------------------------------------------------------------
/// <summary>
/// Occurs when data had change.
/// </summary>
public event EventHandler DataChanged;
//----------------------------------------------------------------------------
/// <summary>
/// Occurs when changes of data was commited.
/// </summary>
public event EventHandler DataCommited;
//----------------------------------------------------------------------------
/// <summary>
/// Occurs when changes of data was reverted.
/// </summary>
public event EventHandler DataReverted;
//----------------------------------------------------------------------------
private readonly Dictionary<string, CxAttributeValue> m_Attributes = new Dictionary<string, CxAttributeValue>();
private bool m_IsDisposed;
private readonly Dictionary<string, CxAttributeValue> m_PrimaryKeys = new Dictionary<string, CxAttributeValue>();
private CxEntityUsage m_EntityUsage;
private int m_ChangesCounter;
//----------------------------------------------------------------------------
/// <summary>
/// Validates all atrributes values.
/// </summary>
protected virtual void Validate()
{
foreach (CxAttributeValue attribute in m_Attributes.Values)
{
attribute.Validate();
}
}
//----------------------------------------------------------------------------
/// <summary>
/// Sets the value to dynamically generated property.
/// </summary>
/// <param name="attrValue">The property value.</param>
public virtual void SetDynamicProperty(CxAttributeValue attrValue)
{
PropertyInfo pInfo = GetType().GetProperty(attrValue.DynamicPropertyName);
if (pInfo == null)
{
throw new ExApplicationException("Dynamic property with name '{0}' does " +
"not exists in dynamic class '{1}'.", attrValue.DynamicPropertyName, GetType().Name);
}
pInfo.SetValue(this, attrValue, null);
m_Attributes.Add(attrValue.AttributeId, attrValue);
if (attrValue.IsPrimaryKey)
{
m_PrimaryKeys.Add(attrValue.AttributeId.ToUpper(), attrValue);
}
attrValue.DataChanged += Value_DataChanged;
attrValue.DataCommited += Value_DataCommited;
attrValue.DataReverted += Value_DataReverted;
}
//----------------------------------------------------------------------------
/// <summary>
/// Sets rowsource.
/// </summary>
/// <param name="unfilteredRowSources">Dictionary of unfiltered rowsources.</param>
/// <param name="filteredRowSources">List of unfiltered rowsources.</param>
public void SetRowSources(
IDictionary<string, CxClientRowSource> unfilteredRowSources,
IList<CxClientRowSource> filteredRowSources)
{
foreach (CxAttributeValue attrValue in m_Attributes.Values)
{
if (unfilteredRowSources.ContainsKey(attrValue.AttributeMetadata.RowSourceId) &&
!attrValue.AttributeMetadata.HasRowSourceFilter)
{
attrValue.RowSource = unfilteredRowSources[attrValue.AttributeMetadata.RowSourceId];
}
}
foreach (CxClientRowSource rowSource in filteredRowSources)
{
if (m_Attributes.ContainsKey(rowSource.OwnerAttributeId))
{
m_Attributes[rowSource.OwnerAttributeId].RowSource = rowSource;
}
}
}
//----------------------------------------------------------------------------
/// <summary>
/// Handles the CxAttributeValue.DataReverted event.
/// </summary>
void Value_DataReverted(object sender, EventArgs e)
{
m_ChangesCounter--;
if (m_ChangesCounter == 0)
{
OnDataReverted();
}
}
//----------------------------------------------------------------------------
/// <summary>
/// Handles the CxAttributeValue.DataCommited event.
/// </summary>
void Value_DataCommited(object sender, EventArgs e)
{
m_ChangesCounter--;
if (m_ChangesCounter == 0)
{
OnDataCommited();
}
}
//----------------------------------------------------------------------------
/// <summary>
/// Handles the CxAttributeValue.DataChanged event.
/// </summary>
void Value_DataChanged(object sender, EventArgs e)
{
m_ChangesCounter++;
OnDataChanged();
CxAttributeValue attributeValue = (CxAttributeValue)sender;
if (attributeValue.HasDependencies)
{
OnDependencyAttributeChanged(new CxDependencyAttributeChangedEventArgs(attributeValue.AttributeId));
}
}
//----------------------------------------------------------------------------
/// <summary>
/// Raises the DependencyAttributeChanged event.
/// </summary>
protected virtual void OnDependencyAttributeChanged(CxDependencyAttributeChangedEventArgs e)
{
if (DependencyAttributeChanged != null)
{
DependencyAttributeChanged(this, e);
}
}
//----------------------------------------------------------------------------
/// <summary>
/// Raises the DataChanged event.
/// </summary>
protected virtual void OnDataChanged()
{
if (DataChanged != null)
{
DataChanged(this, EventArgs.Empty);
}
}
//----------------------------------------------------------------------------
/// <summary>
/// Raises the DataReverted event.
/// </summary>
protected virtual void OnDataReverted()
{
if (DataReverted != null)
{
DataReverted(this, EventArgs.Empty);
}
}
//----------------------------------------------------------------------------
/// <summary>
/// Raises the DataCommited event.
/// </summary>
protected virtual void OnDataCommited()
{
if (DataCommited != null)
{
DataCommited(this, EventArgs.Empty);
}
}
//----------------------------------------------------------------------------
/// <summary>
/// Commits all the changes made to this DataObject since
/// the last time AcceptChanges() was called.
/// <param name="validate">True - validate befor accepting.</param>
/// </summary>
public void AcceptChanges(bool validate)
{
foreach (IxDataObject attribute in m_Attributes.Values)
{
attribute.AcceptChanges(validate);
}
m_ChangesCounter = 0;
}
//----------------------------------------------------------------------------
/// <summary>
/// Commits all changes made to this DataObject since
/// the last time AcceptChanges() was called.
/// </summary>
public void AcceptChanges()
{
AcceptChanges(true);
}
//----------------------------------------------------------------------------
/// <summary>
/// Rolls back all changes that have been made to the
/// DataObject since the last time AcceptChanges() was called.
/// </summary>
public void RejectChanges()
{
foreach (IxDataObject attribute in m_Attributes.Values)
{
attribute.RejectChanges();
}
m_WaitingToUpload.Clear();
m_ChangesCounter = 0;
}
//----------------------------------------------------------------------------
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing,
/// or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
if (!m_IsDisposed)
{
foreach (IxDataObject attribute in m_Attributes.Values)
{
attribute.DataChanged -= Value_DataChanged;
attribute.DataCommited -= Value_DataCommited;
attribute.DataReverted -= Value_DataReverted;
}
foreach (KeyValuePair<string, CxFileToUpload> pair in m_WaitingToUpload)
{
pair.Value.Stream.Close();
}
m_IsDisposed = true;
}
}
//----------------------------------------------------------------------------
/// <summary>
/// Gets the value from dynamically generated property.
/// </summary>
/// <param name="attributeName">Entity Atrtribute name.</param>
/// <returns></returns>
private object GetDynamicPropertyValue(string attributeName)
{
return GetDynamicProperty(attributeName).Value;
}
//----------------------------------------------------------------------------
/// <summary>
/// Gets dynamically generated property.
/// </summary>
/// <param name="attributeId">Entity Atrtribute Id.</param>
/// <returns>Instance of CxAttributeValue</returns>
public CxAttributeValue GetDynamicProperty(string attributeId)
{
string upperName = attributeId.ToUpper();
if (!m_Attributes.ContainsKey(upperName))
{
throw new ExApplicationException("Property that associated with attribute '{0}' does " +
"not exists in dynamic class '{1}'.", attributeId, GetType().Name);
}
return m_Attributes[upperName];
}
//----------------------------------------------------------------------------
/// <summary>
/// Indexed property to get value by name.
/// </summary>
public object this[string name]
{
get { return GetDynamicPropertyValue(name); }
}
//----------------------------------------------------------------------------
/// <summary>
/// Compares primary key(s) values with specified.
/// </summary>
/// <param name="pkList">List of values to compare.</param>
/// <returns>Returns true if primary key(s) is identical or false if otherwise. </returns>
public virtual bool ComparePk(IList<CxAttributeSnapShot> pkList)
{
if (m_PrimaryKeys.Count != pkList.Count)//Primary keys count is different
{
return false;
}
foreach (CxAttributeSnapShot pkSnapShot in pkList)
{
if (!m_PrimaryKeys.ContainsKey(pkSnapShot.AttributeId))// Primary keys names is different
{
return false;
}
}
foreach (CxAttributeSnapShot pkSnapShot in pkList)
{
if (!m_PrimaryKeys[pkSnapShot.AttributeId].Equals(pkSnapShot.AttributeValue))// Primary keys value is different
{
return false;
}
}
return true;
}
//----------------------------------------------------------------------------
/// <summary>
/// Compares primary key(s) values with specified.
/// </summary>
/// <param name="pkList">List of values to compare.</param>
/// <returns>Returns true if primary key(s) is identical or false if otherwise. </returns>
public virtual bool ComparePk(IDictionary<string, object> pkList)
{
if (m_PrimaryKeys.Count != pkList.Count)//Primary keys count is different
{
return false;
}
foreach (KeyValuePair<string, object> pk in pkList)
{
if (!m_PrimaryKeys.ContainsKey(pk.Key))
// Primary keys are different fields
return false;
// Initialize the primary key value.
CxAttributeValue entityPkValue = m_PrimaryKeys[pk.Key];
if (entityPkValue.Value == null && pk.Value == null)
continue;
if (entityPkValue.Value == null || pk.Value == null)
return false;
if (!entityPkValue.Value.Equals(pk.Value))
return false;
}
return true;
}
//----------------------------------------------------------------------------
/// <summary>
/// Gets CxEntityUsage that associates with this Entity.
/// </summary>
public CxEntityUsage EntityUsage
{
get { return m_EntityUsage; }
internal set { m_EntityUsage = value; }
}
//----------------------------------------------------------------------------
/// <summary>
/// Returns command with the given name.
/// </summary>
/// <param name="commandId">command Id</param>
/// <returns>command with the given name</returns>
public CxCommand GetCommand(string commandId)
{
IList<CxClientCommandMetadata> result = (from commandMetadata in EntityUsage.EntityMetadata.Commands
where commandMetadata.Id == commandId
select commandMetadata).ToArray();
if (result.Count == 0)
{
throw new ExApplicationException("Command with Id '{0}' is not defined in" +
"EntityUsage with Id '{1}'.", commandId, EntityUsage.EntityMetadata.Id);
}
CxCommand cmd = new CxCommand(result[0]);
return cmd;
}
//----------------------------------------------------------------------------
/// <summary>
/// Gets dictionary of primary keys.
/// key - key name; value - key value;
/// </summary>
public IDictionary<string, CxAttributeValue> PrimaryKeys
{
get { return m_PrimaryKeys; }
}
//----------------------------------------------------------------------------
/// <summary>
/// Gets value that defines, has this entity uncommitted changes or not.
/// </summary>
public bool HasChanges
{
get { return m_ChangesCounter > 0; }
}
//----------------------------------------------------------------------------
/// <summary>
/// Gets or sets value that defines there was DataObject is commited.
/// </summary>
public bool IsCommited { get; set; }
//----------------------------------------------------------------------------
/// <summary>
/// Refreshes entity by given CxExpressionResult.
/// </summary>
/// <param name="exprResult">CxExpressionResult with fresh values.</param>
public void Refresh(CxExpressionResult exprResult)
{
foreach (KeyValuePair<CxClientAttributeMetadata, object> pair in exprResult.Entity)
{
m_Attributes[pair.Key.Id].Value = pair.Value;
}
foreach (CxClientRowSource rs in exprResult.FilteredRowSources)
{
m_Attributes[rs.OwnerAttributeId].RowSource = rs;
}
foreach (KeyValuePair<string, CxAttributeValue> pair in m_Attributes)
{
if (!string.IsNullOrEmpty(pair.Value.AttributeMetadata.RowSourceId) &&
exprResult.UnfilteredRowSources.ContainsKey(pair.Value.AttributeMetadata.RowSourceId))
{
pair.Value.RowSource = exprResult.UnfilteredRowSources[pair.Value.AttributeMetadata.RowSourceId];
}
}
}
//----------------------------------------------------------------------------
/// <summary>
/// Dictionary of waiting to upload streams.
/// key - attribute id; value - stream to upload;
/// </summary>
private readonly Dictionary<string, CxFileToUpload> m_WaitingToUpload = new Dictionary<string, CxFileToUpload>();
//----------------------------------------------------------------------------
/// <summary>
/// Adds Stream to upload.
/// </summary>
/// <param name="stream">Stream to upload</param>
/// <param name="attributeId">Attribute id, appropriated with given Stream.</param>
/// <param name="fileName">File name.</param>
public virtual void AddStreamToUpload(Stream stream, string attributeId, string fileName)
{
if (!m_WaitingToUpload.ContainsKey(attributeId))
{
m_WaitingToUpload.Add(attributeId, new CxFileToUpload { Stream = stream, FileName = fileName });
}
else
{
m_WaitingToUpload[attributeId].Stream.Close();
m_WaitingToUpload[attributeId] = new CxFileToUpload { Stream = stream, FileName = fileName };
}
CxAttributeValue attrValue = m_Attributes[attributeId];
CxClientAttributeMetadata attrMetadata = attrValue.AttributeMetadata;
//set file name in appropriated attribute
if (!string.IsNullOrEmpty(attrMetadata.BlobFileNameAttributeId))
{
GetDynamicProperty(attrMetadata.BlobFileNameAttributeId).Value = fileName;
}
//set file size in appropriated attribute
if (!string.IsNullOrEmpty(attrMetadata.BlobFileSizeAttributeId))
{
GetDynamicProperty(attrMetadata.BlobFileSizeAttributeId).Value = stream.Length;
}
}
//----------------------------------------------------------------------------
/// <summary>
/// Adds to removing BLOB from server.
/// </summary>
/// <param name="attributeId">Attribute id that contains BLOB.</param>
public virtual void AddToDeleteBlob(string attributeId)
{
CxAttributeValue stateAttrValue = m_Attributes[CxEntityUsage.GetBlobStateAttributeId(attributeId)];
stateAttrValue.Value = CxEntityUsage.REMOVE_BLOB_FROM_DB;
CxAttributeValue attrValue = m_Attributes[attributeId];
CxClientAttributeMetadata attrMetadata = attrValue.AttributeMetadata;
//set file name in appropriated attribute
if (!string.IsNullOrEmpty(attrMetadata.BlobFileNameAttributeId))
{
GetDynamicProperty(attrMetadata.BlobFileNameAttributeId).Value = null;
}
//set file size in appropriated attribute
if (!string.IsNullOrEmpty(attrMetadata.BlobFileSizeAttributeId))
{
GetDynamicProperty(attrMetadata.BlobFileSizeAttributeId).Value = null;
}
}
//----------------------------------------------------------------------------
/// <summary>
/// Returns dictionary of waiting to upload streams.
/// </summary>
/// <returns>Dictionary with streams.</returns>
public virtual Dictionary<string, CxFileToUpload> GetStreamsToUpload()
{
return m_WaitingToUpload;
}
}
}