简体   繁体   English

C#通用绑定

[英]C# Generic binding

I am working on an assignment for school and trying to implement as much features just for learning sake. 我正在为学校分配作业,并尝试为学习而实现尽可能多的功能。 Hence I've made a generic mapper that maps databse tables to objects to see what's possible. 因此,我做了一个通用的映射器,将数据库表映射到对象,以查看可能的情况。 The Db in this case is local. 在这种情况下,Db是本地的。 I know I'm making loads and loads of calls and should go around this very differently but.... 我知道我要处理大量的呼叫,应该以不同的方式解决这个问题,但是...

Everything works as intended except for when a class has a Collection of another class. 一切都按预期工作,但当一个类具有另一个类的Collection时除外。

Example: 例:

class Student {

public int Id { get; set; }
public string Name { get; set; }
}

My method for filling a list of all the students in the database. 我的方法用于填充数据库中所有学生的列表。

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;
    }

My ValidateSelectSql just returns the sql string that needs to be used in the query. 我的ValidateSelectSql只是返回查询中需要使用的sql字符串。

After calling: 致电后:

List<Student> = MapEntitiesFromDb<Student>("students");

It will return a list with all the students like intended. 它将返回一个列表,其中包含所有想要的学生。

Things go wrong when I add a collection for example: 例如,当我添加一个集合时,事情就出错了:

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;
}

The courses list returned empty and with some help of the debugger tool I found out at the time of creating the object the Id property is 0 of course. 课程列表返回为空,并且在我创建对象时发现的调试器工具的帮助下,Id属性当然为0。 In my query I am filtering on student Id but at the time of executing the method to fill the Courses list in the constructor the Id of student will always be 0 becuase it's set at a later stage and the result will be no courses in the list. 在我的查询中,我正在过滤学生ID,但是在执行将方法填充到构造函数中的“课程”列表时,该学生的ID始终为0,因为它是在稍后阶段设置的,结果将是该列表中没有课程。

I'm wondering if I should put a check for an ICollection property after the other properties are set and if so execute a method on the object that in return executes the method that's now inside the constructor? 我想知道是否应该在设置其他属性后检查ICollection属性,是否在对象上执行一个方法,然后返回执行构造函数内部的方法?

I can't call any methods on TModel, else it would be as simple as finding if TModel has a collection property and call obj.FillCollection(); 我不能在TModel上调用任何方法,否则就像找到TModel是否具有collection属性并调用obj.FillCollection()一样简单。 after the Id property has been assigned in the GetEntitiesFromDb method. 在GetEntitiesFromDb方法中分配Id属性之后。

I was also thinking about recursion. 我也在考虑递归。 Again I'd have to find if obj has a collection property and then call GetEntitiesFromDB but it seems undoable because I also need to find out the type in between <> and I Can't send any customquery from the outside... 再次,我必须查找obj是否具有collection属性,然后调用GetEntitiesFromDB,但这似乎是不可撤消的,因为我还需要找出<>之间的类型,并且我无法从外部发送任何customquery ...

Maybe tackle it from a whole other perspective? 也许是从另一个角度解决呢?

I can really use some advice on how to tackle this problem. 我真的可以就如何解决这个问题使用一些建议。

The most straightforward way to approach this would be to have the collection property lazy load what it needs. 解决此问题的最直接方法是让collection属性延迟加载所需的内容。 I would additionally recommend that you use IEnumerable<T> instead of ICollection<T> because this represents a read-only view of what's currently in the database, nobody should be modifying it in any way. 我还建议您使用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);
    }
}

I'm only recommending this approach because you mentioned that this is just an academic exercise to help you learn about the tools available to you. 我只推荐这种方法,因为您提到这只是一个学术练习,可以帮助您了解可用的工具。 In an actual production environment this approach would very quickly become unwieldy and I would recommend using Entity Framework instead (which may be something else that you might want to learn about). 在实际的生产环境中,这种方法很快就会变得笨拙,我建议改用Entity Framework(这可能是您可能想了解的其他东西)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM