I have a view model that have a lot of properties, and I created a custom attribute for send the data to hubspot, because hubspot need a specific nomenclature, then I need to create a method that have some king of iterator, that for every property that contain my custom attribute he put a specific output, here is the code:
public class CreateTrialUserHubspotViewModel {
[HubspotAttribute("firstname")]
[Display(Name = "First Name")]
[StringLength(50)]
[Required]
public string FirstName { get; set; }
[HubspotAttribute("lastname")]
[Display(Name = "Last Name")]
[StringLength(50)]
[Required]
public string LastName { get; set; }
[HubspotAttribute("jobtitle")]
[Display(Name = "Job title")]
[StringLength(50)]
[Required]
public string JobTitle { get; set; }
}
Now this is my custom attribute
[AttributeUsage(AttributeTargets.All)]
public class HubspotAttribute : System.Attribute {
public readonly string hubspotValue;
public HubspotAttribute(string value)
{
this.hubspotValue = value;
}
}
And then I need to create a method that take a viewmodel object and create my output, I need some suggest about how to do that, will be something like this:
private static RowValidation ValidateRowWithManifest<T>(CreateTrialUserHubspotViewModel trialUser) {
RowValidation validation = new RowValidation();
FieldInfo[] fields = typeof(T).GetPropertiesOfSomeWay;
foreach (DataType field in fields) {
output+=whatINeed
}
return validation;
}
}
The needed output will be like: [firstname:"pepe", lastname="perez", jobtitle"none"]. just calling that method will return all the data I need.
public string GetString<T>(T #object)
{
var output = new StringBuilder();
var type = typeof(T);
var properties = type.GetProperties();
foreach (var property in properties)
{
var attributes = property.GetCustomAttributes(typeof(HubspotAttribute), true);
if (attributes.Length == 0)
continue;
var name = ((HubspotAttribute)attributes[0]).hubspotValue;
var value = property.GetValue(#object) ?? "none";
output.AppendFormat("{0}:\"{1}\",", name, value);
}
var fields = output.ToString().TrimEnd(',');
return string.Format("[{0}]", fields);
}
If you are looking for something that will concatenate properties into a string that looks like a JSON string (and that would be a better way to handle it), you can use something like the following:
private static string CreateOutput(CreateTrialUserHubspotViewModel trialUser)
{
var properties = trialUser.GetType().GetProperties().Where(x => Attribute.IsDefined(x, typeof(HubspotAttribute))).ToList();
var values = properties.Select(x =>
{
var att = x.GetCustomAttribute(typeof(HubspotAttribute));
var key = ((HubspotAttribute)att).hubspotValue;
var val = x.GetValue(trialUser);
return $"{key}:{val}";
});
var sb = new StringBuilder();
values.ToList().ForEach(v =>
{
sb.Append(v);
if (values.Last() != v) sb.Append(',');
});
return sb.ToString();
}
Related
I've written a function that generates an HTML email and fills it with information from a database.
I've been trying to iterate through a list, but can't seem to get the function to be generic and run throught the Items list.
Here is the Email Generator function. It is fairly generic, so that it can be used in a wide variety of email templates.
public interface IMailObject
{
string Subject { get; }
}
public interface IEmailGenerator
{
MailMessage generateEmail(IMailObject mailObject, string htmlTemplate, string textTemplate);
}
public class EmailGenerator : IEmailGenerator, IRegisterInIoC
{
private string mergeTemplate(string template, object obj)
{
Regex operationParser = new Regex(#"\$(?:(?<operation>[\w\-\,\.]+)\x20)(?<value>[\w\-\,\.]+)\$", RegexOptions.Compiled);
Regex valueParser = new Regex(#"\$(?<value>[\w\-\,\.]+)\$", RegexOptions.Compiled);
var operationMatches = operationParser.Matches(template).Cast<Match>().Reverse().ToList();
foreach (var match in operationMatches)
{
string operation = match.Groups["operation"].Value;
string value = match.Groups["value"].Value;
var propertyInfo = obj.GetType().GetProperty(value);
if (propertyInfo == null)
throw new TillitException(String.Format("Could not find '{0}' in object of type '{1}'.", value, obj));
object dataValue = propertyInfo.GetValue(obj, null);
if (operation == "endforeach")
{
string foreachToken = "$foreach " + value + "$";
var startIndex = template.LastIndexOf(foreachToken, match.Index);
var templateBlock = template.Substring(startIndex + foreachToken.Length, match.Index - startIndex - foreachToken.Length);
var items = (IEnumerable) value;
string blockResult = "";
foreach (object item in items)
{
blockResult += mergeTemplate(templateBlock, item);
}
template = template.Remove(startIndex, match.Index - startIndex).Insert(startIndex, blockResult);
}
}
var valueMatches = valueParser.Matches(template).Cast<Match>().Reverse().ToList();
foreach (var match in valueMatches)
{
string value = match.Groups["value"].Value;
var propertyInfo = obj.GetType().GetProperty(value);
if (propertyInfo == null)
throw new Exception(String.Format("Could not find '{0}' in object of type '{1}'.", value, obj));
object dataValue = propertyInfo.GetValue(obj, null);
template = template.Remove(match.Index, match.Length).Insert(match.Index, dataValue.ToString());
}
return template;
}
public MailMessage generateEmail(IMailObject mailObject, string htmlTemplate, string textTemplate)
{
var mailMessage = new MailMessage();
mailMessage.IsBodyHtml = true;
mailMessage.Subject = mailObject.Subject;
mailMessage.BodyEncoding = Encoding.UTF8;
// Create the Plain Text version of the email
mailMessage.Body = this.mergeTemplate(textTemplate, mailObject);
// Create the HTML version of the email
ContentType mimeType = new System.Net.Mime.ContentType("text/html");
AlternateView alternate = AlternateView.CreateAlternateViewFromString(this.mergeTemplate(htmlTemplate, mailObject), mimeType);
mailMessage.AlternateViews.Add(alternate);
return mailMessage;
}
}
And here is a case of the message data:
public class MessageData : IMailObject
{
public string Property1 { get; private set; }
public string Property2 { get; private set; }
public string Property3 { get; private set; }
public string Property4 { get; private set; }
public string Property5 { get; private set; }
public string Property6 { get; private set; }
public string Subject
{
get { return this.Property1 + DateTime.Now.ToShortDateString(); }
}
public List<MessageItemData> Items { get; private set; }
public MessageData(string property1, string property2, string property3, DateTime property4, string property7, string property8, DateTime property9, DateTime property10, int property11, double property12, string property5, string property6)
{
this.Property1 = property1;
this.Property2 = property2;
this.Property3 = property3;
this.Property4 = property4.ToShortDateString();
this.Property5 = property5;
this.Property6 = property6;
this.Items = new List<MessageItemData>();
this.Items.Add(new MessageItemData(property7, property8, property9, property10, property11, property12));
}
}
public class MessageItemData
{
public string Property7 { get; private set; }
public string Property8 { get; private set; }
public string Property9 { get; private set; }
public string Property10 { get; private set; }
public int Property11 { get; private set; }
public double Property12 { get; private set; }
public MessageItemData( string property7, string property8, DateTime property9, DateTime property10, int property11, double property12)
{
this.Property7 = property7;
this.Property8 = property8;
this.Property9 = property9.ToShortDateString();
this.Property10 = property10.ToShortDateString();
this.Property11 = property11;
this.Property12 = property12;
}
}
The function works when there is only one set of elements being used. If we use the MessageData class as an example. All the information will be replaced correctly, but I'm wanting to improve the email generator function, because this particular MessageData class has a list of objects, where Property7 to Property12 will be replaced multiple times.
The function is started at: if (operation == "endforeach"), but I need some help to improve it so that it runs through the: var items = (IEnumerable) value;, so that the function returns TemplateHeader + TemplateItem + TemplateItem + ...however many TemplateItems there are + TemplateFooter. It currently will only return TemplateHeader + TemplateItem + TemplateFooter, even though there are multiple items in the list, it will only return the first item.
In this case I'm assuming I need to get the List Items. I've been trying to implement it into the EmailGenerator just below:
var items = (IEnumerable) value;
with the code:
if (propertyInfo.PropertyType == typeof(List<>))
{
foreach (var item in items)
{
Console.WriteLine(item);
}
}
Console.WriteLine is just for testing purposes, to see if I get any values in Debug(which I'm currently getting null)
Assuming the type of the Items property is the same across all instances you may want to try using IsInstanceOfType instead. And then get the value of the property via the GetValue method. Reflection can be confusing at times ;) but hopefully, it is what you are looking for.
var data = new MessageData("a", "b", "c", DateTime.Now, "d", "e", DateTime.Now, DateTime.Now, 1, 2, "f", "g");
data.Items.Add(new MessageItemData("7", "8", DateTime.Now, DateTime.Now, 11, 12));
data.Items.Add(new MessageItemData("71", "81", DateTime.Now, DateTime.Now, 111, 112));
var dataType = data.GetType();
foreach (var propertyInfo in dataType.GetProperties())
{
if (propertyInfo.PropertyType.IsInstanceOfType(data.Items))
{
foreach (var item in (List<MessageItemData>)propertyInfo.GetValue(data))
{
Console.WriteLine(item);
}
}
}
I have a list with Strings of Display names of Model:
public class TVSystemViewData : BaseViewData
{
[Display(Name = "AccountId", Description = "")]
public String AccountId { get; set; }
[Display(Name = "AllocatedManagedMemory", Description = "")]
public String AllocatedManagedMemory { get; set; }
[Display(Name = "AllocatedPhysicalMemory", Description = "")]
public String AllocatedPhysicalMemory { get; set; }
[Display(Name = "AudioMute", Description = "")]
public String AudioMute { get; set; }
}
I need to set the properties with a foreach loop to add the values to my Model:
This is how get values from POST the application
var model.AccountId = shell.getParameter("AccountId")
var model.AllocatedManagedMemory = shell.getParameter("AllocatedManagedMemory");
The shell.GetParameter get the value from a POST.
this is how i want:
I have a a Method to get all Display attr
public List<String> GetTVSystemProperties()
{
return typeof(TVSystemViewData)
.GetProperties()
.SelectMany(x => x.GetCustomAttributes(typeof(DisplayAttribute), true) //select many because can have multiple attributes
.Select(e => ((DisplayAttribute)e))) //change type from generic attribute to DisplayAttribute
.Where(x => x != null).Select(x => x.Name) //select not null and take only name
.ToList();
}
My collection is a list of Strings
ex: collection {"AccountId","AllocatedManagedMemory"...}
My model is TVSystemViewData
foreach (item in collection)
{
if(item == modelProperty name){
// i don know how
model.property = shell.getParameter(item)
}
}
[UPDATED]
I am using this:
foreach (var property in UtilsHandler.getConfigAsList("sysDataSource"))
{
//Set Values to Model
try
{
model.GetType().GetProperty(property).SetValue(model, shell.getParameter(property), null);
}
catch (Exception)
{
Have issue with data types
}
}
I have issues with data types.
But i use one foreach loop.
Still looking for a best method
you need to make the class inherit from IEnumerator, or add a GetEnumerator method yourself.
var model = new TVSystemViewData();
foreach(var item in model)
{
item.AccountId = shell.getParameter("AccountId");
//item.AllocatedManagedMemory ...
}
Please refer to this post for more information : How to make the class as an IEnumerable in C#?
Check this article out: https://support.microsoft.com/en-us/help/322022/how-to-make-a-visual-c-class-usable-in-a-foreach-statement
//EDIT. Forgot this part :
List<TVSystemViewData> model;
[Display(Name = "AccountId", Description = "")]
public String AccountId { get; set; }
[Display(Name = "AllocatedManagedMemory", Description = "")]
public String AllocatedManagedMemory { get; set; }
[Display(Name = "AllocatedPhysicalMemory", Description = "")]
public String AllocatedPhysicalMemory { get; set; }
[Display(Name = "AudioMute", Description = "")]
public String AudioMute { get; set; }
public IEnumerator<TVSystemViewData> GetEnumerator()
{
foreach (var item in model)
{
yield return item;
}
}
EDIT According to your update question: I don't know if this is the way to go but it should work.
var model = new TVSystemViewData();
PropertyInfo[] properties = typeof(TVSystemViewData).GetProperties();
List<string> items = new List<string> { "AccountId", "AllocatedManagedMemory" }; //your collection of strings
foreach (var item in items)
{
foreach (var property in properties)
{
if (item == property.Name)
{
property.SetValue(model, shell.getParameter(item));
}
}
}
I am trying to pass object as a parameter to String.Format but it's not working throwing zero-based index exception as it's not getting object property to allocate.
How do I pass class object to String.Format?
class TestPrimaryObject
{
public string userId { get; set; }
public string param2 { get; set; }
public string param3 { get; set; }
public string param4 { get; set; }
}
I am passing object of above class to String.Format. Below are the values of object property:
TestPrimaryObject objTest = new TestPrimaryObject();
objTest.userId = "test1";
objTest.param2 = "test2";
objTest.param3 = "test3";
objTest.param4 = "test4";
string formatedString = string.Format("http://example.com?param3={0}&userId={1}", objTest);
Parameter might vary according to URL
How do I achieve this?
Try this:
var objTest = new TestPrimaryObject {
param1 = "test1",
param2 = "test2",
param3 = "test3",
param4 = "test4"
};
var path = "http://example.com?param2={0}¶m4={1}";
//HttpUtility from System.Web;
var keys = HttpUtility.ParseQueryString(new Uri(path).Query).AllKeys
.Select(x => objTest.GetType().GetProperty(x).GetValue(objTest)).ToArray();
var formatedString = string.Format(path, keys);
//output - http://example.com?param2=test2¶m4=test4
The thing you want to achieve is not possible. I bet that you would like to get the behaviour, such that {0} and {1} would get replaced by param1 and param2 of an object. First of all, if this were possible, how could compiler know which properties you wanted to pass? Secondly, what if you passed 2 objects with 3 placeholders?
class Object1 {
public int Property1 { get; set; }
public int Property2 { get; set; }
}
Now imagine such scenario:
string.Format("{0} {1} {2}", new Object1(), new Object1());
Should the compiler take 2 properties of first object and one from the second or the other way round?
You have to explicitly pass properties.
string formatedString = string.Format("http://example.com?var1={0}&var2={1}", objTest.param1, objTest.param2);
Why don't you directly assign the values to url
objTest.param1 = "test1";
objTest.param2 = "test2";
objTest.param3 = "test3";
objTest.param4 = "test4";
string sURL = "http://example.com?var1="+objTest.param1+"&var2="+objTest.param2
//string formatedString = string.Format("http://example.com?var1={0}&var2={1}", type1);
the error you're getting is because you want to show 2 parameters {0} {1} and you provide just 1 argument.
If you want the output to display the properties : I would recommend to override the ToString() function of the class. and work with a strategy pattern to change the behaviour accordingly.
Note that this is not an elegant solution.
void Main()
{
var test= new TestPrimaryObject(){ param1="param1", param2="param2", param3="Param3", param4="param4"};
Console.WriteLine(test);
test.Formatter = new Param3And4();
Console.WriteLine(test);
test.Formatter = new Param1And2();
Console.WriteLine(test);
}
public class TestPrimaryObject
{
public TestPrimaryObject(){
Formatter= new Param1And2(); // set default here
}
public string param1 { get; set; }
public string param2 { get; set; }
public string param3 { get; set; }
public string param4 { get; set; }
public IStringOverride<TestPrimaryObject> Formatter {get;set;}
public override string ToString(){
return Formatter.Format(this);
}
}
public interface IStringOverride<T>{
string Format(T arg);
}
public class Param1And2 : IStringOverride<TestPrimaryObject>{
public string Format(TestPrimaryObject arg){
return string.Format("http://example.com?var1={0}&var2={1}", arg.param1, arg.param2);
}
}
public class Param3And4 : IStringOverride<TestPrimaryObject>{
public string Format(TestPrimaryObject arg){
return string.Format("http://example.com?var1={0}&var2={1}", arg.param3, arg.param4);
}
}
You can't achieve what you are trying to do with just a single call to String.Format. Since the number of parameters will vary, you need to conditionally be able to generate your url with an arbitrary number of parameters. You can create a method to do so:
publc string FormatUrl(string url, TestPrimaryObject objTest)
{
StringBuilder sb = new StringBuilder();
if (!String.IsNullOrEmpty(objTest.userId))
{
sb.Append($"userId={objTest.userId}");
}
if (!String.IsNullOrEmpty(objTest.param2))
{
if (sb.Length > 0)
sb.Append("&");
sb.Append($"param2={objTest.param2}");
}
if (!String.IsNullOrEmpty(objTest.param3))
{
if (sb.Length > 0)
sb.Append("&");
sb.Append($"param3={objTest.param3}");
}
if (!String.IsNullOrEmpty(objTest.param4))
{
if (sb.Length > 0)
sb.Append("&");
sb.Append($"param4={objTest.param4}");
}
return $"{url}?{sb.ToString()}";
}
And call it like so:
TestPrimaryObject objTest = new TestPrimaryObject();
objTest.userId = "test1";
objTest.param2 = "test2";
objTest.param3 = "test3";
objTest.param4 = "test4";
string url = FormatUrl("http://www.example.com/", objTest);
It will return:
http://www.example.com/?userId=test1¶m2=test2¶m3=test3¶m4=test4
Currently, am adding the properties and values to the object manually like this example and sending to Dapper.SimpleCRUD to fetch data from Dapper Orm. This is the desired output I would like to achieve.
object whereCriteria = null;
whereCriteria = new
{
CountryId = 2,
CountryName = "Anywhere on Earth",
CountryCode = "AOE",
IsActive = true
};
The following class should build the object in the above mentioned format and return the ready-made object.
public static class WhereClauseBuilder
{
public static object BuildWhereClause(object model)
{
object whereObject = null;
var properties = GetProperties(model);
foreach (var property in properties)
{
var value = GetValue(property, model);
//Want to whereObject according to the property and value. Need help in this part!!!
}
return whereObject;
}
private static object GetValue(PropertyInfo property, object model)
{
return property.GetValue(model);
}
private static IEnumerable<PropertyInfo> GetProperties(object model)
{
return model.GetType().GetProperties();
}
}
This function WhereClauseBuilder.BuildWhereClause(object model) should return the object in expected format (mentiond above). Here is the implementation of how I would like to use.
public sealed class CountryModel
{
public int CountryId { get; set; }
public string CountryName { get; set; }
public string CountryCode { get; set; }
public bool IsActive { get; set; }
}
public class WhereClauseClass
{
public WhereClauseClass()
{
var model = new CountryModel()
{
CountryCode = "AOE",
CountryId = 2,
CountryName = "Anywhere on Earth",
IsActive = true
};
//Currently, won't return the correct object because the implementation is missing.
var whereClauseObject = WhereClauseBuilder.BuildWhereClause(model);
}
}
Maybe something like that:
private const string CodeTemplate = #"
namespace XXXX
{
public class Surrogate
{
##code##
}
}";
public static Type CreateSurrogate(IEnumerable<PropertyInfo> properties)
{
var compiler = new CSharpCodeProvider();
var compilerParameters = new CompilerParameters { GenerateInMemory = true };
foreach (var item in AppDomain.CurrentDomain.GetAssemblies().Where(x => !x.IsDynamic))
{
compilerParameters.ReferencedAssemblies.Add(item.Location);
}
var propertiesCode =
string.join("\n\n", from pi in properties
select "public " + pi.PropertyType.Name + " " + pi.Name + " { get; set; }");
var source = CodeTemplate.Replace("##code##", propertiesCode);
var compilerResult = compiler.CompileAssemblyFromSource(compilerParameters, source);
if (compilerResult.Errors.HasErrors)
{
throw new InvalidOperationException(string.Format("Surrogate compilation error: {0}", string.Join("\n", compilerResult.Errors.Cast<CompilerError>())));
}
return compilerResult.CompiledAssembly.GetTypes().First(x => x.Name == "Surrogate");
}
And now use it:
public static object BuildWhereClause(object model)
{
var properties = GetProperties(model);
var surrogateType = CreateSurrogate(properties);
var result = Activator.CreateInstance(surrogateType);
foreach (var property in properties)
{
var value = GetValue(property, model);
var targetProperty = surrogateType.GetProperty(property.Name);
targetProperty.SetValue(result, value, null);
}
return result;
}
I didn't compile that. It's only written here. Maybe there are some errors. :-)
EDIT:
To use ExpandoObject you can try this:
public static object BuildWhereClause(object model)
{
var properties = GetProperties(model);
var result = (IDictionary<string, object>)new ExpandoObject();
foreach (var property in properties)
{
var value = GetValue(property, model);
result.Add(property.Name, value);
}
return result;
}
But I don't know whether this will work for you.
I want to create an attribute to use with my viewmodel. I want to display different textstrings depending on a third value.
I would like to do something like this...
[DisplayIf("IsPropertyValid", true, Name="value 1")]
[DisplayIf("IsPropertyValid", false, Name="value 2")]
public string MyProperty { get; set; }
public bool IsPropertyValid { get; set; }
Depending on whether my value IsPropertyValid is true or not I want to show one or the other. Ie. When property IspPropertyValid equals true "value 1" will be the displaytext and if not it will be "value 2".
Is this possible with ASPNET.MVC attributes? Or even better... a combinated one like....
[DisplayIf("IsPropertyValid", new {"value 1", "value 2"})].
public string MyProperty { get; set; }
public bool IsPropertyValid { get; set; }
Then the attribute checks the value of IsPropertyValid and makes sure the value displayed is "value 1" or "value 2".
Here's an example of how to go about this.
What we'll do is create a simple class called Person and display some basic information about them.
A Person has two properties
Name
IsActive
The IsActive property is a bool value and will be the property used to determine what the user's name is displayed as.
Ultimately what we'll do is apply a new attribute called DisplayIf to the Name property. It looks like this:
[DisplayIf("IsActive", "This value is true.", "This value is false.")]
First, let's create our model. Create a class called Person and put it into a Models folder.
Models/Person.cs
public class Person
{
[DisplayIf("IsActive", "This value is true.", "This value is false.")]
public string Name { get; set; }
public bool IsActive { get; set; }
}
Create a folder called Attributes and then put the following class in it:
Attributes/DisplayIfAttribute.cs
public class DisplayIfAttribute : Attribute
{
private string _propertyName;
private string _trueValue;
private string _falseValue;
public string PropertyName
{
get { return _propertyName; }
}
public string TrueValue
{
get { return _trueValue; }
}
public string FalseValue
{
get { return _falseValue; }
}
public DisplayIfAttribute(string propertyName, string trueValue, string falseValue)
{
_propertyName = propertyName;
_trueValue = trueValue;
_falseValue = falseValue;
}
}
Let's create a simple controller and action. We'll use the common /Home/Index.
Controllers/HomeController.cs
public class HomeController : Controller
{
public ActionResult Index()
{
HomeIndexViewModel viewModel = new HomeIndexViewModel();
Person male = new Person() { Name = "Bob Smith", IsActive = true };
Person female = new Person() { Name = "Generic Jane", IsActive = false };
Person[] persons = {male, female};
viewModel.Persons = persons;
return View(viewModel);
}
}
Create a new folder called ViewModels and create a HomeViewModels.cs class.
ViewModels/HomeViewModels.cs
public class HomeIndexViewModel
{
public IEnumerable<Person> Persons { get; set; }
}
Our Index view is very simple.
Views/Home/Index.cshtml
#model HomeIndexViewModel
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<div>
#Html.DisplayForModel()
</div>
DisplayForModel will work when you create this display template:
Views/Home/DisplayTemplates/HomeIndexViewModel.cshtml
#model HomeIndexViewModel
#Html.DisplayFor(m => m.Persons)
DisplayFor -> Persons will work when you create this display template:
Views/Shared/DisplayTemplates/Person.cshtml
#model Person
#foreach (var prop in ViewData.ModelMetadata.Properties)
{
if (prop.HasDisplayIfAttribute())
{
<p>#Html.DisplayIfFor(x => prop)</p>
}
else
{
<p>#Html.DisplayFor(x => prop.Model)</p>
}
}
But what are these methods in this display template? Create a new folder called Extensions and add the following classes:
Extensions/ModelMetaDataExtensions.cs
public static class ModelMetaDataExtensions
{
public static bool HasDisplayIfAttribute(this ModelMetadata data)
{
var containerType = data.ContainerType;
var containerProperties = containerType.GetProperties();
var thisProperty = containerProperties.SingleOrDefault(x => x.Name == data.PropertyName);
var propertyAttributes = thisProperty.GetCustomAttributes(false);
var displayIfAttribute = propertyAttributes.FirstOrDefault(x => x is DisplayIfAttribute);
return displayIfAttribute != null;
}
}
Extensions/HtmlHelperExtensions.cs
public static class HtmlHelperExtensions
{
public static IHtmlString DisplayIfFor<TModel, TProperty>
(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
where TProperty : ModelMetadata
{
string returnValue = string.Empty;
var modelMetaData = expression.Compile().Invoke(helper.ViewData.Model);
var containerType = typeof(TModel);
var containerProperties = containerType.GetProperties();
var propertyInfo = containerProperties
.SingleOrDefault(x => x.Name == modelMetaData.PropertyName);
var attribute = propertyInfo.GetCustomAttributes(false)
.SingleOrDefault(x => x is DisplayIfAttribute) as DisplayIfAttribute;
var conditionalTarget = attribute.PropertyName;
var conditionalTargetValue = (bool)containerType
.GetProperty(conditionalTarget).GetValue(helper.ViewData.Model);
if (conditionalTargetValue)
{
returnValue = attribute.TrueValue;
}
else
{
returnValue = attribute.FalseValue;
}
return MvcHtmlString.Create(returnValue);
}
}
The final output: