基于 Entity Framework FluentAPI 思想的强类型的 Lambda Expressions 实体映射
2013-02-06 10:36 音乐让我说 阅读(1612) 评论(2) 编辑 收藏 举报可能你还感兴趣:
1. C# Lambda 表达式学习之(一):得到一个类的字段(Field)或属性(Property)名,强类型得到
2. C# Lambda 表达式学习之(二):LambdaExpression 实战练习
3. 基于 Entity Framework FluentAPI 思想的强类型的 Lambda Expressions 实体映射
4. C# Lambda 表达式学习之(三):动态构建类似于 c => c.Age == null || c.Age > 18 的表达式
5. C# Lambda 表达式学习之(四):动态构建类似于 c => c.Age == 2 || c.Age == 5 || c => c.Age == 17 等等一个或多个 OrElse 的表达式
最近在优化一套自己以前的通用数据层访问的小“架构”,主要是根据数据库中的表来建立对应的 Repository, 起初 Entities 层的每个类名、属性名都要跟数据库中的表名、字段名一模一样,比如数据库中有一个 Category 表,里面有 Id、CateName 两个字段,那么建立的 C# 实体也必须是 :
/// <summary> /// 产品种类 /// </summary> public partial class Category { public Category() { } /// <summary> /// 种类的 Id /// </summary> public int Id { get; set; } /// <summary> /// 种类名称 /// </summary> public string CateName { get; set; } }
后来,同事说这样可能不是很好, 很有可能数据库中的表名、字段名不与 C# 实体完全相同,能不能像 Entity Framework CodeFirst 中一样,通过建立 Map 文件来映射,我仔细想了想,觉得有道理,后来就有了下面这样的改进,基本上算实现了,且往下看。
1. 不管数据库中的表名和字段名怎样,我们先建立一个 Category 类。
/// <summary> /// 产品种类 /// </summary> public partial class Category { public Category() { } /// <summary> /// 种类的 Id /// </summary> public int Id { get; set; } /// <summary> /// 种类名称 /// </summary> public string CateName { get; set; } }
2. 建立一个 CategoryMap 类,继承自 EntityMappingConfiguration<Category> 类。
关于 EntityMappingConfiguration<T> 类,后面会介绍。
/// <summary> /// 产品种类表与数据库中的表的映射关系配置 /// 规则01: 该类中可以设置表名(TableName)、主键列(HasKey)、属性与数据库中列的映射(MapProperty)。 /// 规则02: 可以不设置表名,如果不设置则默认实体的类名就是数据库中的表名。 /// 规则03: 可以不指定主键列,默认会以 Id 或 [类名+Id] 的列作为主键,如果都不存在,则抛出异常。 /// 如果同时存在 Id 和 [类名+Id] 字段,则以 Id 作为主键。 /// 规则04: 可以不指定属性与数据库中列的映射,如果不设置则默认实体的属性名就是数据库中表的列名。 /// </summary> public class CategoryMap : EntityMappingConfiguration<Category> { public CategoryMap() { this.TableName = "MyCategory"; this.HasKey(c => c.Id); this.MapProperty(c => c.Id, "myId"); this.MapProperty(c => c.CateName, "myCateName"); } }
PS:可以不建立 CategoryMap 类,如果不建立,那么默认 Category 类对应数据库中的 Category 表,且字段 Id、CateName 和数据库中一模一样。
3. 在 Global.asax 中的 Application_Start 事件中注册它
public class Global : System.Web.HttpApplication { protected void Application_Start(object sender, EventArgs e) { GlobalEntityMappingHelper.RegisterEntityMapping(); } } public class GlobalEntityMappingHelper { public static void RegisterEntityMapping() { new CategoryMap(); // 下面可以注册其它的实体配置... // ... } }
PS:一定要注册 CategoryMap 类,要不然不会生效,这和 Entity Framework FluentAPI 思想保持一样。
4. 这样就可以在相应的地方使用它了,建立一个 index.aspx 的文件,在后台代码中:
protected void Page_Load(object sender, EventArgs e) { if(!IsPostBack) { InitData(); } } protected void InitData() { string tableName = EntityMappingConfiguration<Category>.GetTableName(); string primaryKeyName = EntityMappingConfiguration<Category>.GetPrimaryKeyName(); string cateName = EntityMappingConfiguration<Category>.GetProperty(c => c.CateName); this.ltMessage.Text = string.Format(@"数据库表名:{0}<br/> 数据库主键列:{1}<br/> 属性 ""CateName"" 所对应的数据库列名:{2}<br/>", tableName, primaryKeyName, cateName); }
效果如下:
附录:EntityMappingConfiguration<T> 的代码如下:
/// <summary> /// 实体映射配置基类 /// </summary> /// <typeparam name="TEntityType">实体的类型</typeparam> public abstract class EntityMappingConfiguration<TEntityType> where TEntityType : class, new() { private static readonly Dictionary<string, string> _dicTableNames = new Dictionary<string, string>(); private static readonly Dictionary<string, string> _dicPrimaryKey = new Dictionary<string, string>(); private static readonly Dictionary<string, string> _dicArrayMember = new Dictionary<string, string>(); private const string SEPARATOR = "="; #region private or protected 方法 /// <summary> /// 设置该实体类与之相对应的数据库中的表名 /// </summary> protected string TableName { set { if(string.IsNullOrEmpty(value)) { throw new ArgumentNullException("赋予的表名不能为空!"); } string className = typeof(TEntityType).FullName; _dicTableNames.Add(className, value); } } /// <summary> /// 设置主键列与之对应实体的属性 /// </summary> /// <typeparam name="TKey">主键列的类型</typeparam> /// <param name="keyExpression">指定主键列的 Lambda 表达式</param> protected void HasKey<TKey>(Expression<Func<TEntityType, TKey>> keyExpression) { if (keyExpression == null) { throw new ArgumentNullException(); } MemberExpression memberExpression; InspectIsMemberExpression<TKey>(keyExpression, true, out memberExpression); string className = typeof(TEntityType).FullName; _dicPrimaryKey.Add(className, GetCacheKey(memberExpression)); } /// <summary> /// 设置数据表中的列与之对应实体的属性 /// </summary> /// <typeparam name="TProperty">属性的类型</typeparam> /// <param name="propertyExpression">指定实体中的属性的 Lambda 表达式</param> /// <param name="dbColumnName">数据表的列名</param> protected void MapProperty<TProperty>(Expression<Func<TEntityType, TProperty>> propertyExpression, string dbColumnName) { if (propertyExpression == null) { throw new ArgumentNullException(); } if (string.IsNullOrEmpty(dbColumnName)) { throw new ArgumentNullException(); } MemberExpression memberExpression; InspectIsMemberExpression<TProperty>(propertyExpression, true, out memberExpression); _dicArrayMember.Add(GetCacheKey(memberExpression), dbColumnName); } #endregion #region 静态方法 private static bool InspectIsMemberExpression<TProperty>(Expression<Func<TEntityType, TProperty>> express, bool throwExOnNull, out MemberExpression memberExpression) { if (express == null) { throw new ArgumentNullException("express"); } memberExpression = express.Body as MemberExpression; if (memberExpression == null) { if (throwExOnNull) { throw new ArgumentException("请为类型 \"" + typeof(TEntityType).FullName + "\" 的指定一个字段(Field)或属性(Property)作为 Lambda 的主体(Body)。"); } return false; } return true; } private static string GetCacheKey(PropertyInfo pInfo) { return GetCacheKey(pInfo.DeclaringType.FullName, pInfo.PropertyType.FullName, pInfo.Name); } private static string GetCacheKey(MemberExpression memberExpression) { return GetCacheKey(memberExpression.Member.DeclaringType.FullName, memberExpression.Type.FullName, memberExpression.Member.Name); } private static string GetCacheKey(string classFullName, string propertyFullName, string propertyName) { return classFullName + SEPARATOR + propertyFullName + SEPARATOR + propertyName; } /// <summary> /// 得到实体的属性所对应的数据库中表的列名 /// </summary> /// <typeparam name="TProperty">属性的类型</typeparam> /// <param name="propertyExpression">指定实体中的属性的 Lambda 表达式</param> /// <returns></returns> public static string GetProperty<TProperty>(Expression<Func<TEntityType, TProperty>> propertyExpression) { if (propertyExpression == null) { throw new ArgumentNullException(); } MemberExpression memberExpression; InspectIsMemberExpression<TProperty>(propertyExpression, true, out memberExpression); string dbColumnName; _dicArrayMember.TryGetValue(GetCacheKey(memberExpression), out dbColumnName); if (string.IsNullOrEmpty(dbColumnName)) { return memberExpression.Member.Name; //throw new NotSupportedException(string.Format("您还没有给类 \"{0}\" 的属性 \"{1}\" 执行 Map 操作!", typeof(TEntityType).FullName, memberExpression.Member.Name)); } return dbColumnName; } /// <summary> /// 得到表名 /// </summary> /// <returns></returns> public static string GetTableName() { Type currentType = typeof(TEntityType); string classFullName = currentType.FullName; string mappingConfigTableName; _dicTableNames.TryGetValue(classFullName, out mappingConfigTableName); if (string.IsNullOrEmpty(mappingConfigTableName)) { return currentType.Name; //throw new NotSupportedException(string.Format("您还没有给类 \"{0}\" 设置 TableName 属性!", classFullName)); } return mappingConfigTableName; } private static PropertyInfo GetDefaultPrimaryKey(Type currentType) { PropertyInfo pInfo = currentType.GetProperty("Id"); return pInfo ?? currentType.GetProperty(currentType.Name + "Id"); } /// <summary> /// 得到主键列名 /// </summary> /// <returns></returns> public static string GetPrimaryKeyName() { Type currentType = typeof(TEntityType); string classFullName = currentType.FullName; string mappingConfigKey; _dicPrimaryKey.TryGetValue(classFullName, out mappingConfigKey); // 尝试得到主键 Property if (string.IsNullOrEmpty(mappingConfigKey)) { PropertyInfo pInfo = GetDefaultPrimaryKey(currentType); if (pInfo == null) { throw new NotSupportedException(string.Format("您还没有给类 \"{0}\" 执行 HasKey 操作!", classFullName)); } mappingConfigKey = GetCacheKey(pInfo); _dicPrimaryKey.Add(classFullName, mappingConfigKey); } string dbColumnName; _dicArrayMember.TryGetValue(mappingConfigKey, out dbColumnName); // 尝试得到主键 Property 所配置的数据库字段的名称 if (string.IsNullOrEmpty(dbColumnName)) { // 为空说明没有配置,那么就取这个主键 Property 的名称 dbColumnName = mappingConfigKey.Split(new[] { SEPARATOR }, StringSplitOptions.None)[2]; _dicArrayMember.Add(mappingConfigKey, dbColumnName); //string[] temp = mappingConfigKey.Split(new[] { SEPARATOR }, StringSplitOptions.None); //throw new NotSupportedException(string.Format("您还没有给类 \"{0}\" 的主键列 \"{1} {2}\" 执行 MapProperty 操作!", classFullName, temp[1], temp[2])); } return dbColumnName; } #endregion }
快过年了,提前给大家拜年,祝大家新年快乐,恭喜发财,哈哈哈。。。。
谢谢浏览!
作者:音乐让我说(音乐让我说 - 博客园)
出处:http://music.cnblogs.com/
文章版权归本人所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。