简体   繁体   中英

Want to make Func<UnknownType, bool> where UnknownType is only known at runtime

So, I'm experimenting with expressions atm. Got code below: Code works fine except for 1 thing: I need to replace ViewModel type with ForeignKeyProperty.PropertyType, which is only known at runtime, at the line of var condition = Expression.Lambda < Func < ViewModel, bool> >

Expected end result:

ForeignKeyProperty.SetValue(model, repository.GetList  <ForeignKeyProperty.PropertyType >().Single(x => x.Id == model.Id));

protected List < Action < IVenturaRepository, ViewModel>> SetForeignKeyProperties<ViewModel>() where ViewModel : BaseViewModel
        {
            var viewModelType = typeof(ViewModel);
            var foreignKeyProperties = viewModelType.GetProperties().Where(x => x.PropertyType.IsSubclassOf(typeof(BaseViewModel)));
            var actions = new List < Action < IVenturaRepository, ViewModel>>();
            var repositoryType = typeof(IVenturaRepository);
            foreach(var ForeignKeyProperty in foreignKeyProperties)
            {
                var foreignKeyIdProperty = viewModelType.GetProperties().SingleOrDefault(x => x.Name == ForeignKeyProperty.Name + "Id");
                //ForeignKeyProperty.SetValue(model, repository.GetList<ViewModel>().Single(x => x.Id == model.Id));
                var listMethod = repositoryType.GetMethods().SingleOrDefault(x => x.Name == "GetList").MakeGenericMethod(ForeignKeyProperty.PropertyType);
                //Expression.Call(singleMethod,);
                var repositoryVariable = Expression.Parameter(repositoryType, "repository");
                var paramViewModelType = Expression.Parameter(viewModelType, "model");
                var paramForeignEntityId = Expression.Property(paramViewModelType, "Id");
                var listMethodCall = Expression.Call(repositoryVariable, listMethod);
                var modelParameter = Expression.Parameter(ForeignKeyProperty.PropertyType, "x");
                var foreignKeyTypeConstant = Expression.Constant(ForeignKeyProperty.PropertyType);
                var condition =
                    Expression.Lambda < Func < ViewModel, bool>>(
                        Expression.Equal(
                            Expression.Property(paramViewModelType, foreignKeyIdProperty.Name),
                            Expression.Convert(Expression.Property(modelParameter, "Id"),foreignKeyIdProperty.PropertyType)
                        ),
                        modelParameter
                    );
                //var singleMethod = typeof(Enumerable).GetMethods().SingleOrDefault(x => x.Name.Equals("SingleOrDefault") && x.GetParameters().Count() ==2).MakeGenericMethod(viewModelType);
                //var singleMethod = typeof(IEnumerable<ViewModel>).GetMethods().SingleOrDefault(x => x.GetParameters().Count() > 0).MakeGenericMethod(viewModelType);
                //var singleLambda = Expression.Lambda(Expression.Property(modelParameter, "Id"), modelParameter);
                var singleMethodCall = Expression.Call(typeof(Enumerable), "SingleOrDefault", new[] { ForeignKeyProperty.PropertyType },listMethodCall, condition);
                //var singleMethodCall = Expression.Call(listMethodCall, singleMethod, condition);
                var setMethod = ForeignKeyProperty.GetSetMethod();
                var oParameter = Expression.Parameter(viewModelType, "obj");
                var vParameter = Expression.Parameter(typeof(ViewModel),"value");
                var method = Expression.Call(oParameter,setMethod, singleMethodCall);
                var expression = Expression.Lambda<Action<IVenturaRepository, ViewModel>>(method);
                actions.Add(expression.Compile());
            }
            return actions;
        }

Could someone point me in the right direction please?

use Object as type. then you can check type by getType() at runtime and after check cast to correct type. or use dynamic to avoid casting.

So, I did as you suggested and used object instead. It works now. Code below works in a specific controller that inherits from BaseController. public override ActionResult Edit(long id, DeliveryEditViewModel model) { model.SourceModel.DeliveryRound = Repository.DeliveryRounds.Single(x => x.Id == model.DeliveryRoundId); model.SourceModel.Sale = Repository.Sales.Single(x => x.Id == model.SaleId); return base.Edit(id, model); } public override ActionResult Edit(long id, DeliveryEditViewModel model) { model.SourceModel.DeliveryRound = Repository.DeliveryRounds.Single(x => x.Id == model.DeliveryRoundId); model.SourceModel.Sale = Repository.Sales.Single(x => x.Id == model.SaleId); return base.Edit(id, model); } Idea is that code below works in BaseController: public virtual ActionResult Edit(long id, EditModel model) { try { if (setForeignKeyActionList == null) setForeignKeyActionList = SetForeignKeyProperties(); setForeignKeyActionList.ForEach(action => action(Repository, model.SourceModel)); Repository.SaveItem(model); return RedirectToAction("Index"); } catch(Exception ex) { ModelState.AddModelError("Error", ex); return View(Repository.GetEditableItem(id)); } } public virtual ActionResult Edit(long id, EditModel model) { try { if (setForeignKeyActionList == null) setForeignKeyActionList = SetForeignKeyProperties(); setForeignKeyActionList.ForEach(action => action(Repository, model.SourceModel)); Repository.SaveItem(model); return RedirectToAction("Index"); } catch(Exception ex) { ModelState.AddModelError("Error", ex); return View(Repository.GetEditableItem(id)); } }

