第十讲 IQueryable和IEnumerable、Reflection基础、Attribute基础
namespace MyPreContentTeach
{
public class IQueryableAndIEnumerable
{
public void Test1()
{
EFDBEntities db = new EFDBEntities();
// string sql = "select StudentName,Age,StudentId from Students";
// public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source,
// Expression<Func<TSource, bool>> predicate);
var stuList1 = db.Students.Where(s => s.Age > 20);
var stuList2 = stuList1.Where(s => s.Gender == "男");
// Console.WriteLine(stuList2.ToString());
// SELECT
// [Extent1].[StudentId] AS[StudentId],
// [Extent1].[StudentName] AS[StudentName],
// [Extent1].[Gender] AS[Gender],
// [Extent1].[Birthday] AS[Birthday],
// [Extent1].[StudentIdNo] AS[StudentIdNo],
// [Extent1].[Age] AS[Age],
// [Extent1].[PhoneNumber] AS[PhoneNumber],
// [Extent1].[StudentAddress] AS[StudentAddress],
// [Extent1].[ClassId]
// AS[ClassId]
//FROM[dbo].[Students]
// AS[Extent1]
//WHERE([Extent1].[Age] > 20) AND('男' = [Extent1].[Gender])
foreach (var item in stuList2)
{
Console.WriteLine(item.StudentName);
}
//执行发现:在数据库中只有一条SQL语句,并且是两个条件的合并。
//好处: 实现延迟加载。我们对数据库的操作,不应该把所有的查询到,然后在内存筛选,而应该把我们所有的查询综合到一起,然后组合成一条SQL语句去执行。
}
public void Test2()
{
EFDBEntities db = new EFDBEntities();
var stuList1 = db.Students.Where(s => s.Age > 20).AsEnumerable();//结果是IEnumerable类型
var stuList2 = stuList1.Where(s => s.Gender == "男");
//查看stuList1.Where的定义
// public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source,
// Func<TSource, bool> predicate);
//参数接收的是一个Func<>表达式,就是一个委托,委托一旦调用,会立即执行,并将结果保存到内存中
//也就是意味着,我们只会看到s => s.Age > 20这个表达式所对应的SQL语句,而看不到后面的条件,后面的是内存筛选。
foreach (var item in stuList2)
{
Console.WriteLine(item.StudentName);
}
// SELECT
// [Extent1].[StudentId] AS[StudentId],
// [Extent1].[StudentName] AS[StudentName],
// [Extent1].[Gender] AS[Gender],
// [Extent1].[Birthday] AS[Birthday],
// [Extent1].[StudentIdNo] AS[StudentIdNo],
// [Extent1].[Age] AS[Age],
// [Extent1].[PhoneNumber] AS[PhoneNumber],
// [Extent1].[StudentAddress] AS[StudentAddress],
// [Extent1].[ClassId]
// AS[ClassId]
//FROM[dbo].[Students]
// AS[Extent1]
//WHERE[Extent1].[Age] > 20
//执行发现:在数据库中确实只有一条SQL语句,而且是前面的。
//问题:我们在查询数据库的时候,不能直接用这种形式,否则会造成内存严重浪费!而需要将表达式组合好,然后一起提交。
}
}
}
namespace MyPreContentTeach.ORM
{
public class SysAdmin
{
public int LoginId { get; set; }
public string LoginPwd { get; set; }
public string AdminName { get; set; }
}
}
namespace MyPreContentTeach.ORM
{
public class AttributeStudy
{
public void GetAllAttributesFromModel<T>(T model)
{
// Type type = model.GetType();
//typeof关键字和GetType()有什么不同?都能获取对应的类型
//typeof(具体的类名,比如Student,或类型名称,DateTime)不能使用变量
//Type type1 = typeof(string);
PropertyInfo[] properties = model.GetType().GetProperties();
foreach (var item in properties)
{
//获取当前属性中所有自定义的“特性”
var cusAttributes = item.GetCustomAttributes();
foreach (var attribute in cusAttributes)
{
Console.WriteLine("属性名称:"+item.Name);
Console.WriteLine("特性名称:"+attribute.GetType().Name);
Console.WriteLine("=====================");
}
}
}
}
}
namespace MyPreContentTeach.ORM
{
public class PropertityStudy
{
public void GetAllPropertiesFromModel<T>(T model)
{
Type type = model.GetType();
// public PropertyInfo[] GetProperties()
//Type:其实就是指一个特定类的所有的程序信息,当我输出的时候,是以字符串方式,所以会看到这个类型的《完全限定名》
Console.WriteLine("实体类型:" + type); //MyPreContentTeach.Student
Console.WriteLine("实体名称:" + type.Name);
Console.WriteLine("=======================");
PropertyInfo[] properties = type.GetProperties();
//PropertyInfo 不仅包含了属性的基本信息,还可以对属性进行操作...
foreach (var item in properties)
{
Console.WriteLine("属性姓名:" + item.Name);
Console.WriteLine("属性类型:" + item.PropertyType);
Console.WriteLine("属性 值:" + item.GetValue(model, null));
Console.WriteLine("************************************");
}
}
//我们在操作数据库的时候,我们通常是传递一个对象,然后把对象解析成SQL语句(ADO.NET实现原理)
//我们希望就扔给你一个Model,至于说SQL语句,你能不能不让我写呢?EF做到了!因为ORM框架替你完成!
//想一下:SQL语句生成的依据?
//只要看看SQL语句里面有什么?insert into 表名称 (字段名称...) values(对应字段值...)
//基础课程就告诉我们,写实体类的时候,要和数据表做到“映射”(实体名称=表名称,实体属性名=表字段名)
//通过前面的测试:你随便给我一个实体,我能够得到实体名称、所有的属性名、属性值
//最后:我们根据一个封装好的实体,生成一条SQL语句还是非常容易的!
//编写一个生成insert类型SQL语句的方法
public void CreateInsertSQL<T>(T model)
{
//【1】获取实体所有的属性信息数组
PropertyInfo[] properties = model.GetType().GetProperties();
//insert into 表名称 (字段名称...) values(对应字段值...)
//常用的SQL语句,其实是带参数的SQL语句
//insert into 表名称 (字段名称...) values(@字段名称...)
StringBuilder sqlFields = new StringBuilder($"insert into {model.GetType().Name} (");
StringBuilder sqlValues = new StringBuilder(" values (");
List<SqlParameter> paramList = new List<SqlParameter>();
//获取标识列、扩展属性
List<string> idList = GetPropertiesByAttribute(properties, "IdentityAttribute");//硬编码!不提倡(可以使用枚举)
List<string> noTableList = GetPropertiesByAttribute(properties, "NonTableAttribute");
foreach (var item in properties)
{
if (idList.Contains(item.Name) || noTableList.Contains(item.Name)) continue;
sqlFields.Append(item.Name + ",");
sqlValues.Append($"@{item.Name},");
//将属性值封装到参数对象(需要考虑null的问题)
paramList.Add(new SqlParameter($"@{item.Name }", item.GetValue(model, null)));
}
string fields = sqlFields.ToString().TrimEnd(',') + ")";//去掉最后的逗号
string values = sqlValues.ToString().TrimEnd(',') + ")";
string sql = fields + values;
Console.WriteLine(sql);
//以上是假想我们添加一个对象的时候,把所有的属性都添加进去,但是实际中?有的属性是不能添加的
//比如,标识列、扩展属性、属性值是null的也要去掉
//问题?如何找到我们不需要的属性?我们可以使用特性Attribute
//如何理解Attribute?
//特性可以被添加到类、属性、方法等很多地方!如何创建一个特性类呢?只要添加一个类,并且让它继承Attr类就行。
//特性其实非常常见:MVC属性的验证,是不是有特性?WebService的时候,方法是不是也有WebMethod特性、[httpPost]
//特性:是一种特殊的类,这种类是用[]标记到对应的位置,好处?
//好处:它不仅是一个标记,还可以有标记值!我们使用特性,可以轻松的将一个类、方法、属性进行扩展!而这种扩展,对原有封装,没有任何破坏!
//AOP:特性其其实就是一种AOP的反映!
}
//继续思考:如果某一个属性没有被特性过滤,但是有null值或其他默认值,我们应该怎么样处理?
public List<string> GetPropertiesByAttribute(PropertyInfo[] properties, string attributeName)
{
List<string> columList = new List<string>();
foreach (var item in properties)
{
//首先过滤一下我们不需要的属性?使用特性过滤(标识列、扩展属性)
var cusAttributes = item.GetCustomAttributes();
foreach (var attribute in cusAttributes)
{
string attrName = attribute.GetType().Name;
if (attrName.Equals(attributeName))
{
columList.Add(item.Name);
}
}
}
return columList;
}
}
}
namespace MyPreContentTeach.ORM
{
[Serializable]
public class Student
{
[PrimaryKey]
[Identity]
public int StudentId { get; set; }
public string StudentName { get; set; }
public DateTime Birthday { get; set; }
public string Gender { get; set; }
public string StudentIdNo { get; set; }
public int Age { get; set; }
public string StuImage { get; set; }
public string PhoneNumber { get; set; }
public string StudentAddress { get; set; }
public string CardNo { get; set; }
public int ClassId { get; set; }
//扩展属性
[NonTable]
public string ClassName { get; set; }
}
/// <summary>
/// 标识列特性
/// </summary>
public class IdentityAttribute : Attribute
{
public bool IsIdentity { get; } = true;
}
/// <summary>
/// 扩展属性
/// </summary>
public class NonTableAttribute : Attribute
{
public bool IsNotField { get; } = true;
}
/// <summary>
/// 主键属性
/// </summary>
public class PrimaryKeyAttribute : Attribute
{
public bool IsPKey { get; } = true;
}
//根据需要添加其他的特性...
}
namespace MyPreContentTeach.ORM
{
class Program
{
static void Main(string[] args)
{
IQueryableAndIEnumerable TestClass1 = new IQueryableAndIEnumerable();
//TestClass1.Test1();
TestClass1.Test2();
#region 属性和特性
//封装对象
Student student = new Student()
{
StudentName = "喜科堂VIP高级学员",
Gender = "女",
Birthday = Convert.ToDateTime("1997-10-10"),
StudentIdNo = "120223199710101513",
PhoneNumber = "022-99888890",
StudentAddress = "该用户地址不详",
CardNo = "1000003",
ClassId = 1,
Age = DateTime.Now.Year - Convert.ToDateTime("1997-10-10").Year,
StuImage = ""
};
PropertityStudy propertityStudy = new PropertityStudy();
propertityStudy.GetAllPropertiesFromModel(student);
Console.WriteLine("++++++++++++++++++++++++++++");
propertityStudy.GetAllPropertiesFromModel(new SysAdmin
{
LoginId = 10000,
LoginPwd = "123456",
AdminName = "LLR"
});
Console.WriteLine("++++++++++++++++++++++++++++");
propertityStudy.CreateInsertSQL(student);
Console.WriteLine("++++++++++++++++++++++++++++");
AttributeStudy attributeStudy = new AttributeStudy();
attributeStudy.GetAllAttributesFromModel(student);
Console.Read();
}
#endregion
}
}