【手撸一个ORM】第二步、封装实体描述和实体属性描述

一、实体属性描述 [MyProperty.cs]

  • Name,属性名称
  • PropertyInfo,反射获取的属性信息,后面很多地方需要通过该属性获取对应的实体类型,或调用SetValue进行赋值
  • FieldName,对应的数据表列名
  • IsKey,是否主键
  • IsMap,查询时是否映射该属性,若属性非值类型或string,则默认为false,其他默认为true;若需手动设置为false,如计算属性,需要在MyColumnAttribute中配置 IsMap=false
  • InsertIgnore,插入时忽略
  • UpdateIgnore,更新时忽略
  • JoinAble,是否可以通过Join进行查询,等同于导航属性
  • ForignKey,如果可以通过Join查询,对应的外键名
  • MasterKey,如果可以通过Join查询,对应的主表Id,默认为Id,若主表主键列名不是Id,需手动在MyForeignKey中配置 MasterKey="StudentId"
using System.Reflection;

namespace MyOrm.Reflections
{
    public class MyProperty
    {
        public string Name { get; set; }

        public PropertyInfo PropertyInfo { get; set; }public string FieldName { get; set; }

        public bool IsKey { get; set; }

        public bool IsMap { get; set; } = true;

        public bool InsertIgnore { get; set; }

        public bool UpdateIgnore { get; set; }

        public bool JoinAble { get; set; }

        public string ForeignKey { get; set; }

        public string MasterKey { get; set; }

        public MyProperty(PropertyInfo property)
        {
            Name = property.Name;
            TypeName = property.PropertyType.Name;
            PropertyInfo = property;

            if (property.IsMapAble())
            {
                // 判断是否主键
                var keyAttribute = property.GetKeyAttribute();
                if (keyAttribute != null)
                {
                    //
                    IsKey = true;
                    FieldName = string.IsNullOrWhiteSpace(keyAttribute.FieldName) ? Name : keyAttribute.FieldName;
                    if (keyAttribute.IsIncrement)
                    {
                        // 如果是自增列,不能插入和修改
                        InsertIgnore = true;
                        UpdateIgnore = true;
                    }
                    else
                    {
                        // 如果不是自增列,可插入但不能修改
                        InsertIgnore = true;
                    }
                }
                else if (Name == "Id")
                {
                    FieldName = "Id";
                    IsKey = true;
                    InsertIgnore = true;
                    UpdateIgnore = true;
                }
                else
                {
                    // 可映射的属性
                    var columnAttribute = property.GetMyColumnAttribute();

                    if (columnAttribute != null)
                    {
                        FieldName = string.IsNullOrWhiteSpace(columnAttribute.ColumnName)
                            ? Name
                            : columnAttribute.ColumnName;
                        InsertIgnore = columnAttribute.Ignore || columnAttribute.InsertIgnore;
                        UpdateIgnore = columnAttribute.Ignore || columnAttribute.UpdateIgnore;
                    }
                    else
                    {
                        FieldName = Name;
                    }
                }
            }
            else if (property.IsJoinAble())
            {
                // 可关联查询的属性
                IsMap = false;
                JoinAble = true;
                UpdateIgnore = true;
                InsertIgnore = true;
                var foreignAttribute = property.GetForeignKeyAttribute();
                if (foreignAttribute == null)
                {
                    ForeignKey = Name + "Id";
                    MasterKey = "Id";
                }
                else
                {
                    ForeignKey = string.IsNullOrWhiteSpace(foreignAttribute.ForeignKey)
                        ? Name + "Id"
                        : foreignAttribute.ForeignKey;
                    MasterKey = string.IsNullOrWhiteSpace(foreignAttribute.MasterKey)
                        ? "Id"
                        : foreignAttribute.MasterKey;
                }
            }
            else
            {
                // 其他属性
                IsMap = false;
                UpdateIgnore = true;
                InsertIgnore = true;
            }
        }
    }
}

二、数据实体描述 [MyEntity.cs]

  • KeyColumn,数据表中主键列名称
  • Name,实体名称
  • TableName,实体对应的数据表名称
  • IsSoftDelete,是否软删除(继承ISoftDelete),后面会有说明
  • IsCreateAudit,是否创建审计(保存创建人、创建时间)
  • IsUpdateAudit,是否更新审计(保存修改人、修改时间)
  • Properties,封装过的属性信息列表-考虑过这里用字典保存,但是因为后面需要大量的遍历操作,个人感觉还是List用起来方便,所以最终选择了List类型
using MyOrm.Attributes;
using MyOrm.Commons;
using System;
using System.Collections.Generic;

namespace MyOrm.Reflections
{
    public class MyEntity
    {
        public string KeyColumn { get; set; }

        public string Name { get; set; }

        public string TableName { get; set; }

        public bool IsSoftDelete { get; set; }

        public bool IsCreateAudit { get; set; }

        public bool IsUpdateAudit { get; set; }

        public List<MyProperty> Properties { get; set; }

        public MyEntity(Type type)
        {
            Name = type.Name;
            IsSoftDelete = type.IsInstanceOfType(typeof(ISoftDelete));
            IsCreateAudit = type.IsInstanceOfType(typeof(ICreateAudit));
            IsUpdateAudit = type.IsInstanceOfType(typeof(IUpdateAudit));

            var tableAttr = type.GetCustomAttributes(typeof(MyTableAttribute), false);
            if (tableAttr.Length > 0)
            {
                var tableName = ((MyTableAttribute)tableAttr[0]).TableName;
                TableName = string.IsNullOrWhiteSpace(tableName) ? type.Name.Replace("Entity", "") : tableName;
            }
            else
            {
                TableName = Name;
            }

            Properties = new List<MyProperty>();

            foreach (var propertyInfo in type.GetProperties())
            {
                var property = new MyProperty(propertyInfo);
                if (property.IsKey)
                {
                    KeyColumn = property.FieldName;
                }
                Properties.Add(property);
            }
        }
    }
}

三、实体容器

上面对实体及其属性进行了封装,但是如果每次都需要反射获取,那性能损耗会非常厉害,因此将生成的内容缓存在起来便十分必要了。这里使用的是线程安全的静态字典作为缓存容器。

using System;
using System.Collections.Concurrent;

namespace MyOrm.Reflections
{
    public class MyEntityContainer
    {
        private static readonly ConcurrentDictionary<string, MyEntity> Dict = 
            new ConcurrentDictionary<string, MyEntity>();

        public static MyEntity Get(Type type)
        {
            if (type == null) throw new ArgumentNullException(nameof(type));
            if (Dict.TryGetValue(type.FullName ?? throw new InvalidOperationException(), out var result))
            {
                return result;
            }
            else
            {
                var entity = new MyEntity(type);
                Dict.TryAdd(type.FullName, entity);
                return entity;
            }
        }
    }
}
原文地址:https://www.cnblogs.com/diwu0510/p/10663339.html