171 lines
7.5 KiB
C#
171 lines
7.5 KiB
C#
/*
|
|
* DynamORM - Dynamic Object-Relational Mapping library.
|
|
* Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com)
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using DynamORM.Mapper;
|
|
|
|
namespace DynamORM.Helpers
|
|
{
|
|
internal static class DynamicProcedureParameterBinder
|
|
{
|
|
internal sealed class BindingResult
|
|
{
|
|
public bool ReturnValueAdded { get; set; }
|
|
public Dictionary<string, int> ReturnParameters { get; set; }
|
|
}
|
|
|
|
internal static bool CanBind(object item)
|
|
{
|
|
if (!DynamicProcedureResultBinder.IsProcedureContract(item))
|
|
return false;
|
|
|
|
return GetBindableProperties(item.GetType()).Any();
|
|
}
|
|
|
|
internal static BindingResult Bind(DynamicDatabase db, IDbCommand cmd, object item)
|
|
{
|
|
if (db == null)
|
|
throw new ArgumentNullException("db");
|
|
if (cmd == null)
|
|
throw new ArgumentNullException("cmd");
|
|
if (item == null)
|
|
throw new ArgumentNullException("item");
|
|
|
|
BindingResult result = new BindingResult();
|
|
|
|
foreach (PropertyInfo property in GetBindableProperties(item.GetType()))
|
|
{
|
|
ProcedureParameterAttribute procAttr = property.GetCustomAttributes(typeof(ProcedureParameterAttribute), true).Cast<ProcedureParameterAttribute>().FirstOrDefault();
|
|
ColumnAttribute colAttr = property.GetCustomAttributes(typeof(ColumnAttribute), true).Cast<ColumnAttribute>().FirstOrDefault();
|
|
|
|
string name = (procAttr != null && !string.IsNullOrEmpty(procAttr.Name) ? procAttr.Name : null)
|
|
?? (colAttr != null && !string.IsNullOrEmpty(colAttr.Name) ? colAttr.Name : null)
|
|
?? property.Name;
|
|
|
|
ParameterDirection direction = procAttr == null ? ParameterDirection.Input : procAttr.Direction;
|
|
object value = property.GetValue(item, null);
|
|
DbType dbType = ResolveDbType(property.PropertyType, value, procAttr, colAttr, direction);
|
|
int size = ResolveSize(dbType, value, procAttr, colAttr, direction);
|
|
byte precision = (procAttr != null && procAttr.Precision != ProcedureParameterAttribute.UnspecifiedByte ? procAttr.Precision : default(byte));
|
|
byte scale = (procAttr != null && procAttr.Scale != ProcedureParameterAttribute.UnspecifiedByte ? procAttr.Scale : default(byte));
|
|
|
|
if (procAttr == null && colAttr != null)
|
|
{
|
|
precision = colAttr.Precision ?? precision;
|
|
scale = colAttr.Scale ?? scale;
|
|
}
|
|
|
|
if (direction == ParameterDirection.ReturnValue)
|
|
result.ReturnValueAdded = true;
|
|
|
|
if (direction == ParameterDirection.Output || direction == ParameterDirection.InputOutput || direction == ParameterDirection.ReturnValue)
|
|
{
|
|
if (result.ReturnParameters == null)
|
|
result.ReturnParameters = new Dictionary<string, int>();
|
|
result.ReturnParameters.Add(name, cmd.Parameters.Count);
|
|
}
|
|
|
|
cmd.AddParameter(
|
|
db.GetParameterName(name),
|
|
direction,
|
|
dbType,
|
|
size,
|
|
precision,
|
|
scale,
|
|
direction == ParameterDirection.Output || direction == ParameterDirection.ReturnValue ? DBNull.Value : (value ?? DBNull.Value));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private static IEnumerable<PropertyInfo> GetBindableProperties(Type type)
|
|
{
|
|
return type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
|
.Where(x => x.CanRead && x.GetIndexParameters().Length == 0)
|
|
.Where(x => x.GetCustomAttributes(typeof(ProcedureParameterAttribute), true).Any() || x.GetCustomAttributes(typeof(ColumnAttribute), true).Any())
|
|
.OrderBy(x =>
|
|
{
|
|
ProcedureParameterAttribute attr = x.GetCustomAttributes(typeof(ProcedureParameterAttribute), true).Cast<ProcedureParameterAttribute>().FirstOrDefault();
|
|
return attr == null ? int.MaxValue : attr.Order;
|
|
})
|
|
.ThenBy(x => x.MetadataToken);
|
|
}
|
|
|
|
private static DbType ResolveDbType(Type propertyType, object value, ProcedureParameterAttribute procAttr, ColumnAttribute colAttr, ParameterDirection direction)
|
|
{
|
|
if (procAttr != null && (int)procAttr.DbType != ProcedureParameterAttribute.UnspecifiedDbType)
|
|
return procAttr.DbType;
|
|
|
|
if (colAttr != null && colAttr.Type.HasValue)
|
|
return colAttr.Type.Value;
|
|
|
|
Type targetType = Nullable.GetUnderlyingType(propertyType) ?? propertyType;
|
|
|
|
if (targetType == typeof(object) && value != null)
|
|
targetType = value.GetType();
|
|
|
|
if (value == null && direction == ParameterDirection.ReturnValue)
|
|
return DbType.Int32;
|
|
|
|
return targetType.ToDbType();
|
|
}
|
|
|
|
private static int ResolveSize(DbType dbType, object value, ProcedureParameterAttribute procAttr, ColumnAttribute colAttr, ParameterDirection direction)
|
|
{
|
|
if (procAttr != null && procAttr.Size != ProcedureParameterAttribute.UnspecifiedSize)
|
|
return procAttr.Size;
|
|
|
|
if (colAttr != null && colAttr.Size.HasValue)
|
|
return colAttr.Size.Value;
|
|
|
|
if (direction == ParameterDirection.ReturnValue)
|
|
return 4;
|
|
|
|
if (dbType == DbType.AnsiString || dbType == DbType.AnsiStringFixedLength)
|
|
{
|
|
if (value != null)
|
|
return value.ToString().Length > 8000 ? -1 : 8000;
|
|
return 8000;
|
|
}
|
|
|
|
if (dbType == DbType.String || dbType == DbType.StringFixedLength)
|
|
{
|
|
if (value != null)
|
|
return value.ToString().Length > 4000 ? -1 : 4000;
|
|
return 4000;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
}
|