This commit is contained in:
2025-06-04 14:17:32 +07:00
parent 4c953f7efb
commit 5beb66f11a
257 changed files with 72800 additions and 2 deletions

View File

@ -0,0 +1,113 @@
using Dapper;
using System.Data.Common;
using System.Reflection;
namespace ManagementApp.Dapper.AExtentions
{
public static class DapperCommand
{
public static async Task Insert<T>(this DbConnection connection, T obj, bool identityInsert = true)
{
var type = typeof(T);
var tableName = TableMapper.GetTableName(type);
var allProperties = PropertiesCache.TypePropertiesCache(type);
var keyProperties = PropertiesCache.KeyPropertiesCache(type);
var computedProperties = PropertiesCache.ComputedPropertiesCache(type);
var columns = PropertiesCache.GetColumnNamesCache(type);
var insertProperties = allProperties.Except(computedProperties).ToList();
var insertPropertiesString = string.Empty;
if (identityInsert)
{
insertProperties = insertProperties.Except(keyProperties).ToList();
insertPropertiesString = GetColumnsString(insertProperties, columns);
var insertedId = await connection.QueryFirstAsync<int>($@"
INSERT INTO {FormatTableName(tableName)} ({insertPropertiesString}) VALUES ({GetColumnsString(insertProperties, columns, "@")});
SELECT LAST_INSERT_ID()", obj);
keyProperties[0].SetValue(obj, insertedId);
}
else
{
insertPropertiesString = GetColumnsString(insertProperties, columns);
await connection.QueryFirstAsync($@"
INSERT INTO {FormatTableName(tableName)}({insertPropertiesString})
VALUES ({GetColumnsString(insertProperties, columns, "@")})", obj);
}
}
public static async Task Update<T>(this DbConnection connection, T obj)
{
var type = typeof(T);
var tableName = TableMapper.GetTableName(type);
var allProperties = PropertiesCache.TypePropertiesCache(type);
var keyProperties = PropertiesCache.KeyPropertiesCache(type);
var computedProperties = PropertiesCache.ComputedPropertiesCache(type);
var columns = PropertiesCache.GetColumnNamesCache(type);
T obj1 = Activator.CreateInstance<T>();
var uProperties = allProperties.Except(computedProperties).Except(keyProperties).ToList();
var diffProperties = ComparePropertiesValue<T>(obj, obj1, uProperties);
await connection.QueryAsync($@"
UPDATE {FormatTableName(tableName)} Set {GetUpdateString(diffProperties, columns, " , ")}
WHERE {GetUpdateString(keyProperties, columns, " and ")}", obj);
}
private static List<PropertyInfo> ComparePropertiesValue<T>(T obj1, T obj2, IEnumerable<PropertyInfo> properties)
{
List<PropertyInfo> propertiesDiff = new List<PropertyInfo>();
foreach (PropertyInfo property in properties) {
if (property.GetValue(obj1, null) != property.GetValue(obj2, null))
{
propertiesDiff.Add(property);
}
}
return propertiesDiff;
}
private static string GetUpdateString(IEnumerable<PropertyInfo> properties, IReadOnlyDictionary<string, string> columnNames, string prefix)
{
return string.Join(prefix, properties.Select(p => $"{columnNames[p.Name]} = @{p.Name}"));
}
private static string GetColumnsString(IEnumerable<PropertyInfo> properties, IReadOnlyDictionary<string, string> columnNames, string tablePrefix = null)
{
if (tablePrefix == "target.")
{
return string.Join(", ", properties.Select(property => $"{tablePrefix}{columnNames[property.Name]} as {property.Name}"));
}
else if(tablePrefix == "@")
{
return string.Join(", ", properties.Select(property => $"{tablePrefix}{property.Name}"));
}
return string.Join(", ", properties.Select(property => $"{tablePrefix}{columnNames[property.Name]}"));
}
private static string FormatTableName(string table)
{
if (string.IsNullOrEmpty(table))
{
return table;
}
var parts = table.Split('.');
if (parts.Length == 1)
{
return $"{table}";
}
var tableName = "";
for (int i = 0; i < parts.Length; i++)
{
tableName += $"{parts[i]}";
if (i + 1 < parts.Length)
{
tableName += ".";
}
}
return tableName;
}
}
}

View File

@ -0,0 +1,111 @@
using System.Collections.Concurrent;
using System.Linq;
using System.Reflection;
namespace ManagementApp.Dapper.AExtentions
{
public class PropertiesCache
{
private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> KeyProperties = new();
private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> TypeProperties = new();
private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> ComputedProperties = new();
private static readonly ConcurrentDictionary<RuntimeTypeHandle, IReadOnlyDictionary<string, string>> ColumnNames = new();
public static List<PropertyInfo> TypePropertiesCache(Type type)
{
if (TypeProperties.TryGetValue(type.TypeHandle, out var cachedProps))
{
return cachedProps.ToList();
}
var properties = type.GetProperties().Where(ValidateProperty).ToList();
TypeProperties[type.TypeHandle] = properties;
ColumnNames[type.TypeHandle] = GetColumnNames(properties);
return properties.ToList();
}
public static IReadOnlyDictionary<string, string> GetColumnNamesCache(Type type)
{
if (ColumnNames.TryGetValue(type.TypeHandle, out var cachedProps))
{
return cachedProps;
}
var properties = type.GetProperties().Where(ValidateProperty).ToList();
TypeProperties[type.TypeHandle] = properties;
ColumnNames[type.TypeHandle] = GetColumnNames(properties);
return ColumnNames[type.TypeHandle];
}
public static bool ValidateProperty(PropertyInfo prop)
{
var result = prop.CanWrite;
result = result && (prop.GetSetMethod(true)?.IsPublic ?? false);
result = result && (!prop.PropertyType.IsClass || prop.PropertyType == typeof(string) || prop.PropertyType == typeof(byte[]));
result = result && prop.GetCustomAttributes(true).All(a => a.GetType().Name != "NotMappedAttribute");
var writeAttribute = prop.GetCustomAttributes(true).FirstOrDefault(x => x.GetType().Name == "WriteAttribute");
if (writeAttribute != null)
{
var writeProperty = writeAttribute.GetType().GetProperty("Write");
if (writeProperty != null && writeProperty.PropertyType == typeof(bool))
{
result = result && (bool)writeProperty.GetValue(writeAttribute);
}
}
return result;
}
public static List<PropertyInfo> KeyPropertiesCache(Type type)
{
if (KeyProperties.TryGetValue(type.TypeHandle, out var cachedProps))
{
return cachedProps.ToList();
}
var allProperties = TypePropertiesCache(type);
var keyProperties = allProperties.Where(p => p.GetCustomAttributes(true).Any(a => a.GetType().Name == "KeyAttribute")).ToList();
if (keyProperties.Count == 0)
{
var idProp = allProperties.Find(p => string.Equals(p.Name, "id", StringComparison.CurrentCultureIgnoreCase));
if (idProp != null)
{
keyProperties.Add(idProp);
}
}
KeyProperties[type.TypeHandle] = keyProperties;
return keyProperties;
}
public static List<PropertyInfo> ComputedPropertiesCache(Type type)
{
if (ComputedProperties.TryGetValue(type.TypeHandle, out var cachedProps))
{
return cachedProps.ToList();
}
var computedProperties = TypePropertiesCache(type).Where(p => p.GetCustomAttributes(true).Any(a => a.GetType().Name == "ComputedAttribute")).ToList();
ComputedProperties[type.TypeHandle] = computedProperties;
return computedProperties;
}
private static IReadOnlyDictionary<string, string> GetColumnNames(IEnumerable<PropertyInfo> props)
{
var ret = new Dictionary<string, string>();
foreach (var prop in props)
{
var columnAttr = prop.GetCustomAttributes(false).SingleOrDefault(attr => attr.GetType().Name == "ColumnAttribute") as dynamic;
// if the column attribute exists, and specifies a column name, use that, otherwise fall back to the property name as the column name
ret.Add(prop.Name, columnAttr != null ? (string)columnAttr.Name ?? prop.Name : prop.Name);
}
return ret;
}
}
}

View File

@ -0,0 +1,57 @@
using System.Collections.Concurrent;
using System.Data;
namespace ManagementApp.Dapper.AExtentions
{
public class TableMapper
{
private static readonly ConcurrentDictionary<RuntimeTypeHandle, string> TableNames = new();
private static string _prefix = string.Empty;
/// <summary>
/// Used to setup custom table conventions.
/// </summary>
/// <param name="tablePrefix">table name prefix</param>
/// <param name="tableSuffix">table name suffix</param>
// ReSharper disable once UnusedMember.Global
public static void SetupConvention(string tablePrefix)
{
if (!TableNames.IsEmpty)
{
throw new InvalidConstraintException("TableMapper.SetupConvention called after usage.");
}
_prefix = tablePrefix;
TableNames.Clear();
}
internal static string GetTableName(Type type)
{
if (TableNames.TryGetValue(type.TypeHandle, out var name))
{
return name;
}
var tableAttr = type.GetCustomAttributes(false).SingleOrDefault(attr => attr.GetType().Name == "TableAttribute") as dynamic;
if (tableAttr != null)
{
name = tableAttr.Name;
if (tableAttr.Schema != null)
{
name = tableAttr.Schema + "." + tableAttr.Name;
}
}
else
{
name = type.IsInterface && type.Name.StartsWith("I") ? type.Name.Substring(1) : type.Name;
name = $"{_prefix}{name}";
}
TableNames[type.TypeHandle] = name;
return name;
}
}
}