mirror of
https://github.com/OpenDAoC/OpenDAoC-Core
synced 2026-08-12 00:23:14 -04:00
244 lines
No EOL
8.5 KiB
C#
244 lines
No EOL
8.5 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Data;
|
|
using DataTable = System.Data.DataTable;
|
|
using DOL.Database.Attributes;
|
|
|
|
namespace DOL.Database
|
|
{
|
|
/// <summary>
|
|
/// DataTableHandler that keep tracks of DataObjects Definition for matching Database schema.
|
|
/// </summary>
|
|
public sealed class DataTableHandler
|
|
{
|
|
/// <summary>
|
|
/// Data Object Type
|
|
/// </summary>
|
|
public Type ObjectType { get; }
|
|
/// <summary>
|
|
/// Has Relations
|
|
/// </summary>
|
|
public bool HasRelations { get; }
|
|
/// <summary>
|
|
/// Pre Cache Directory Handler
|
|
/// </summary>
|
|
private readonly ConcurrentDictionary<object, DataObject> _precache;
|
|
/// <summary>
|
|
/// Uses Precaching
|
|
/// </summary>
|
|
public bool UsesPreCaching { get; }
|
|
/// <summary>
|
|
/// The Table Name for this Handler
|
|
/// </summary>
|
|
public string TableName { get; }
|
|
/// <summary>
|
|
/// Element Bindings for this Handler
|
|
/// </summary>
|
|
public ElementBinding[] ElementBindings { get; }
|
|
/// <summary>
|
|
/// Element Bindings for DataTable Fields Only
|
|
/// </summary>
|
|
public ElementBinding[] FieldElementBindings { get; }
|
|
/// <summary>
|
|
/// Element Bindings that can be part of an UPDATE statement
|
|
/// </summary>
|
|
public ElementBinding[] UpdateElementBindings { get; }
|
|
/// <summary>
|
|
/// Element Binding representing the LastTimeRowUpdated column, if present
|
|
/// </summary>
|
|
public ElementBinding LastUpdatedBinding { get; }
|
|
/// <summary>
|
|
/// Data Table Handled
|
|
/// </summary>
|
|
public DataTable Table { get; }
|
|
/// <summary>
|
|
/// Retrieve Single Primary Key Binding
|
|
/// </summary>
|
|
public ElementBinding PrimaryKey { get { return PrimaryKeys.FirstOrDefault(); } }
|
|
/// <summary>
|
|
/// Retrieve Multiple Primary Key Binding (Future Support)
|
|
/// </summary>
|
|
public ElementBinding[] PrimaryKeys { get { return Table.PrimaryKey.Select(col => ElementBindings.FirstOrDefault(bind => bind.ColumnName.Equals(col.ColumnName, StringComparison.OrdinalIgnoreCase))).ToArray(); } }
|
|
|
|
/// <summary>
|
|
/// Create new instance of <see cref="DataTableHandler"/>
|
|
/// </summary>
|
|
/// <param name="ObjectType">DataObject Type</param>
|
|
public DataTableHandler(Type ObjectType)
|
|
{
|
|
if (!typeof(DataObject).IsAssignableFrom(ObjectType))
|
|
throw new ArgumentException("DataTableHandler can only register DataObject Types", "ObjectType");
|
|
|
|
this.ObjectType = ObjectType;
|
|
// Init Cache and Table Params
|
|
TableName = AttributeUtil.GetTableOrViewName(ObjectType);
|
|
|
|
var isView = AttributeUtil.GetViewName(ObjectType) != null;
|
|
|
|
HasRelations = false;
|
|
UsesPreCaching = AttributeUtil.GetPreCachedFlag(ObjectType);
|
|
if (UsesPreCaching)
|
|
_precache = new ConcurrentDictionary<object, DataObject>();
|
|
|
|
// Parse Table Type
|
|
ElementBindings = ObjectType.GetMembers().Select(member => new ElementBinding(member)).Where(bind => bind.IsDataElementBinding).ToArray();
|
|
|
|
// Snapshot of non-relation bindings *before* any synthetic key column is added.
|
|
ElementBinding[] preKeyFieldBindings = ElementBindings.Where(static bind => bind.Relation == null).ToArray();
|
|
|
|
// Views can't handle auto GUID key.
|
|
if (!isView)
|
|
{
|
|
if (preKeyFieldBindings.Any(bind => bind.PrimaryKey != null && !bind.PrimaryKey.AutoIncrement))
|
|
{
|
|
ElementBindings = ElementBindings.Concat(
|
|
[
|
|
new ElementBinding(ObjectType.GetProperty("ObjectId"),
|
|
new DataElement(){ Unique = true },
|
|
string.Format("{0}_ID", TableName))
|
|
]).ToArray();
|
|
}
|
|
else if (preKeyFieldBindings.All(bind => bind.PrimaryKey == null))
|
|
{
|
|
ElementBindings = ElementBindings.Concat(
|
|
[
|
|
new ElementBinding(ObjectType.GetProperty("ObjectId"),
|
|
new PrimaryKey(),
|
|
string.Format("{0}_ID", TableName))
|
|
]).ToArray();
|
|
}
|
|
}
|
|
|
|
// Cache final derived collections once ElementBindings is settled.
|
|
FieldElementBindings = ElementBindings.Where(static bind => bind.Relation == null).ToArray();
|
|
UpdateElementBindings = FieldElementBindings.Where(static bind => bind.PrimaryKey == null && bind.ReadOnly == null).ToArray();
|
|
LastUpdatedBinding = UpdateElementBindings.FirstOrDefault(static bind => bind.ColumnName == nameof(DataObject.LastTimeRowUpdated));
|
|
|
|
// Prepare Table
|
|
Table = new DataTable(TableName);
|
|
var multipleUnique = new List<ElementBinding>();
|
|
var indexes = new Dictionary<string, ElementBinding[]>();
|
|
|
|
//Build Table for DataSet
|
|
foreach (var bind in ElementBindings)
|
|
{
|
|
if (bind.Relation != null)
|
|
{
|
|
HasRelations = true;
|
|
continue;
|
|
}
|
|
|
|
var column = Table.Columns.Add(bind.ColumnName, bind.ValueType);
|
|
|
|
if (bind.PrimaryKey != null)
|
|
{
|
|
column.AutoIncrement = bind.PrimaryKey.AutoIncrement;
|
|
Table.PrimaryKey = new [] { column };
|
|
}
|
|
|
|
if (bind.DataElement != null)
|
|
{
|
|
column.AllowDBNull = bind.DataElement.AllowDbNull;
|
|
|
|
// Store Multi Unique for definition after table
|
|
// Single Unique can be defined directly.
|
|
if (!string.IsNullOrEmpty(bind.DataElement.UniqueColumns))
|
|
{
|
|
multipleUnique.Add(bind);
|
|
|
|
}
|
|
else if (bind.DataElement.Unique)
|
|
{
|
|
Table.Constraints.Add(new UniqueConstraint(string.Format("U_{0}_{1}", TableName, bind.ColumnName), column));
|
|
}
|
|
|
|
// Store Indexes for definition after table
|
|
if (!string.IsNullOrEmpty(bind.DataElement.IndexColumns))
|
|
indexes.Add(string.Format("I_{0}_{1}", TableName, bind.ColumnName), bind.DataElement.IndexColumns.Split(',')
|
|
.Select(col => FieldElementBindings.FirstOrDefault(ind => ind.ColumnName.Equals(col.Trim(), StringComparison.OrdinalIgnoreCase)))
|
|
.Concat(new [] { bind }).ToArray());
|
|
else if (bind.DataElement.Index)
|
|
indexes.Add(string.Format("I_{0}_{1}", TableName, bind.ColumnName), new [] { bind });
|
|
|
|
if (bind.DataElement.Varchar > 0)
|
|
column.ExtendedProperties.Add("VARCHAR", bind.DataElement.Varchar);
|
|
}
|
|
}
|
|
|
|
// Set Indexes when all columns are set
|
|
Table.ExtendedProperties.Add("INDEXES", indexes.Select(kv => new KeyValuePair<string, DataColumn[]>(
|
|
kv.Key,
|
|
kv.Value.Select(bind => Table.Columns[bind.ColumnName]).ToArray()))
|
|
.ToDictionary(kv => kv.Key, kv => kv.Value));
|
|
|
|
// Set Unique Constraints when all columns are set.
|
|
foreach (var bind in multipleUnique)
|
|
{
|
|
var columns = bind.DataElement.UniqueColumns.Split(',').Select(column => column.Trim()).Concat(new [] { bind.ColumnName });
|
|
Table.Constraints.Add(new UniqueConstraint(string.Format("U_{0}_{1}", TableName, bind.ColumnName),
|
|
columns.Select(column => Table.Columns[column]).ToArray()));
|
|
}
|
|
}
|
|
|
|
#region PreCache Handling
|
|
/// <summary>
|
|
/// Set Pre-Cached Object
|
|
/// </summary>
|
|
/// <param name="key">The key object</param>
|
|
/// <param name="obj">The value DataObject</param>
|
|
public void SetPreCachedObject(object key, DataObject obj)
|
|
{
|
|
// Force Case insensitive Storage...
|
|
var stringKey = key as string;
|
|
if (stringKey != null)
|
|
key = stringKey.ToLower();
|
|
|
|
_precache.AddOrUpdate(key, obj, (k, v) => obj);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get Pre-Cached Object
|
|
/// </summary>
|
|
/// <param name="key">The key object</param>
|
|
/// <returns>The value DataObject</returns>
|
|
public DataObject GetPreCachedObject(object key)
|
|
{
|
|
// Force Case insensitive Retrieve...
|
|
var stringKey = key as string;
|
|
if (stringKey != null)
|
|
key = stringKey.ToLower();
|
|
|
|
DataObject obj;
|
|
return _precache.TryGetValue(key, out obj) ? obj : null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Delete Pre-Cached Object
|
|
/// </summary>
|
|
/// <param name="key">The key object</param>
|
|
/// <returns>True if found and removed</returns>
|
|
public bool DeletePreCachedObject(object key)
|
|
{
|
|
// Force Case insensitive Retrieve...
|
|
var stringKey = key as string;
|
|
if (stringKey != null)
|
|
key = stringKey.ToLower();
|
|
|
|
DataObject dummy;
|
|
return _precache.TryRemove(key, out dummy);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Search Pre-Cached Object
|
|
/// </summary>
|
|
/// <param name="whereClause">Select Object when True</param>
|
|
/// <returns>IEnumerable of DataObjects</returns>
|
|
public IEnumerable<DataObject> SearchPreCachedObjects(Func<DataObject, bool> whereClause)
|
|
{
|
|
return _precache.Where(kv => whereClause(kv.Value)).Select(kv => kv.Value);
|
|
}
|
|
#endregion
|
|
}
|
|
} |