mysql orm emit_来一点反射和Emit,让ORM的使用极度简化

PDF.NET开发框架一直是号称“无需反射”的,因为它的ORM框架(PDF.NET不仅仅是一个ORM框架,详细请见官网)中实体类的设计很特别,不需要反射就能够获知映射的字段信息,我们用实际的例子来说明下。

1,实体类解析

假设有这样一个数据库LocalDb中有一个表Table_User ,如下图:

dee3aeed9683a726442db4646210bf5b.png

图中的数据库用PDF.NET集成开发工具打开,该工具可以在官网找到下载地址。找到该表后,在左边的表名称树节点或者右边的查询窗口,鼠标右键菜单上,找到生成实体类的功能,具体过程这里不做演示了,因为这不是本文的主题。

下面,我们看看生成的实体类:

[Serializable()]public partial classTable_User : EntityBase {publicTable_User()

{

TableName= "Table_User";

EntityMap=EntityMapType.Table;//IdentityName = "标识字段名";

IdentityName = "UID";//PrimaryKeys.Add("主键字段名");

PrimaryKeys.Add("UID");

}protected override voidSetFieldNames()

{

PropertyNames= new string[] { "UID", "Name", "Sex", "Height", "Birthday"};

}///

///

///

publicSystem.Int32 UID

{get { return getProperty("UID"); }set { setProperty("UID", value); }

}///

///

///

publicSystem.String Name

{get { return getProperty("Name"); }set { setProperty("Name", value, 50); }

}///

///

///

publicSystem.Boolean Sex

{get { return getProperty("Sex"); }set { setProperty("Sex", value); }

}///

///

///

publicSystem.Single Height

{get { return getProperty("Height"); }set { setProperty("Height", value); }

}///

///

///

publicSystem.DateTime Birthday

{get { return getProperty("Birthday"); }set { setProperty("Birthday", value); }

}

}

在实体类的构造函数中,下面几个属性指明了表的一些特性:

TableName = "Table_User";

表示实体类映射的表名称;

EntityMap =EntityMapType.Table;

表示实体类的映射类型是一个表,当然还可以是视图、存储过程、函数等;

//IdentityName = "标识字段名";

IdentityName = "UID";//PrimaryKeys.Add("主键字段名");

PrimaryKeys.Add("UID");

这个不用多说,有注释了。注意主键可以设置多个的。

protected override voidSetFieldNames()

该方法说明了实体类映射的哪些字段。

publicSystem.Int32 UID

{get { return getProperty("UID"); }set { setProperty("UID", value); }

}

UID属性的Get和Set方法也很简单,看名字就知道它的功能了。注意属性中映射了字段名称,比如数据库的字段是UID,那么属性改个名字,象下面这样写也是完全可以的:

publicSystem.Int32 UserId

{get { return getProperty("UID"); }set { setProperty("UID", value); }

}

2,问题和优化

因此,从总体上来说,PDF.NET实体类的结构很简单,比起EF的DbFirst方式和其它ORM框架的实体类来说,要简单很多,所以我一般情况下都是手写实体类,但是对于不是很熟悉框架的朋友来说,如果没有代码工具,要手写还是比较麻烦,毕竟属性的Get和Set访问器还是要多写一行代码。

如果我们将实体类先抽象出来一个接口,然后让框架根据该接口,自动继承EntityBase基类和实现接口的属性方法,那该多好啊!

PS:这个想法我已经想了好几年了,但总觉得不是很有必要。现在,CodeFirst越来越流行了,都是先定义实体类,然后在定义或者自动创建数据库。同样,PDF.NET的广大用户也要求能够更简单的使用框架,跟上时代潮流。所以,我最近才付诸实际行动。

我们用一点反射和一点Emit,来完成这个过程:

反射得到构造函数和属性定义:

//得到类型生成器

TypeBuilder typeBuilder =modBuilder.DefineType(newTypeName, newTypeAttribute, newTypeParent, newTypeInterfaces);

typeBuilder.AddInterfaceImplementation(targetType);//定义构造函数

BuildConstructor(typeBuilder, newTypeParent, targetType.Name);//以下将为新类型声明方法:新类型应该override基类型的所以virtual方法

PropertyInfo[] pis =targetType.GetProperties();

