简体   繁体   English

属性属性的反射设置值

[英]Reflection set value of a properties property

I have 2 classes: 我有2节课:

public class CustomerViewModel {
  public SystemViewModel system { get;set; }
}

public class SystemViewModel {
  public bool isReadOnly { get; set; }
}

On the method controller action I have a custom filter attribute which executes some code and determines whether or the user has ReadOnly or Write access. 在方法控制器操作上,我具有一个自定义过滤器属性,该属性执行一些代码并确定用户是否具有ReadOnly或Write访问权限。 This attribute can be applied to multiple actions across multiple controllers. 该属性可以应用于多个控制器上的多个动作。

So far using reflection I can get access to the model using: 到目前为止,使用反射我可以使用以下方式访问模型:

var viewModel = filterContext.Controller.ViewData.Model;

I can not cast this model to CustomerViewModel because on a different action it might be something like SalaryViewModel. 我无法将此模型转换为CustomerViewModel,因为在其他操作上,它可能类似于SalaryViewModel。 What I do know is that any model that requires the readonly property will have SystemViewModel property. 我所知道的是,任何需要readonly属性的模型都将具有SystemViewModel属性。

From my custom filter I need a way to be able to change the value of readonly. 从我的自定义过滤器中,我需要一种能够更改readonly值的方法。

So far I have this: 到目前为止,我有这个:

public override void OnActionExecuted(ActionExecutedContext filterContext) {
    var viewModel = filterContext.Controller.ViewData.Model;

    var systemViewModelPropertyInfo = model.GetType()
        .GetProperties()
        .FirstOrDefault(p => p.PropertyType == typeof(SystemViewModel));

    if (systemViewModelPropertyInfo != null) {
        // Up to here, everything works, systemViewModelPropertyInfo is of
        // type PropertyInfo, and the systemViewModelPropertyInfo.PropertyType 
        // shows the SystemViewModel type
        // If we get here, the model has the system property
        // Here I need to try and set the IsReadOnly property to true/false;
        // This is where I need help please
    }
}

SOLVED Thanks to everyone who pitched in to help solve this. 已解决感谢所有参与解决此问题的人。 Special thanks to Julián Urbano for having the solution I was looking for. 特别感谢JuliánUrbano提供我一直在寻找的解决方案。 Here is my resulting code from within my filter: 这是我从过滤器中得到的代码:

public override void OnActionExecuted(ActionExecutedContext filterContext)
{
    try
    {
        var viewModel = filterContext.Controller.ViewData.Model;
        var systemViewModelPropertyInfoCount = viewModel.GetType().GetProperties().Count(p => p.PropertyType == typeof(SystemViewModel));

        if(systemViewModelPropertyInfoCount == 1)
        {
            var systemViewModelPropertyInfo = viewModel.GetType().GetProperties().First(p => p.PropertyType == typeof(SystemViewModel));
            if(systemViewModelPropertyInfo != null)
            {
                var systemViewModel = systemViewModelPropertyInfo.GetValue(viewModel, null) as SystemViewModel;

                if(systemViewModel != null)
                {
                    var admin = GetAdmin(filterContext.HttpContext.User.Identity.Name);
                    if(admin != null && _adminService.HasPermission(admin, _privilege, Access.Level.ReadWrite))
                        systemViewModel.ReadOnly = false;
                    else
                        systemViewModel.ReadOnly = true;
                }
            }
        } else if(systemViewModelPropertyInfoCount > 1)
        {
            throw new Exception("Only once instance of type SystemViewModel allowed");
        }
    }
    catch (Exception exception)
    {
        Log.Error(MethodBase.GetCurrentMethod(), exception);
        filterContext.Controller.TempData["ErrorMessage"] = string.Format("Technical error occurred");
        filterContext.Result = new RedirectResult("/Error/Index");
    }
    finally
    {
        base.OnActionExecuted(filterContext);
    }
}

I can not cast this model to CustomerViewModel because on a different action it might be something like SalaryViewModel. 我无法将此模型转换为CustomerViewModel,因为在其他操作上,它可能类似于SalaryViewModel。 What I do know is that any model that requires the readonly property will have SystemViewModel property. 我所知道的是,任何需要readonly属性的模型都将具有SystemViewModel属性。

option 1 选项1

Seems to me that the best option is to write an interface like: 在我看来,最好的选择是编写一个像这样的接口:

public interface IWithSystemViewModel {
    SystemViewModel System {get;}
}

and implement it from your classes, much like: 并从您的类中实现它,就像:

public class CustomerViewModel : IWithSystemViewModel{
    public SystemViewModel System { get;set; }
}
public class SalaryViewModel : IWithSystemViewModel{
    public SystemViewModel System { get;set; }
}

so you can cast it and access the isReadOnly property: 因此您可以投射它并访问isReadOnly属性:

IWithSystemViewModel viewModel = filterContext.Controller.ViewData.Model as IWithSystemViewModel;
if(viewModel!=null){
    viewModel.System.isReadOnly ...
}

option 2 选项2

If you want to stick to using reflection: 如果您想坚持使用反射:

var viewModel = filterContext.Controller.ViewData.Model;
SystemViewModel  theSystem = viewModel.GetType().GetProperty("system")
    .GetValue(viewModel, null) as SystemViewModel;
theSystem.isReadOnly ...

Tricky thing: in your code, you select the property whose type is SystemViewModel . 棘手的事情:在您的代码中,选择类型为SystemViewModel的属性。 But what if the object actually has several SystemViewModel properties that you don't know about? 但是,如果该对象实际上具有您不知道的几个SystemViewModel属性,该怎么办? Are you sure you're accessing the proper one? 您确定要访问正确的地址吗? You may force all of them to use the same name, but then again, that would be like using the interface in option 1 above. 您可以强制所有的用户使用相同的名称,但是再说一遍,就像使用上面选项1中的接口一样。

I'd definitely go with option 1. 我肯定会选择选项1。

var viewModel = new CustomerViewModel();

var systemViewModelPropertyInfo = viewModel.GetType()
    .GetProperties()
    .FirstOrDefault(p => p.PropertyType == typeof(SystemViewModel));

if (systemViewModelPropertyInfo != null) {
    var systemViewModelProperty = systemViewModelPropertyInfo.GetValue(viewModel, null) as SystemViewModel;

    // get the desired value of isReadOnly here...
    var isReadOnly = false;

    // here, systemViewModelProperty may be null if it has not been set.
    // You can decide what to do in that case. If you need a value to be
    // present, you'll have to do something like this...
    if (systemViewModelProperty == null) {
        systemViewModelPropertyInfo.SetValue(viewModel, new SystemViewModel { isReadOnly = isReadOnly }, null);
    }
    else {
        systemViewModelProperty.isReadOnly = isReadOnly;
    }
}

That said, this whole process would probably be easier if you implemented an interface... 也就是说,如果您实现了一个界面,则整个过程可能会更容易...

public interface IHaveSystemViewModel {
    SystemViewModel system { get; set; }
}

var model = viewModel as IHaveSystemViewModel;
if (model != null) {

    // again, you need to make sure you actually have a reference here...
    var system = model.system ?? (model.system = new SystemViewModel());
    system.isReadOnly = false; // or true
}

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

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