使用反射的重构代码

时间:2016-01-25 21:36:12

标签: c# asp.net-mvc reflection

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

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控制器中。 Class1到Class4是Model类。

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

我将Entity Framework与这样的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; }
...
    }

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

3 个答案:

答案 0 :(得分:0)

我会按如下方式重构:

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()方法。如果没有正确的代码,就不可能知道要使用哪些类,但这只是为了给你一个想法。这显然需要修复。

答案 1 :(得分:0)

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

我制作了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));
            }

}

然后我需要做的就是让4个SelectListItem构造函数接受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()!

答案 2 :(得分:0)

我喜欢你的问题与其他观点,如建立动态查询。所以我尝试了一种不同的方法来使用表达式树构建动态查询。这将是一种扩展方法,它将与所有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();
    }
}

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

  

item =&gt; new SelectListItem(){Text = item.Name,Value = item.Id.ToString()}

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

用法 -

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

虽然它有更多的优化区域,可以扩展为更通用,而不是将SelectedItem作为结果类型处理。