C# 泛型绑定
C# Generic binding
我正在为学校做作业,并尝试实现尽可能多的功能只是为了学习。因此,我制作了一个通用映射器,将数据库表映射到对象以查看可能的情况。本例中的 Db 是本地的。我知道我打了很多电话,应该以非常不同的方式解决这个问题,但是....
除了 class 拥有另一个 class 的集合外,一切都按预期工作。
示例:
class Student {
public int Id { get; set; }
public string Name { get; set; }
}
我在数据库中填写所有学生列表的方法。
public List<TModel> MapEntitiesFromDb<TModel>(string tablename, string customquery = "") where TModel : class, new()
{
try
{
sql = ValidateSelectSql(tablename, customquery);
}
catch (AccessViolationException ex) { Console.WriteLine(ex.Message); }
command.CommandText = sql;
command.Connection = conn;
List<TModel> list = new List<TModel>();
try
{
using (conn)
{
Type t = new TModel().GetType();
conn.Open();
using (reader = command.ExecuteReader())
{
if (t.GetProperties().Length != reader.FieldCount)
throw new Exception("There is a mismatch between the amount of properties and the database columns. Please check the input code and try again.");
//Possible check is to store each column and property name in arrays and match them to a new boolean array, if there's 1 false throw an exception.
string columnname;
string propertyname;
//Pairing properties with columns
while (reader.Read())
{
TModel obj = new TModel();
for (int i = 0; i < reader.FieldCount; i++)
{
columnname = reader.GetName(i).ToString().ToLower();
PropertyInfo[] properties = t.GetProperties();
foreach (PropertyInfo propertyinfo in properties)
{
propertyname = propertyinfo.Name.ToLower();
if (propertyname == columnname)
{
propertyinfo.SetValue(obj, reader.GetValue(i));
break;
}
}
}
list.Add(obj);
}
}
}
}
catch (Exception ex) { Console.WriteLine(ex.Message); }
return list;
}
我的 ValidateSelectSql 只是 return 需要在查询中使用的 sql 字符串。
调用后:
List<Student> = MapEntitiesFromDb<Student>("students");
它将 return 列出所有符合预期的学生。
添加集合时出错,例如:
class Student {
public Student()
{
this.Courses = new List<Course>();
string customsqlquery = ::: this works and is tested! :::
Courses = MapEntitiesFromDb<Course>("", customsqlquery);
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Course> Courses;
}
课程列表 return 是空的,在调试器工具的帮助下,我在创建对象时发现 Id 属性 当然是 0。在我的查询中,我正在过滤学生 ID,但是在执行填充构造函数中的课程列表的方法时,学生的 ID 将始终为 0,因为它是在稍后阶段设置的,结果列表中没有课程.
我想知道是否应该在设置其他属性后检查 ICollection 属性,如果是,则对 return 中的对象执行现在的方法在构造函数中?
我不能在 TModel 上调用任何方法,否则就像查找 TModel 是否有集合 属性 并调用 obj.FillCollection() 一样简单;在 GetEntitiesFromDb 方法中分配 Id 属性 之后。
我也在考虑递归。我必须再次查找 obj 是否有一个集合 属性 然后调用 GetEntitiesFromDB 但这似乎是不可撤销的,因为我还需要找出 <> 之间的类型并且我无法从外部发送任何自定义查询。 ..
也许从另一个角度来解决它?
我真的可以使用一些关于如何解决这个问题的建议。
最直接的方法是让集合 属性 延迟加载它需要的内容。我还建议您使用 IEnumerable<T>
而不是 ICollection<T>
,因为这代表数据库中当前内容的只读视图,任何人都不应该以任何方式修改它。
public class Student
{
private readonly Lazy<IEnumerable<Course>> courses;
public int Id { get; set; }
public IEnumerable<Course> Courses => this.courses.Value;
public Student()
{
this.courses = new Lazy<IEnumerable<Course>>(LoadCourses);
}
private IEnumerable<Course> LoadCourses()
{
var sql = "custom SQL query that uses this.Id after it's loaded";
return MapEntitiesFromDb(sql);
}
}
我之所以推荐这种方法,是因为您提到这只是一个学术练习,可以帮助您了解可用的工具。在实际的生产环境中,这种方法很快就会变得笨拙,我建议改用 Entity Framework(这可能是您可能想了解的其他内容)。
我正在为学校做作业,并尝试实现尽可能多的功能只是为了学习。因此,我制作了一个通用映射器,将数据库表映射到对象以查看可能的情况。本例中的 Db 是本地的。我知道我打了很多电话,应该以非常不同的方式解决这个问题,但是....
除了 class 拥有另一个 class 的集合外,一切都按预期工作。
示例:
class Student {
public int Id { get; set; }
public string Name { get; set; }
}
我在数据库中填写所有学生列表的方法。
public List<TModel> MapEntitiesFromDb<TModel>(string tablename, string customquery = "") where TModel : class, new()
{
try
{
sql = ValidateSelectSql(tablename, customquery);
}
catch (AccessViolationException ex) { Console.WriteLine(ex.Message); }
command.CommandText = sql;
command.Connection = conn;
List<TModel> list = new List<TModel>();
try
{
using (conn)
{
Type t = new TModel().GetType();
conn.Open();
using (reader = command.ExecuteReader())
{
if (t.GetProperties().Length != reader.FieldCount)
throw new Exception("There is a mismatch between the amount of properties and the database columns. Please check the input code and try again.");
//Possible check is to store each column and property name in arrays and match them to a new boolean array, if there's 1 false throw an exception.
string columnname;
string propertyname;
//Pairing properties with columns
while (reader.Read())
{
TModel obj = new TModel();
for (int i = 0; i < reader.FieldCount; i++)
{
columnname = reader.GetName(i).ToString().ToLower();
PropertyInfo[] properties = t.GetProperties();
foreach (PropertyInfo propertyinfo in properties)
{
propertyname = propertyinfo.Name.ToLower();
if (propertyname == columnname)
{
propertyinfo.SetValue(obj, reader.GetValue(i));
break;
}
}
}
list.Add(obj);
}
}
}
}
catch (Exception ex) { Console.WriteLine(ex.Message); }
return list;
}
我的 ValidateSelectSql 只是 return 需要在查询中使用的 sql 字符串。
调用后:
List<Student> = MapEntitiesFromDb<Student>("students");
它将 return 列出所有符合预期的学生。
添加集合时出错,例如:
class Student {
public Student()
{
this.Courses = new List<Course>();
string customsqlquery = ::: this works and is tested! :::
Courses = MapEntitiesFromDb<Course>("", customsqlquery);
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Course> Courses;
}
课程列表 return 是空的,在调试器工具的帮助下,我在创建对象时发现 Id 属性 当然是 0。在我的查询中,我正在过滤学生 ID,但是在执行填充构造函数中的课程列表的方法时,学生的 ID 将始终为 0,因为它是在稍后阶段设置的,结果列表中没有课程.
我想知道是否应该在设置其他属性后检查 ICollection 属性,如果是,则对 return 中的对象执行现在的方法在构造函数中?
我不能在 TModel 上调用任何方法,否则就像查找 TModel 是否有集合 属性 并调用 obj.FillCollection() 一样简单;在 GetEntitiesFromDb 方法中分配 Id 属性 之后。
我也在考虑递归。我必须再次查找 obj 是否有一个集合 属性 然后调用 GetEntitiesFromDB 但这似乎是不可撤销的,因为我还需要找出 <> 之间的类型并且我无法从外部发送任何自定义查询。 ..
也许从另一个角度来解决它?
我真的可以使用一些关于如何解决这个问题的建议。
最直接的方法是让集合 属性 延迟加载它需要的内容。我还建议您使用 IEnumerable<T>
而不是 ICollection<T>
,因为这代表数据库中当前内容的只读视图,任何人都不应该以任何方式修改它。
public class Student
{
private readonly Lazy<IEnumerable<Course>> courses;
public int Id { get; set; }
public IEnumerable<Course> Courses => this.courses.Value;
public Student()
{
this.courses = new Lazy<IEnumerable<Course>>(LoadCourses);
}
private IEnumerable<Course> LoadCourses()
{
var sql = "custom SQL query that uses this.Id after it's loaded";
return MapEntitiesFromDb(sql);
}
}
我之所以推荐这种方法,是因为您提到这只是一个学术练习,可以帮助您了解可用的工具。在实际的生产环境中,这种方法很快就会变得笨拙,我建议改用 Entity Framework(这可能是您可能想了解的其他内容)。