This commit is contained in:
@@ -1952,7 +1952,7 @@ namespace DynamORM
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Bulk update objects in database.</summary>
|
/// <summary>Bulk update objects in database.</summary>
|
||||||
/// <param name="T">Type of objects to update.</param>
|
/// <param name="t">Type of objects to update.</param>
|
||||||
/// <param name="e">Enumerable containing instances of objects to update.</param>
|
/// <param name="e">Enumerable containing instances of objects to update.</param>
|
||||||
/// <returns>Number of updated rows.</returns>
|
/// <returns>Number of updated rows.</returns>
|
||||||
public virtual int Update(Type t, IEnumerable e)
|
public virtual int Update(Type t, IEnumerable e)
|
||||||
@@ -13814,6 +13814,429 @@ namespace DynamORM
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Objects
|
||||||
|
{
|
||||||
|
/// <summary>Base class for strong typed objects.</summary>
|
||||||
|
public class DynamicEntityBase
|
||||||
|
{
|
||||||
|
private Dictionary<string, object> _changedFields = new Dictionary<string, object>();
|
||||||
|
private DynamicEntityState _dynamicEntityState = DynamicEntityState.Unknown;
|
||||||
|
|
||||||
|
/// <summary>Occurs when object property is changing.</summary>
|
||||||
|
public event EventHandler<DynamicPropertyChangingEventArgs> PropertyChanging;
|
||||||
|
|
||||||
|
/// <summary>Gets the state of the dynamic entity.</summary>
|
||||||
|
/// <returns>Current state of entity.</returns>
|
||||||
|
public virtual DynamicEntityState GetDynamicEntityState() { return _dynamicEntityState; }
|
||||||
|
|
||||||
|
/// <summary>Sets the state of the dynamic entity.</summary>
|
||||||
|
/// <param name="state">The state.</param>
|
||||||
|
public virtual void SetDynamicEntityState(DynamicEntityState state) { _dynamicEntityState = state; }
|
||||||
|
|
||||||
|
/// <summary>Called when object property is changing.</summary>
|
||||||
|
/// <param name="propertyName">Name of the property.</param>
|
||||||
|
/// <param name="oldValue">The old property value.</param>
|
||||||
|
/// <param name="newValue">The new property value.</param>
|
||||||
|
protected virtual void OnPropertyChanging(string propertyName, object oldValue, object newValue)
|
||||||
|
{
|
||||||
|
OnPropertyChanging(new DynamicPropertyChangingEventArgs(propertyName, oldValue, newValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Raises the <see cref="E:PropertyChanging"/> event.</summary>
|
||||||
|
/// <param name="e">The <see cref="DynamicPropertyChangingEventArgs"/> instance containing the event data.</param>
|
||||||
|
protected virtual void OnPropertyChanging(DynamicPropertyChangingEventArgs e)
|
||||||
|
{
|
||||||
|
_changedFields[e.PropertyName] = e.NewValue;
|
||||||
|
if (PropertyChanging != null)
|
||||||
|
PropertyChanging(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Validates this object instance.</summary>
|
||||||
|
/// <returns>Returns list of <see cref="ValidationResult"/> containing results of validation.</returns>
|
||||||
|
public virtual IList<ValidationResult> Validate()
|
||||||
|
{
|
||||||
|
return DynamicMapperCache.GetMapper(this.GetType()).ValidateObject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Saves this object to database.</summary>
|
||||||
|
/// <param name="database">The database.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
/// <exception cref="InvalidOperationException">Can be thrown when object is in invalid state.</exception>
|
||||||
|
public virtual bool Save(DynamicDatabase database)
|
||||||
|
{
|
||||||
|
switch (GetDynamicEntityState())
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case DynamicEntityState.Unknown:
|
||||||
|
throw new InvalidOperationException("Unknown object state. Unable to decide whish action should be performed.");
|
||||||
|
|
||||||
|
case DynamicEntityState.New:
|
||||||
|
return Insert(database);
|
||||||
|
|
||||||
|
case DynamicEntityState.Existing:
|
||||||
|
return Update(database);
|
||||||
|
|
||||||
|
case DynamicEntityState.ToBeDeleted:
|
||||||
|
return Delete(database);
|
||||||
|
|
||||||
|
case DynamicEntityState.Deleted:
|
||||||
|
throw new InvalidOperationException("Unable to do any database action on deleted object.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Insert/Update/Delete
|
||||||
|
|
||||||
|
/// <summary>Inserts this object to database.</summary>
|
||||||
|
/// <param name="db">The database.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
public virtual bool Insert(DynamicDatabase db)
|
||||||
|
{
|
||||||
|
if (db.Insert(this.GetType())
|
||||||
|
.Values(x => this)
|
||||||
|
.Execute() > 0)
|
||||||
|
{
|
||||||
|
_changedFields.Clear();
|
||||||
|
SetDynamicEntityState(DynamicEntityState.Existing);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Updates this object in database.</summary>
|
||||||
|
/// <param name="db">The database.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
public virtual bool Update(DynamicDatabase db)
|
||||||
|
{
|
||||||
|
var t = GetType();
|
||||||
|
var mapper = DynamicMapperCache.GetMapper(t);
|
||||||
|
var query = db.Update(t);
|
||||||
|
|
||||||
|
MakeQueryWhere(mapper, query);
|
||||||
|
|
||||||
|
if (_changedFields.Any())
|
||||||
|
{
|
||||||
|
bool any = false;
|
||||||
|
|
||||||
|
foreach (var cf in _changedFields)
|
||||||
|
{
|
||||||
|
var cn = mapper.PropertyMap[cf.Key];
|
||||||
|
var pm = mapper.ColumnsMap[cn.ToLower()];
|
||||||
|
if (pm.Ignore)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (pm.Column != null)
|
||||||
|
{
|
||||||
|
if (pm.Column.IsKey)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!pm.Column.AllowNull && cf.Value == null)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
query.Values(cn, cf.Value);
|
||||||
|
any = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!any)
|
||||||
|
query.Set(x => this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
query.Set(x => this);
|
||||||
|
|
||||||
|
if (query.Execute() == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
SetDynamicEntityState(DynamicEntityState.Existing);
|
||||||
|
_changedFields.Clear();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Deletes this object from database.</summary>
|
||||||
|
/// <param name="db">The database.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
public virtual bool Delete(DynamicDatabase db)
|
||||||
|
{
|
||||||
|
var t = this.GetType();
|
||||||
|
var mapper = DynamicMapperCache.GetMapper(t);
|
||||||
|
|
||||||
|
var query = db.Delete(t);
|
||||||
|
|
||||||
|
MakeQueryWhere(mapper, query);
|
||||||
|
|
||||||
|
if (query.Execute() == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
SetDynamicEntityState(DynamicEntityState.Deleted);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Insert/Update/Delete
|
||||||
|
|
||||||
|
#region Select
|
||||||
|
|
||||||
|
/// <summary>Refresh non key data from database.</summary>
|
||||||
|
/// <param name="db">The database.</param>
|
||||||
|
/// <remarks>All properties that are primary key values must be filled.</remarks>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
public virtual bool Refresh(DynamicDatabase db)
|
||||||
|
{
|
||||||
|
var t = this.GetType();
|
||||||
|
var mapper = DynamicMapperCache.GetMapper(t);
|
||||||
|
var query = db.From(t);
|
||||||
|
MakeQueryWhere(mapper, query);
|
||||||
|
var o = (query.Execute() as IEnumerable<dynamic>).FirstOrDefault();
|
||||||
|
|
||||||
|
if (o == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
mapper.Map(o, this);
|
||||||
|
|
||||||
|
SetDynamicEntityState(DynamicEntityState.Existing);
|
||||||
|
_changedFields.Clear();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Select
|
||||||
|
|
||||||
|
#region Query Helpers
|
||||||
|
|
||||||
|
private void MakeQueryWhere(DynamicTypeMap mapper, IDynamicUpdateQueryBuilder query)
|
||||||
|
{
|
||||||
|
bool keyNotDefined = true;
|
||||||
|
|
||||||
|
foreach (var cm in mapper.ColumnsMap)
|
||||||
|
{
|
||||||
|
if (cm.Value.Column != null && cm.Value.Column.IsKey)
|
||||||
|
{
|
||||||
|
query.Where(cm.Key, DynamicColumn.CompareOperator.Eq, cm.Value.Get(this));
|
||||||
|
keyNotDefined = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyNotDefined)
|
||||||
|
throw new InvalidOperationException(String.Format("Class '{0}' have no key columns defined",
|
||||||
|
this.GetType().FullName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MakeQueryWhere(DynamicTypeMap mapper, IDynamicDeleteQueryBuilder query)
|
||||||
|
{
|
||||||
|
bool keyNotDefined = true;
|
||||||
|
|
||||||
|
foreach (var cm in mapper.ColumnsMap)
|
||||||
|
{
|
||||||
|
if (cm.Value.Column != null && cm.Value.Column.IsKey)
|
||||||
|
{
|
||||||
|
query.Where(cm.Key, DynamicColumn.CompareOperator.Eq, cm.Value.Get(this));
|
||||||
|
keyNotDefined = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyNotDefined)
|
||||||
|
throw new InvalidOperationException(String.Format("Class '{0}' have no key columns defined",
|
||||||
|
this.GetType().FullName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MakeQueryWhere(DynamicTypeMap mapper, IDynamicSelectQueryBuilder query)
|
||||||
|
{
|
||||||
|
bool keyNotDefined = true;
|
||||||
|
|
||||||
|
foreach (var cm in mapper.ColumnsMap)
|
||||||
|
{
|
||||||
|
if (cm.Value.Column != null && cm.Value.Column.IsKey)
|
||||||
|
{
|
||||||
|
var v = cm.Value.Get(this);
|
||||||
|
|
||||||
|
if (v == null)
|
||||||
|
throw new InvalidOperationException(String.Format("Class '{0}' have key columns {1} not filled with data.",
|
||||||
|
this.GetType().FullName, cm.Value.Name));
|
||||||
|
|
||||||
|
query.Where(cm.Key, DynamicColumn.CompareOperator.Eq, cm.Value.Get(this));
|
||||||
|
keyNotDefined = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyNotDefined)
|
||||||
|
throw new InvalidOperationException(String.Format("Class '{0}' have no key columns defined",
|
||||||
|
this.GetType().FullName));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Query Helpers
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Possible states of dynamic database objects.</summary>
|
||||||
|
public enum DynamicEntityState
|
||||||
|
{
|
||||||
|
/// <summary>Default state. This state will only permit to refresh data from database.</summary>
|
||||||
|
/// <remarks>In this state repository will be unable to tell if object with this state should be added
|
||||||
|
/// or updated in database, but you can still manually perform update or insert on such object.</remarks>
|
||||||
|
Unknown,
|
||||||
|
|
||||||
|
/// <summary>This state should be set to new objects in database.</summary>
|
||||||
|
New,
|
||||||
|
|
||||||
|
/// <summary>This state is ser when data is refreshed from database or object was loaded from repository.</summary>
|
||||||
|
Existing,
|
||||||
|
|
||||||
|
/// <summary>You can set this state to an object if you want repository to perform delete from database.</summary>
|
||||||
|
ToBeDeleted,
|
||||||
|
|
||||||
|
/// <summary>This state is set for objects that were deleted from database.</summary>
|
||||||
|
Deleted,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Class containing changed property data.</summary>
|
||||||
|
/// <seealso cref="System.EventArgs" />
|
||||||
|
public class DynamicPropertyChangingEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
/// <summary>Gets the name of the property.</summary>
|
||||||
|
/// <value>The name of the property.</value>
|
||||||
|
public string PropertyName { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets the old property value.</summary>
|
||||||
|
/// <value>The old value.</value>
|
||||||
|
public object OldValue { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets the new property value.</summary>
|
||||||
|
/// <value>The new value.</value>
|
||||||
|
public object NewValue { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Initializes a new instance of the <see cref="DynamicPropertyChangingEventArgs"/> class.</summary>
|
||||||
|
/// <param name="propertyName">Name of the property.</param>
|
||||||
|
/// <param name="oldValue">The old property value.</param>
|
||||||
|
/// <param name="newValue">The new property value.</param>
|
||||||
|
public DynamicPropertyChangingEventArgs(string propertyName, object oldValue, object newValue)
|
||||||
|
{
|
||||||
|
PropertyName = propertyName;
|
||||||
|
OldValue = oldValue;
|
||||||
|
NewValue = newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Base repository class for specified object type.</summary>
|
||||||
|
/// <typeparam name="T">Type of stored object.</typeparam>
|
||||||
|
public class DynamicRepositoryBase<T> : IDisposable where T : DynamicEntityBase
|
||||||
|
{
|
||||||
|
private DynamicDatabase _database;
|
||||||
|
|
||||||
|
/// <summary>Initializes a new instance of the <see cref="DynamicRepositoryBase{T}"/> class.</summary>
|
||||||
|
/// <param name="database">The database.</param>
|
||||||
|
public DynamicRepositoryBase(DynamicDatabase database)
|
||||||
|
{
|
||||||
|
_database = database;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get all rows from database.</summary>
|
||||||
|
/// <returns>Objects enumerator.</returns>
|
||||||
|
public virtual IEnumerable<T> GetAll()
|
||||||
|
{
|
||||||
|
return EnumerateQuery(_database.From<T>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get rows from database by custom query.</summary>
|
||||||
|
/// <param name="query">The query.</param>
|
||||||
|
/// <remarks>Query must be based on object type.</remarks>
|
||||||
|
/// <returns>Objects enumerator.</returns>
|
||||||
|
public virtual IEnumerable<T> GetByQuery(IDynamicSelectQueryBuilder query)
|
||||||
|
{
|
||||||
|
return EnumerateQuery(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<T> EnumerateQuery(IDynamicSelectQueryBuilder query, bool forceType = true)
|
||||||
|
{
|
||||||
|
if (forceType)
|
||||||
|
{
|
||||||
|
var mapper = DynamicMapperCache.GetMapper(typeof(T));
|
||||||
|
|
||||||
|
var tn = mapper.Table == null || string.IsNullOrEmpty(mapper.Table.Name) ?
|
||||||
|
mapper.Type.Name : mapper.Table.Name;
|
||||||
|
|
||||||
|
if (!query.Tables.Any(t => t.Name == tn))
|
||||||
|
throw new InvalidOperationException(string.Format("Query is not related to '{0}' class.", typeof(T).FullName));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var o in query.Execute<T>())
|
||||||
|
{
|
||||||
|
o.SetDynamicEntityState(DynamicEntityState.Existing);
|
||||||
|
yield return o;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Saves single object to database.</summary>
|
||||||
|
/// <param name="element">The element.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
public virtual bool Save(T element)
|
||||||
|
{
|
||||||
|
return element.Save(_database);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Saves collection of objects to database.</summary>
|
||||||
|
/// <param name="element">The element.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
public virtual bool Save(IEnumerable<T> element)
|
||||||
|
{
|
||||||
|
return element.All(x => x.Save(_database));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Insert single object to database.</summary>
|
||||||
|
/// <param name="element">The element.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
public virtual bool Insert(T element)
|
||||||
|
{
|
||||||
|
return element.Insert(_database);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Insert collection of objects to database.</summary>
|
||||||
|
/// <param name="element">The element.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
public virtual bool Insert(IEnumerable<T> element)
|
||||||
|
{
|
||||||
|
return element.All(x => x.Insert(_database));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Update single object to database.</summary>
|
||||||
|
/// <param name="element">The element.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
public virtual bool Update(T element)
|
||||||
|
{
|
||||||
|
return element.Update(_database);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Update collection of objects to database.</summary>
|
||||||
|
/// <param name="element">The element.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
public virtual bool Update(IEnumerable<T> element)
|
||||||
|
{
|
||||||
|
return element.All(x => x.Update(_database));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Delete single object to database.</summary>
|
||||||
|
/// <param name="element">The element.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
public virtual bool Delete(T element)
|
||||||
|
{
|
||||||
|
return element.Delete(_database);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Delete collection of objects to database.</summary>
|
||||||
|
/// <param name="element">The element.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
public virtual bool Delete(IEnumerable<T> element)
|
||||||
|
{
|
||||||
|
return element.All(x => x.Delete(_database));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Releases unmanaged and - optionally - managed resources.</summary>
|
||||||
|
public virtual void Dispose()
|
||||||
|
{
|
||||||
|
_database = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace Validation
|
namespace Validation
|
||||||
{
|
{
|
||||||
/// <summary>Required attribute can be used to validate fields in objects using mapper class.</summary>
|
/// <summary>Required attribute can be used to validate fields in objects using mapper class.</summary>
|
||||||
|
|||||||
@@ -105,6 +105,10 @@
|
|||||||
<Compile Include="Mapper\DynamicPropertyInvoker.cs" />
|
<Compile Include="Mapper\DynamicPropertyInvoker.cs" />
|
||||||
<Compile Include="Mapper\DynamicTypeMap.cs" />
|
<Compile Include="Mapper\DynamicTypeMap.cs" />
|
||||||
<Compile Include="Mapper\IgnoreAttribute.cs" />
|
<Compile Include="Mapper\IgnoreAttribute.cs" />
|
||||||
|
<Compile Include="Objects\DynamicEntityBase.cs" />
|
||||||
|
<Compile Include="Objects\DynamicEntityState.cs" />
|
||||||
|
<Compile Include="Objects\DynamicPropertyChangingEventArgs.cs" />
|
||||||
|
<Compile Include="Objects\DynamicRepositoryBase.cs" />
|
||||||
<Compile Include="Validation\RequiredAttribute.cs" />
|
<Compile Include="Validation\RequiredAttribute.cs" />
|
||||||
<Compile Include="Mapper\TableAttribute.cs" />
|
<Compile Include="Mapper\TableAttribute.cs" />
|
||||||
<Compile Include="Mapper\DynamicCast.cs" />
|
<Compile Include="Mapper\DynamicCast.cs" />
|
||||||
|
|||||||
@@ -492,7 +492,7 @@ namespace DynamORM
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Bulk update objects in database.</summary>
|
/// <summary>Bulk update objects in database.</summary>
|
||||||
/// <param name="T">Type of objects to update.</param>
|
/// <param name="t">Type of objects to update.</param>
|
||||||
/// <param name="e">Enumerable containing instances of objects to update.</param>
|
/// <param name="e">Enumerable containing instances of objects to update.</param>
|
||||||
/// <returns>Number of updated rows.</returns>
|
/// <returns>Number of updated rows.</returns>
|
||||||
public virtual int Update(Type t, IEnumerable e)
|
public virtual int Update(Type t, IEnumerable e)
|
||||||
|
|||||||
260
DynamORM/Objects/DynamicEntityBase.cs
Normal file
260
DynamORM/Objects/DynamicEntityBase.cs
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using DynamORM.Builders;
|
||||||
|
using DynamORM.Mapper;
|
||||||
|
using DynamORM.Validation;
|
||||||
|
|
||||||
|
namespace DynamORM.Objects
|
||||||
|
{
|
||||||
|
/// <summary>Base class for strong typed objects.</summary>
|
||||||
|
public class DynamicEntityBase
|
||||||
|
{
|
||||||
|
private Dictionary<string, object> _changedFields = new Dictionary<string, object>();
|
||||||
|
private DynamicEntityState _dynamicEntityState = DynamicEntityState.Unknown;
|
||||||
|
|
||||||
|
/// <summary>Occurs when object property is changing.</summary>
|
||||||
|
public event EventHandler<DynamicPropertyChangingEventArgs> PropertyChanging;
|
||||||
|
|
||||||
|
/// <summary>Gets the state of the dynamic entity.</summary>
|
||||||
|
/// <returns>Current state of entity.</returns>
|
||||||
|
public virtual DynamicEntityState GetDynamicEntityState() { return _dynamicEntityState; }
|
||||||
|
|
||||||
|
/// <summary>Sets the state of the dynamic entity.</summary>
|
||||||
|
/// <param name="state">The state.</param>
|
||||||
|
public virtual void SetDynamicEntityState(DynamicEntityState state) { _dynamicEntityState = state; }
|
||||||
|
|
||||||
|
/// <summary>Called when object property is changing.</summary>
|
||||||
|
/// <param name="propertyName">Name of the property.</param>
|
||||||
|
/// <param name="oldValue">The old property value.</param>
|
||||||
|
/// <param name="newValue">The new property value.</param>
|
||||||
|
protected virtual void OnPropertyChanging(string propertyName, object oldValue, object newValue)
|
||||||
|
{
|
||||||
|
OnPropertyChanging(new DynamicPropertyChangingEventArgs(propertyName, oldValue, newValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Raises the <see cref="E:PropertyChanging"/> event.</summary>
|
||||||
|
/// <param name="e">The <see cref="DynamicPropertyChangingEventArgs"/> instance containing the event data.</param>
|
||||||
|
protected virtual void OnPropertyChanging(DynamicPropertyChangingEventArgs e)
|
||||||
|
{
|
||||||
|
_changedFields[e.PropertyName] = e.NewValue;
|
||||||
|
if (PropertyChanging != null)
|
||||||
|
PropertyChanging(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Validates this object instance.</summary>
|
||||||
|
/// <returns>Returns list of <see cref="ValidationResult"/> containing results of validation.</returns>
|
||||||
|
public virtual IList<ValidationResult> Validate()
|
||||||
|
{
|
||||||
|
return DynamicMapperCache.GetMapper(this.GetType()).ValidateObject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Saves this object to database.</summary>
|
||||||
|
/// <param name="database">The database.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
/// <exception cref="InvalidOperationException">Can be thrown when object is in invalid state.</exception>
|
||||||
|
public virtual bool Save(DynamicDatabase database)
|
||||||
|
{
|
||||||
|
switch (GetDynamicEntityState())
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case DynamicEntityState.Unknown:
|
||||||
|
throw new InvalidOperationException("Unknown object state. Unable to decide whish action should be performed.");
|
||||||
|
|
||||||
|
case DynamicEntityState.New:
|
||||||
|
return Insert(database);
|
||||||
|
|
||||||
|
case DynamicEntityState.Existing:
|
||||||
|
return Update(database);
|
||||||
|
|
||||||
|
case DynamicEntityState.ToBeDeleted:
|
||||||
|
return Delete(database);
|
||||||
|
|
||||||
|
case DynamicEntityState.Deleted:
|
||||||
|
throw new InvalidOperationException("Unable to do any database action on deleted object.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Insert/Update/Delete
|
||||||
|
|
||||||
|
/// <summary>Inserts this object to database.</summary>
|
||||||
|
/// <param name="db">The database.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
public virtual bool Insert(DynamicDatabase db)
|
||||||
|
{
|
||||||
|
if (db.Insert(this.GetType())
|
||||||
|
.Values(x => this)
|
||||||
|
.Execute() > 0)
|
||||||
|
{
|
||||||
|
_changedFields.Clear();
|
||||||
|
SetDynamicEntityState(DynamicEntityState.Existing);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Updates this object in database.</summary>
|
||||||
|
/// <param name="db">The database.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
public virtual bool Update(DynamicDatabase db)
|
||||||
|
{
|
||||||
|
var t = GetType();
|
||||||
|
var mapper = DynamicMapperCache.GetMapper(t);
|
||||||
|
var query = db.Update(t);
|
||||||
|
|
||||||
|
MakeQueryWhere(mapper, query);
|
||||||
|
|
||||||
|
if (_changedFields.Any())
|
||||||
|
{
|
||||||
|
bool any = false;
|
||||||
|
|
||||||
|
foreach (var cf in _changedFields)
|
||||||
|
{
|
||||||
|
var cn = mapper.PropertyMap[cf.Key];
|
||||||
|
var pm = mapper.ColumnsMap[cn.ToLower()];
|
||||||
|
if (pm.Ignore)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (pm.Column != null)
|
||||||
|
{
|
||||||
|
if (pm.Column.IsKey)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!pm.Column.AllowNull && cf.Value == null)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
query.Values(cn, cf.Value);
|
||||||
|
any = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!any)
|
||||||
|
query.Set(x => this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
query.Set(x => this);
|
||||||
|
|
||||||
|
if (query.Execute() == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
SetDynamicEntityState(DynamicEntityState.Existing);
|
||||||
|
_changedFields.Clear();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Deletes this object from database.</summary>
|
||||||
|
/// <param name="db">The database.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
public virtual bool Delete(DynamicDatabase db)
|
||||||
|
{
|
||||||
|
var t = this.GetType();
|
||||||
|
var mapper = DynamicMapperCache.GetMapper(t);
|
||||||
|
|
||||||
|
var query = db.Delete(t);
|
||||||
|
|
||||||
|
MakeQueryWhere(mapper, query);
|
||||||
|
|
||||||
|
if (query.Execute() == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
SetDynamicEntityState(DynamicEntityState.Deleted);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Insert/Update/Delete
|
||||||
|
|
||||||
|
#region Select
|
||||||
|
|
||||||
|
/// <summary>Refresh non key data from database.</summary>
|
||||||
|
/// <param name="db">The database.</param>
|
||||||
|
/// <remarks>All properties that are primary key values must be filled.</remarks>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
public virtual bool Refresh(DynamicDatabase db)
|
||||||
|
{
|
||||||
|
var t = this.GetType();
|
||||||
|
var mapper = DynamicMapperCache.GetMapper(t);
|
||||||
|
var query = db.From(t);
|
||||||
|
MakeQueryWhere(mapper, query);
|
||||||
|
var o = (query.Execute() as IEnumerable<dynamic>).FirstOrDefault();
|
||||||
|
|
||||||
|
if (o == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
mapper.Map(o, this);
|
||||||
|
|
||||||
|
SetDynamicEntityState(DynamicEntityState.Existing);
|
||||||
|
_changedFields.Clear();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Select
|
||||||
|
|
||||||
|
#region Query Helpers
|
||||||
|
|
||||||
|
private void MakeQueryWhere(DynamicTypeMap mapper, IDynamicUpdateQueryBuilder query)
|
||||||
|
{
|
||||||
|
bool keyNotDefined = true;
|
||||||
|
|
||||||
|
foreach (var cm in mapper.ColumnsMap)
|
||||||
|
{
|
||||||
|
if (cm.Value.Column != null && cm.Value.Column.IsKey)
|
||||||
|
{
|
||||||
|
query.Where(cm.Key, DynamicColumn.CompareOperator.Eq, cm.Value.Get(this));
|
||||||
|
keyNotDefined = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyNotDefined)
|
||||||
|
throw new InvalidOperationException(String.Format("Class '{0}' have no key columns defined",
|
||||||
|
this.GetType().FullName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MakeQueryWhere(DynamicTypeMap mapper, IDynamicDeleteQueryBuilder query)
|
||||||
|
{
|
||||||
|
bool keyNotDefined = true;
|
||||||
|
|
||||||
|
foreach (var cm in mapper.ColumnsMap)
|
||||||
|
{
|
||||||
|
if (cm.Value.Column != null && cm.Value.Column.IsKey)
|
||||||
|
{
|
||||||
|
query.Where(cm.Key, DynamicColumn.CompareOperator.Eq, cm.Value.Get(this));
|
||||||
|
keyNotDefined = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyNotDefined)
|
||||||
|
throw new InvalidOperationException(String.Format("Class '{0}' have no key columns defined",
|
||||||
|
this.GetType().FullName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MakeQueryWhere(DynamicTypeMap mapper, IDynamicSelectQueryBuilder query)
|
||||||
|
{
|
||||||
|
bool keyNotDefined = true;
|
||||||
|
|
||||||
|
foreach (var cm in mapper.ColumnsMap)
|
||||||
|
{
|
||||||
|
if (cm.Value.Column != null && cm.Value.Column.IsKey)
|
||||||
|
{
|
||||||
|
var v = cm.Value.Get(this);
|
||||||
|
|
||||||
|
if (v == null)
|
||||||
|
throw new InvalidOperationException(String.Format("Class '{0}' have key columns {1} not filled with data.",
|
||||||
|
this.GetType().FullName, cm.Value.Name));
|
||||||
|
|
||||||
|
query.Where(cm.Key, DynamicColumn.CompareOperator.Eq, cm.Value.Get(this));
|
||||||
|
keyNotDefined = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyNotDefined)
|
||||||
|
throw new InvalidOperationException(String.Format("Class '{0}' have no key columns defined",
|
||||||
|
this.GetType().FullName));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Query Helpers
|
||||||
|
}
|
||||||
|
}
|
||||||
23
DynamORM/Objects/DynamicEntityState.cs
Normal file
23
DynamORM/Objects/DynamicEntityState.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
namespace DynamORM.Objects
|
||||||
|
{
|
||||||
|
/// <summary>Possible states of dynamic database objects.</summary>
|
||||||
|
public enum DynamicEntityState
|
||||||
|
{
|
||||||
|
/// <summary>Default state. This state will only permit to refresh data from database.</summary>
|
||||||
|
/// <remarks>In this state repository will be unable to tell if object with this state should be added
|
||||||
|
/// or updated in database, but you can still manually perform update or insert on such object.</remarks>
|
||||||
|
Unknown,
|
||||||
|
|
||||||
|
/// <summary>This state should be set to new objects in database.</summary>
|
||||||
|
New,
|
||||||
|
|
||||||
|
/// <summary>This state is ser when data is refreshed from database or object was loaded from repository.</summary>
|
||||||
|
Existing,
|
||||||
|
|
||||||
|
/// <summary>You can set this state to an object if you want repository to perform delete from database.</summary>
|
||||||
|
ToBeDeleted,
|
||||||
|
|
||||||
|
/// <summary>This state is set for objects that were deleted from database.</summary>
|
||||||
|
Deleted,
|
||||||
|
}
|
||||||
|
}
|
||||||
32
DynamORM/Objects/DynamicPropertyChangingEventArgs.cs
Normal file
32
DynamORM/Objects/DynamicPropertyChangingEventArgs.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace DynamORM.Objects
|
||||||
|
{
|
||||||
|
/// <summary>Class containing changed property data.</summary>
|
||||||
|
/// <seealso cref="System.EventArgs" />
|
||||||
|
public class DynamicPropertyChangingEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
/// <summary>Gets the name of the property.</summary>
|
||||||
|
/// <value>The name of the property.</value>
|
||||||
|
public string PropertyName { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets the old property value.</summary>
|
||||||
|
/// <value>The old value.</value>
|
||||||
|
public object OldValue { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets the new property value.</summary>
|
||||||
|
/// <value>The new value.</value>
|
||||||
|
public object NewValue { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Initializes a new instance of the <see cref="DynamicPropertyChangingEventArgs"/> class.</summary>
|
||||||
|
/// <param name="propertyName">Name of the property.</param>
|
||||||
|
/// <param name="oldValue">The old property value.</param>
|
||||||
|
/// <param name="newValue">The new property value.</param>
|
||||||
|
public DynamicPropertyChangingEventArgs(string propertyName, object oldValue, object newValue)
|
||||||
|
{
|
||||||
|
PropertyName = propertyName;
|
||||||
|
OldValue = oldValue;
|
||||||
|
NewValue = newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
128
DynamORM/Objects/DynamicRepositoryBase.cs
Normal file
128
DynamORM/Objects/DynamicRepositoryBase.cs
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using DynamORM.Builders;
|
||||||
|
using DynamORM.Mapper;
|
||||||
|
|
||||||
|
namespace DynamORM.Objects
|
||||||
|
{
|
||||||
|
/// <summary>Base repository class for specified object type.</summary>
|
||||||
|
/// <typeparam name="T">Type of stored object.</typeparam>
|
||||||
|
public class DynamicRepositoryBase<T> : IDisposable where T : DynamicEntityBase
|
||||||
|
{
|
||||||
|
private DynamicDatabase _database;
|
||||||
|
|
||||||
|
/// <summary>Initializes a new instance of the <see cref="DynamicRepositoryBase{T}"/> class.</summary>
|
||||||
|
/// <param name="database">The database.</param>
|
||||||
|
public DynamicRepositoryBase(DynamicDatabase database)
|
||||||
|
{
|
||||||
|
_database = database;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get all rows from database.</summary>
|
||||||
|
/// <returns>Objects enumerator.</returns>
|
||||||
|
public virtual IEnumerable<T> GetAll()
|
||||||
|
{
|
||||||
|
return EnumerateQuery(_database.From<T>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get rows from database by custom query.</summary>
|
||||||
|
/// <param name="query">The query.</param>
|
||||||
|
/// <remarks>Query must be based on object type.</remarks>
|
||||||
|
/// <returns>Objects enumerator.</returns>
|
||||||
|
public virtual IEnumerable<T> GetByQuery(IDynamicSelectQueryBuilder query)
|
||||||
|
{
|
||||||
|
return EnumerateQuery(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<T> EnumerateQuery(IDynamicSelectQueryBuilder query, bool forceType = true)
|
||||||
|
{
|
||||||
|
if (forceType)
|
||||||
|
{
|
||||||
|
var mapper = DynamicMapperCache.GetMapper(typeof(T));
|
||||||
|
|
||||||
|
var tn = mapper.Table == null || string.IsNullOrEmpty(mapper.Table.Name) ?
|
||||||
|
mapper.Type.Name : mapper.Table.Name;
|
||||||
|
|
||||||
|
if (!query.Tables.Any(t => t.Name == tn))
|
||||||
|
throw new InvalidOperationException(string.Format("Query is not related to '{0}' class.", typeof(T).FullName));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var o in query.Execute<T>())
|
||||||
|
{
|
||||||
|
o.SetDynamicEntityState(DynamicEntityState.Existing);
|
||||||
|
yield return o;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Saves single object to database.</summary>
|
||||||
|
/// <param name="element">The element.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
public virtual bool Save(T element)
|
||||||
|
{
|
||||||
|
return element.Save(_database);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Saves collection of objects to database.</summary>
|
||||||
|
/// <param name="element">The element.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
public virtual bool Save(IEnumerable<T> element)
|
||||||
|
{
|
||||||
|
return element.All(x => x.Save(_database));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Insert single object to database.</summary>
|
||||||
|
/// <param name="element">The element.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
public virtual bool Insert(T element)
|
||||||
|
{
|
||||||
|
return element.Insert(_database);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Insert collection of objects to database.</summary>
|
||||||
|
/// <param name="element">The element.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
public virtual bool Insert(IEnumerable<T> element)
|
||||||
|
{
|
||||||
|
return element.All(x => x.Insert(_database));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Update single object to database.</summary>
|
||||||
|
/// <param name="element">The element.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
public virtual bool Update(T element)
|
||||||
|
{
|
||||||
|
return element.Update(_database);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Update collection of objects to database.</summary>
|
||||||
|
/// <param name="element">The element.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
public virtual bool Update(IEnumerable<T> element)
|
||||||
|
{
|
||||||
|
return element.All(x => x.Update(_database));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Delete single object to database.</summary>
|
||||||
|
/// <param name="element">The element.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
public virtual bool Delete(T element)
|
||||||
|
{
|
||||||
|
return element.Delete(_database);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Delete collection of objects to database.</summary>
|
||||||
|
/// <param name="element">The element.</param>
|
||||||
|
/// <returns>Returns <c>true</c> if operation was successful.</returns>
|
||||||
|
public virtual bool Delete(IEnumerable<T> element)
|
||||||
|
{
|
||||||
|
return element.All(x => x.Delete(_database));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Releases unmanaged and - optionally - managed resources.</summary>
|
||||||
|
public virtual void Dispose()
|
||||||
|
{
|
||||||
|
_database = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user