This is the revised code based on your suggestion to use object type protected List< Action< IVenturaRepository, ViewModel>> SetForeignKeyProperties() { var viewModelType = typeof(ViewModel); var foreignKeyProperties = viewModelType.GetProperties().Where(x => x.PropertyType.IsSubclassOf(typeof(BaseViewModel))); var actions = new List< Action< IVenturaRepository, ViewModel>>(); var repositoryType = typeof(IVenturaRepository); foreach (var ForeignKeyProperty in foreignKeyProperties) { var foreignKeyIdProperty = viewModelType.GetProperties().SingleOrDefault(x => x.Name == ForeignKeyProperty.Name + "Id"); //ForeignKeyProperty.SetValue(model, repository.GetList< OtherViewModel>().Single(x => x.Id == model.Id)); var listMethod = repositoryType.GetMethods().SingleOrDefault(x => x.Name == "GetList").MakeGenericMethod(ForeignKeyProperty.PropertyType); var repositoryVariable = Expression.Parameter(repositoryType, "repository"); var paramViewModelType = Expression.Parameter(viewModelType, "model"); var paramForeignEntityId = Expression.Property(paramViewModelType, "Id"); var listMethodCall = Expression.Call(repositoryVariable, listMethod); protected List< Action< IVenturaRepository, ViewModel>> SetForeignKeyProperties() { var viewModelType = typeof(ViewModel); var foreignKeyProperties = viewModelType.GetProperties().Where(x => x.PropertyType.IsSubclassOf(typeof(BaseViewModel))); var actions = new List< Action< IVenturaRepository, ViewModel>>(); var repositoryType = typeof(IVenturaRepository); foreach (var ForeignKeyProperty in foreignKeyProperties) { var foreignKeyIdProperty = viewModelType.GetProperties().SingleOrDefault(x => x.Name == ForeignKeyProperty.Name + "Id"); //ForeignKeyProperty.SetValue(model, repository.GetList< OtherViewModel>().Single(x => x.Id == model.Id)); var listMethod = repositoryType.GetMethods().SingleOrDefault(x => x.Name == "GetList").MakeGenericMethod(ForeignKeyProperty.PropertyType); var repositoryVariable = Expression.Parameter(repositoryType, "repository"); var paramViewModelType = Expression.Parameter(viewModelType, "model"); var paramForeignEntityId = Expression.Property(paramViewModelType, "Id"); var listMethodCall = Expression.Call(repositoryVariable, listMethod);

  var foreignKeyTypeConstant = Expression.Constant(ForeignKeyProperty.PropertyType); var objectType = Expression.Parameter(typeof(object), "model"); var modelParameter = Expression.Parameter(typeof(object), "x"); var expressionForeignKeyId = Expression.Property(paramViewModelType, foreignKeyIdProperty.Name); var expressionForeignEntityId = Expression.Convert(Expression.Property(Expression.Convert(modelParameter, ForeignKeyProperty.PropertyType), "Id"), foreignKeyIdProperty.PropertyType); var condition = Expression.Lambda<Func<object, bool>>( Expression.Equal( expressionForeignKeyId, expressionForeignEntityId ), modelParameter ); var singleMethodCall = Expression.Call(typeof(Enumerable), "SingleOrDefault", new[] { ForeignKeyProperty.PropertyType }, listMethodCall, condition); //var singleMethodCall = Expression.Call(listMethodCall, singleMethod, condition); var setMethod = ForeignKeyProperty.GetSetMethod(); //var oParameter = Expression.Parameter(viewModelType, "obj"); var vParameter = Expression.Parameter(typeof(ViewModel), "value"); var method = Expression.Call(paramViewModelType, setMethod, singleMethodCall); var lamdaParameterExpressions = new[] { repositoryVariable, paramViewModelType }; var expression = Expression.Lambda<Action<IVenturaRepository, ViewModel>>(method, lamdaParameterExpressions); actions.Add(expression.Compile()); } return actions; } 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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