Files
TWA-App/TWASys-App/Dapper.AExtentions/PropertiesCache.cs

160 lines
6.8 KiB
C#

using System.Collections.Concurrent;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Reflection;
namespace TWASys_App.Dapper.AExtentions
{
public class PropertiesCache
{
private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> KeyProperties = new();
private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> FKProperties = 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 bool IsPkAutoIncrement(PropertyInfo p)
{
// ===== Fallback by-name (nếu không thể tham chiếu typed)
var attrs = p.GetCustomAttributes(true);
bool hasKeyByName = attrs.Any(a => a.GetType().Name is "KeyAttribute");
var dbgenByName = attrs.FirstOrDefault(a => a.GetType().Name is "DatabaseGeneratedAttribute");
bool isIdentityByName = false;
if (dbgenByName != null)
{
// Tìm property "DatabaseGeneratedOption" (enum) và đọc giá trị "Identity"
var prop = dbgenByName.GetType().GetProperty("DatabaseGeneratedOption");
var val = prop?.GetValue(dbgenByName)?.ToString(); // ví dụ "Identity"
isIdentityByName = string.Equals(val, "Identity", StringComparison.OrdinalIgnoreCase);
}
if (hasKeyByName && isIdentityByName) return true;
return false;
}
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(IsPkAutoIncrement).ToList();
KeyProperties[type.TypeHandle] = keyProperties; // ghi cache khi miss
return keyProperties;
}
public static List<PropertyInfo> FKPropertiesCache(Type type)
{
if (FKProperties.TryGetValue(type.TypeHandle, out var cachedProps))
{
return cachedProps.ToList();
}
var allProperties = TypePropertiesCache(type);
var keyProperties = allProperties.Where(HasForeignKeyAttribute).ToList();
FKProperties[type.TypeHandle] = keyProperties;
return keyProperties;
}
static bool HasForeignKeyAttribute(PropertyInfo p)
{
if (p.GetCustomAttribute<ForeignKeyAttribute>() != null) return true;
// FK đặt trên navigation và chỉ ra tên prop FK
var t = p.DeclaringType!;
foreach (var nav in t.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
// Fallback by-name nếu không tham chiếu DataAnnotations (tùy dự án)
var attr = nav.GetCustomAttributes(true).FirstOrDefault(a => a.GetType().Name == "ForeignKeyAttribute");
if (attr != null)
{
var val = attr.GetType().GetProperty("Name")?.GetValue(attr);
if (val == null && string.IsNullOrWhiteSpace(val?.ToString()))
return true;
}
}
return false;
}
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;
}
public 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;
}
}
}