List propertyNames = new List();foreach (PropertyInfo pi inpis)

{

propertyNames.Add(pi.Name);//属性构造器

PropertyBuilder propBuilder =typeBuilder.DefineProperty(pi.Name,

System.Reflection.PropertyAttributes.HasDefault,

pi.PropertyType,null);

MethodAttributes getSetAttr= MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.NewSlot |MethodAttributes.Final;//构造Get访问器

MethodBuilder getPropMethodBuilder = typeBuilder.DefineMethod("get_" +pi.Name,

getSetAttr,

pi.PropertyType,

Type.EmptyTypes);

GeterIL(pi.Name, newTypeParent, pi.PropertyType, getPropMethodBuilder);//构造Set访问器

MethodBuilder setPropMethodBuilder = typeBuilder.DefineMethod("set_" +pi.Name,

getSetAttr,null,newType[] { pi.PropertyType });

SeterIL(pi.Name, newTypeParent, pi.PropertyType, setPropMethodBuilder);//添加到属性构造器

propBuilder.SetGetMethod(getPropMethodBuilder);

propBuilder.SetSetMethod(setPropMethodBuilder);

}

MethodBuilder SetFieldNamesBuilder= typeBuilder.DefineMethod("SetFieldNames", MethodAttributes.Family | MethodAttributes.Virtual |MethodAttributes.HideBySig);

SetFieldNamesIL(newTypeParent, SetFieldNamesBuilder, propertyNames.ToArray());//真正创建,并返回

Type resuleType=typeBuilder.CreateType();

Emit方式得到属性访问器的具体构造过程:

///

///构造Get访问器///

///

///

///

///

void GeterIL(stringpropertyName, Type baseType, Type propertyType, MethodBuilder methodBuilder)

{

MethodInfo getProperty= null;

MethodInfo[] ms= typeof(EntityBase).GetMethods(BindingFlags.Instance |BindingFlags.NonPublic);foreach (MethodInfo info inms)

{if (info.Name == "getProperty" &&info.IsGenericMethod)

{

getProperty=info;break;

}

}

getProperty=getProperty.MakeGenericMethod(propertyType);var ilGenerator =methodBuilder.GetILGenerator();

ilGenerator.Emit(OpCodes.Ldarg_0);

ilGenerator.Emit(OpCodes.Ldstr, propertyName);

ilGenerator.Emit(OpCodes.Call, getProperty);

ilGenerator.Emit(OpCodes.Ret);

}///

///构造Set访问器///

///

///

///

///

void SeterIL(stringpropertyName, Type baseType, Type propertyType, MethodBuilder methodBuilder)

{

MethodInfo setProperty=null;//= baseType.GetMethod("setProperty", BindingFlags.Instance | BindingFlags.NonPublic);

MethodInfo[] ms = typeof(EntityBase).GetMethods(BindingFlags.Instance |BindingFlags.NonPublic);foreach (MethodInfo info inms)

{if (info.Name == "setProperty")

{if (info.GetParameters().Length == 2)

{

setProperty=info;break;

}

}

}var ilGenerator =methodBuilder.GetILGenerator();

ilGenerator.Emit(OpCodes.Ldarg_0);

ilGenerator.Emit(OpCodes.Ldstr, propertyName);

ilGenerator.Emit(OpCodes.Ldarg_1);//是否是值类型

if(propertyType.IsValueType)

ilGenerator.Emit(OpCodes.Box, propertyType);

ilGenerator.Emit(OpCodes.Call, setProperty);

ilGenerator.Emit(OpCodes.Ret);

}

在上面的IL代码方法中,EntityBase 的 getProperty 和setProperty  方法有泛型实现和重载,所以只有遍历实体类所有的方法。

写Emit代码也不是想象中的那么复杂,基本过程就是先手工写好C#代码,编译得到Exe或者Dll,然后用ILDASM或反编译工具,得到IL代码,最后就是看着IL代码,用Emit一个个对应发出代码,就行了。

OK,我们将这个代码封装到一个EntityBuilder类中,定一个构造实体类的方法

private static Dictionary dictEntityType = new Dictionary();private static object sync_lock = new object();///

///根据接口类型,创建实体类的实例///

///

///

public static T CreateEntity() where T:class{

Type targetType= null;

Type sourceType= typeof(T);if (sourceType.BaseType == typeof(EntityBase)) //如果本身是实体类,则不生成

{

targetType=sourceType;

}else{if (!dictEntityType.TryGetValue(sourceType, outtargetType))

{lock(sync_lock)

{if (!dictEntityType.TryGetValue(sourceType, outtargetType))

{

EntityBuilder builder= newEntityBuilder(sourceType);

targetType=builder.Build();

dictEntityType[sourceType]=targetType;

}

}

}

}

T entity=(T)Activator.CreateInstance(targetType);returnentity;

}

万事俱备,只欠东风!

3,更简单的使用方式

下面,我们将前面的实体类抽象出一个接口ITable_User :

public interfaceITable_User

{

DateTime Birthday {get; set; }float Height { get; set; }string Name { get; set; }bool Sex { get; set; }int UID { get; set; }

}

再做一点准备工作,在应用程序配置文件里面配置一下连接,框架默认取最后一个配置:

然后像下面这样使用实体类并查询:

static voidTestDynamicEntity()

{

ITable_User user= EntityBuilder.CreateEntity();//如果接口的名称不是"ITableName" 这样的格式,那么需要调用 MapNewTableName方法指定//((EntityBase)user).MapNewTableName("Table_User");

OQL qUser=OQL.From((EntityBase)user).Select(user.UID, user.Name, user.Sex).END;

List users = EntityQuery.QueryList(qUser, MyDB.Instance);

}

在代码中,只需要

EntityBuilder.CreateEntity();

这样的方式,定义一个实体类的接口,就自动创建了我们的实体类,是不是非常简单了?

有了实体类,然后可以像普通实体类那样来使用ORM查询语言--OQL,不过原来的EntityQuery泛型实体查询类得改进下,才可以支持“动态实体类”的查询。

当前功能已经在PDF.NET Ver 4.6.4.0525 版本实现,之前的版本,大家可以去开源项目下载:http://pwmis.codeplex.com

4,动态实体类的使用约束

这里说的“动态实体类”是通过程序在运行时动态创建得到实体类,而不是预先在源码中写好的实体类。对本方案而言,使用动态实体类有以下几点约束:

使用接口(interface)定义实体类

实体类属性定义需要get,set 访问器同时存在(否则怎么保存数据到数据库?)

属性名称跟表字段名称一致,且属性类型跟字段的数据类型相兼容

接口名称为“I”打头的表名称,否则需要使用时候映射一下

如果你不想有这些约束,或者想灵活映射字段和属性,那么还是手写实体类吧,多写一行代码,象本文开头示例的那个实体类一样。

-----------------------------------------

欢迎加入PDF.NET开源技术团队,做最快最好的开发框架!

好的,让我来回答您的问题。 首先,Django 是一个流行的 Python Web 框架,它提供了许多强大的功能,其中之一就是 ORM(对象关系映射)。 ORM 允许您使用 Python 对象来处理数据库操作,而不是直接编写 SQL 语句。这样做有很多好处,比如更容易维护和更易于理解。 下面是一些基本的 ORM 操作,用于在 Django 中执行增删改操作。 首先,要使用 ORM,您需要在您的 Django 项目中创建一个模型。模型是一个 Python 类,它代表了数据库中的一个表。 例如,这是一个简单的模型类,它代表了一个名为 `Book` 的表: ``` from django.db import models class Book(models.Model): title = models.CharField(max_length=100) author = models.CharField(max_length=100) published_date = models.DateField() ``` 接下来,我们可以使用以下代码将一个新的 `Book` 对象插入到数据库中: ``` book = Book(title='Django for Beginners', author='William Vincent', published_date='2021-01-01') book.save() ``` 我们可以使用以下代码来更新一个现有的 `Book` 对象: ``` book = Book.objects.get(title='Django for Beginners') book.author = 'William S. Vincent' book.save() ``` 最后,我们可以使用以下代码来删除一个 `Book` 对象: ``` book = Book.objects.get(title='Django for Beginners') book.delete() ``` 这些是最基本的 ORM 操作,但是 Django ORM 还提供了许多其他的高级功能,比如过滤、排序和聚合查询等。如果您想了解更多信息,请查看 Django 的官方文档。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值