使用反射重构代码

本文关键字:代码 重构 反射 | 更新日期: 2023-09-27 18:32:01

我正在努力重构这个(工作)代码:

MyDbContext db = new MyDbContext();
 List<SelectListItem> selectedItems = new List<SelectListItem>();
 if (type == null) return selectedItems;
if (type == typeof(class1))
             selectedItems = db.class1.ToList().Select(ii => new SelectListItem { Text = ii.Name, Value = ii.Id.ToString() }).OrderBy(si => si.Text).ToList();
if (type == typeof(class2))
             selectedItems = db.class2.ToList().Select(ii => new SelectListItem { Text = ii.Name, Value = ii.Id.ToString() }).ToList();
if (type == typeof(class3))
            selectedItems = db.class3.ToList().Select(ii => new SelectListItem { Text = ii.Name, Value = ii.Id.ToString() }).ToList();
if (type == typeof(class4))
            selectedItems = db.class4.ToList().Select(ii => new SelectListItem { Text = ii.Name, Value = ii.Id.ToString() }).ToList();

此代码位于 ASP.NET MVC 控制器内。类 1 到类 4 是模型类。

SelectListItem 只是一个 ModelView 类,我用来从 Class1、2、3 或 4 对象中获取 ID 和 Name。我认为不值得发布其代码。因此,下面的代码只是提取 Class1 或 2 或 3 或 4 的所有出现次数,并将它们转换为将传递给视图(用于 DropDownBox)的选项。当然,我只知道运行时的确切模型类型(Class1...或 4)。

我将实体框架与这样的 DbContext 一起使用:

  public partial class MyDbContext: DbContext
    {
...
        public virtual DbSet<Class1> Class1{ get; set; }
        public virtual DbSet<Class2> Class2{ get; set; }
        public virtual DbSet<Class3> Class3{ get; set; }
        public virtual DbSet<Class4> Class4{ get; set; }
...
    }

我很确定我最终可以得到一个带有反射的干净代码,而不是我写的这个可怕的东西。但是我没有设法得到任何干净的编译。

使用反射重构代码

我会按如下方式重构它:

MyDbContext db = new MyDbContext();
List<SelectListItem> selectedItems = new List<SelectListItem>();
if (type == null)
{
    return selectedItems;
}
var v1 = db.GetType().GetProperty(type.Name).GetValue(db, null);
var v2 = v1.ToList().Select(ii => new SelectListItem { Text = ii.Name, Value = ii.Id.ToString() });
if (type.Name == "class1")
{
    v2 = v2.OrderBy(si => si.Text);
}
v3 = v2.ToList();

真诚地,由于var,我怀疑这是否有效.您可能必须确定实际的类并定义多个变量。这样编译器就知道v1有一个ToList()方法。没有适当的代码,就不可能知道要使用哪些类,但这只是为了给你一个想法。它显然需要修复。

我喜欢你的问题,从其他角度来看,比如构建动态查询。因此,我尝试了一种不同的方法来使用表达式树构建动态查询。这将是一个扩展方法,它将与所有IQueryable<>类型一起出现。

public static class QueryableExtension
{
    public static IEnumerable<SelectListItem> GetTable<T>(this IQueryable<T> source)
    {
        KeyValuePair<PropertyInfo, PropertyInfo> sourceDestPropMap1
                                        = new KeyValuePair<PropertyInfo, PropertyInfo>(
                                            typeof(SelectListItem).GetProperty("Text"), // Text prop of selected item
                                                 typeof(T).GetProperty("Name") // Name prop of T class
                                                 ); 
        KeyValuePair<PropertyInfo, PropertyInfo> sourceDestPropMap2
                                        = new KeyValuePair<PropertyInfo, PropertyInfo>(
                                                 typeof(SelectListItem).GetProperty("Value"), // Value prop of Selected Item
                                                 typeof(T).GetProperty("Id")); // Id prop from T class
        var name = "item";
        // -> declare Lambda parameter 'item' of type T i.e. Class1, Class2 etc.
        var paramExpr = Expression.Parameter(typeof(T), name);
        // -> Text = item.Id
        var propVal = Expression.Property(paramExpr, sourceDestPropMap2.Value);
        // -> Text = item.Id.ToString()
        var expression = Expression.Call(propVal, typeof(object).GetMethod("ToString"));
        // -> item => new SelectedListItem(Text = item.Name, Value = item.Id.Tostring());
        var projection = Expression.Lambda<Func<T, SelectListItem>>(
                              Expression.MemberInit(
                                Expression.New(typeof(SelectListItem)),
                                new[] {
                                   Expression.Bind(sourceDestPropMap1.Key, Expression.Property(paramExpr, sourceDestPropMap1.Value)),
                                   Expression.Bind(sourceDestPropMap2.Key, expression)
                                }
                              ), paramExpr);
   // -> Class1.Select(item => new SelectedListItem(Text = item.Name, Value = item.Id.Tostring()).ToList()
        return source.Select(projection).ToList();
    }
}

这是我能够在变量中生成的 Linq projection所以这应该能够与 select 一起使用。

item =

> 新的 SelectListItem() {Text = item.名称、值 = 项。Id.ToString()}

现在,您所要做的就是在运行时对任何实体对象调用扩展方法。

用法 -

IEnumerable<SelectedListItem> list = db.Class1.GetTable();

尽管它有更多的优化区域,并且可以扩展到更通用,而不是将SelectedItem作为结果类型进行处理。

我使用通用性和反射进行了第一次改进。

我为我的MyDbContext类创建了一个扩展方法:

public static List<SelectListItem> getTable<T>(this MyDbContext db)
{
    List<SelectListItem> ImproItems = new List<SelectListItem>();
    if (typeof(T) == null) return ImproItems;

    List<T> Ts = ((IEnumerable<T>) db.GetType().GetProperty(typeof(T).Name).GetValue(db, null)).ToList(); //.Select(ii => new SelectListItem( (T)ii)); //does not work directly
    foreach(dynamic t in Ts)
    {
        ImproItems.Add(new SelectListItem(t));
    }
}

然后我需要做的就是让 SelectListItem 的 4 个构造函数接受 Class1、Class2、Class3 和 Class4 参数。

编译器不直接接受

List<dynamic> Ts = ((IEnumerable<T>) db.GetType().GetProperty(typeof(T).Name).GetValue(db, null)).ToList(); //.Select(ii => new ImproItemViewModel( (T)ii));
foreach(dynamic t in Ts)
{
    ImproItems.Add(new ImproItemViewModel(t));
}

它不能转换为列表。

我很确定大师们仍然会设法摆脱 foreach,但至少我摆脱了 4 if() !