I used code from this post to implement feature toggles in an application and it is working great. However, now instead of my features being a straight bool on/off, each feature has two bool values: one for the general setting and one for the setting which is only applied to a subset of users. I've been trying to update the code to handle this and I'm finding my basic understanding of reflection and accessors is coming up short.
I'm using the code below to test in LINQPad - basically, compared to the post mentioned above, all I've done is tried to change TestFeatureToggle from a bool, to type MultiToggle but I'm struggling to Set or Get the value of the bool properties of the MultiToggle object. I get an exception at
return (bool)multiToggleProperty.GetValue(null,null);
"Non-static method requires a target". Which makes sense because the properties on MultiToggle are non-static. However, I don't have an instance of those objects and I don't understand how I would get one from a static class.
Any help would be really appreciated. My gut says it might not be possible!
void Main()
{
var exampleFeatureToggles = new List<FeatureToggle>
{
new FeatureToggle {Description = "Test", Id = 1, IsActive = true, Name = "TestFeatureToggle"}
};
FeatureToggles.SetFeatureToggles(exampleFeatureToggles);
Console.WriteLine(FeatureToggles.GetFeatureToggleSetting("TestFeatureToggle"));
}
public class FeatureToggle
{
public string Description { get; set; }
public int Id { get; set; }
public bool IsActive { get; set; }
public string Name { get; set; }
}
public class MultiToggle
{
public bool DefaultValue { get; private set; }
public bool OtherValue { get; private set; }
}
public static class FeatureToggles
{
//public static bool TestFeatureToggle { get; private set; }
public static MultiToggle TestFeatureToggle { get; private set; }
public static void SetFeatureToggles(List<FeatureToggle> toggles)
{
if (toggles == null) return;
var properties = typeof(FeatureToggles).GetProperties(BindingFlags.Public | BindingFlags.Static);
// All properties must be set to false to prevent a property staying true when deleted from the database
foreach (var property in properties)
{
var multiToggleProperties = typeof(MultiToggle).GetProperties();
foreach (var multiToggleProperty in multiToggleProperties)
{
multiToggleProperty.SetValue(new MultiToggle(), false, null);
}
}
foreach (var toggle in toggles)
{
foreach (var property in properties)
{
if (property.Name.ToLower() == toggle.Name.ToLower())
{
Type tProp = property.GetType();
var multiToggleProperties = typeof(MultiToggle).GetProperties();
foreach (var multiToggleProperty in multiToggleProperties)
{
Console.WriteLine(multiToggleProperty);
multiToggleProperty.SetValue(new MultiToggle(), toggle.IsActive, null);
}
}
}
}
}
public static bool GetFeatureToggleSetting(string propertyName)
{
var properties = typeof(FeatureToggles).GetProperties(BindingFlags.Public | BindingFlags.Static);
foreach (var property in properties)
{
if (property.Name.ToLower() == propertyName.ToLower())
{
Type tProp = property.GetType();
var multiToggleProperties = typeof(MultiToggle).GetProperties();
Console.WriteLine(multiToggleProperties);
foreach (var multiToggleProperty in multiToggleProperties)
{
Console.WriteLine(multiToggleProperty);
return (bool)multiToggleProperty.GetValue(null, null);
}
}
}
return false;
}
}
The idea was there but you've essentially "just overshot the mark". You are getting the "Non-static method requires a target" error because you are trying to get the value of a property in the value of a static property, without getting the value of the static property first (what a mouthful). i.e. you are going:
get static property type -> get instance property type -> get value of property from static property.
DefaultValue, and OtherValue are instance properties so you will need the instance object first before you can get their value. I made a few tweaks just to show you how to both set the static property and get the values from the static property. You should be able to tweak it from there:
void Main()
{
var exampleFeatureToggles = new List<FeatureToggle>
{
new FeatureToggle {Description = "Test", Id = 1, IsActive = true, Name = "TestFeatureToggle"}
};
FeatureToggles.SetFeatureToggles(exampleFeatureToggles);
Console.WriteLine(FeatureToggles.GetFeatureToggleSetting("TestFeatureToggle"));
}
public class FeatureToggle
{
public string Description { get; set; }
public int Id { get; set; }
public bool IsActive { get; set; }
public string Name { get; set; }
}
public class MultiToggle
{
public bool DefaultValue { get; private set; }
public bool OtherValue { get; private set; }
}
public static class FeatureToggles
{
//public static bool TestFeatureToggle { get; private set; }
public static MultiToggle TestFeatureToggle { get; private set; }
public static void SetFeatureToggles(List<FeatureToggle> toggles)
{
if (toggles == null) return;
var properties = typeof(FeatureToggles).GetProperties(BindingFlags.Public | BindingFlags.Static);
// All properties must be set to false to prevent a property staying true when deleted from the database
foreach (var property in properties)
{
// first change: set the _property_, not multiToggleProperty
property.SetValue(null, new MultiToggle());
}
foreach (var toggle in toggles)
{
foreach (var property in properties)
{
if (property.Name.ToLower() == toggle.Name.ToLower())
{
Type tProp = property.GetType();
var multiToggleProperties = typeof(MultiToggle).GetProperties();
// second change: create a nee instance, set the values, then set that value on the static property
var newMultiToggle = new MultiToggle();
property.SetValue(null, newMultiToggle);
foreach (var multiToggleProperty in multiToggleProperties)
{
Console.WriteLine(multiToggleProperty);
multiToggleProperty.SetValue(newMultiToggle, toggle.IsActive, null);
}
}
}
}
}
public static bool GetFeatureToggleSetting(string propertyName)
{
var properties = typeof(FeatureToggles).GetProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
foreach (var property in properties)
{
if (property.Name.ToLower() == propertyName.ToLower())
{
Type tProp = property.GetType();
// third change: get the static value first, then get the value from the properties on that instance.
var value = property.GetValue(null);
var multiToggleProperties = typeof(MultiToggle).GetProperties();
Console.WriteLine(multiToggleProperties);
foreach (var multiToggleProperty in multiToggleProperties)
{
Console.WriteLine(multiToggleProperty);
return (bool)multiToggleProperty.GetValue(value, null); //
}
}
}
return false;
}
}
Related
I can't find a solution to my problem, so I try ask here. I have a class and I want to have a method in the class to test for the state of the Properties. The method should return true if any of the properties has more one or more values assigned. But i cannot find any examples of how to loop trough all the properties of the class itself with reflection and test if Count is > than 0.
Or should I use another technique than reflection?
I just want to avoid hard coding the Properties one more time in the Test method.
using System.Reflection;
public class cP
{
public Guid gid { get; set; } = Guid.NewGuid();
public List<string> p1 { get; set; } = new List<string>();
public List<string> p2 { get; set; } = new List<string>();
public bool HasDefinedValues()
{
List<PropertyInfo> properties = this.GetType().GetProperties().ToList();
foreach (PropertyInfo property in properties)
{
if (property.PropertyType == typeof(List<int>))
{
string PName = property.Name;
if (((List<int>)property.GetValue(property.Name, null)).Count > 0) { return true; };
}
}
return false;
}
}
This is working now 😃
using System.Reflection;
public class cP
{
public Guid gid { get; set; } = Guid.NewGuid();
public List<string> p1 { get; set; } = new List<string>();
public List<string> p2 { get; set; } = new List<string>();
public bool HasDefinedValues()
{
List<PropertyInfo> properties = this.GetType().GetProperties().ToList();
foreach (PropertyInfo property in properties)
{
if (property.PropertyType == typeof(List<string>))
{
if (((List<string>)property.GetValue(this, null)).Count > 0) { return true; };
}
}
return false;
}
}
I have here my model:
public class RoleModel
{
public int ID { get; set; }
public string RoleName { get; set; }
public UserRoleModel TrackAndTrace { get; set; }
public UserRoleModel MailDelivery { get; set; }
public UserRoleModel MailAccounting { get; set; }
public UserRoleModel PerformanceMonitoring { get; set; }
public UserRoleModel AdminSettings { get; set; }
}
I want to get the value of any key of 'RoleModel'
public bool SaveRoleModule(RoleModel role)
{
PropertyInfo[] properties = typeof(RoleModel).GetProperties();
foreach (PropertyInfo property in properties)
{
if(property.Name != "ID" && property.Name != "RoleName")
{
Console.WriteLine(role[property.Name]);//(this doesn't work) I want it dynamic
Console.WriteLine(role.TrackAndTrace); //not like this
}
}
return true;
}
I used loop to shorten my code.
You can use this method here: Get property value from string using reflection in C# like this:
public bool SaveRoleModule(RoleModel role)
{
PropertyInfo[] properties = typeof(RoleModel).GetProperties();
foreach (PropertyInfo property in properties)
{
if(property.Name != "ID" && property.Name != "RoleName")
{
var value = GetPropValue(role, property.Name);
Console.WriteLine(value);//(this doesn't work) I want it dynamic
Console.WriteLine(role.TrackAndTrace); //not like this
}
}
return true;
}
public static object GetPropValue(object src, string propName)
{
return src.GetType().GetProperty(propName).GetValue(src, null);
}
You could try something like this:
public bool SaveRoleModule(RoleModel role)
{
PropertyInfo[] properties = typeof(RoleModel).GetProperties();
foreach (PropertyInfo property in properties)
{
if(property.Name != "ID" && property.Name != "RoleName")
{
Type t = role.GetType();
PropertyInfo p = t.GetProperty(property.Name);
UserRoleModel urm = ((UserRoleModel)p.GetValue(role, null));
// do something with urm
}
}
return true;
}
Though Fabio is right, this does seem strange and potentially based on faulty reasoning.
I have a list where TestClass is a class with some predefined properties. So when i get data and bind my list with data i need to ignore some properties of TestClass by comparing it with a list. How can i achieve that?
Below is my code
public class TestClass
{
public int id{get;set;}
public string fname{get;set;}
public string lname{get;set;}
public string job {get;set;}
public string role{get;set;}
public string address{get;set;}
}
List<TestClass> ulist = null;
ulist = ToList<TestClass>(usersdataset.tables[0]); //fill my list with the data code is given below
so after getting the data into the list i need to remove some properties by comparing it with list of properties which should be returned.for example if my filteredlist should only show id,fname,role then i need to remove the extra properties from my ulist. so after the filter ulist should only contain id,fname and role
ToList Method
public static List<T> ToList<T>(DataTable dataTable) where T : new()
{
var dataList = new List<T>();
//Define what attributes to be read from the class
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
//Read Attribute Names and Types
var objFieldNames = typeof(T).GetProperties(flags).Cast<PropertyInfo>().
Select(item => new
{
Name = item.Name,
Type = Nullable.GetUnderlyingType(item.PropertyType) ?? item.PropertyType
}).ToList();
//Read Datatable column names and types
var dtlFieldNames = dataTable.Columns.Cast<DataColumn>().
Select(item => new {
Name = item.ColumnName,
Type = item.DataType
}).ToList();
foreach (DataRow dataRow in dataTable.AsEnumerable().ToList())
{
var classObj = new T();
foreach (var dtField in dtlFieldNames)
{
PropertyInfo propertyInfos = classObj.GetType().GetProperty(dtField.Name);
var field = objFieldNames.Find(x => x.Name == dtField.Name);
//var field = filteredColumns.Find(x => x.PropertyName == dtField.Name);
if (field != null)
{
if (dataRow[dtField.Name] != DBNull.Value)
propertyInfos.SetValue(classObj, dataRow[dtField.Name], null);
}
}
dataList.Add(classObj);
}
return dataList;
}
Use the overvride function Equals:
This sample compare only the id property
public class TestClass
{
public int id { get; set; }
public string fname { get; set; }
public string lname { get; set; }
public string job { get; set; }
public string role { get; set; }
public string address { get; set; }
public override bool Equals(object obj)
{
if (obj.GetType().Name != this.GetType().Name)
{
return false;
}
TestClass testclassObject = (TestClass)obj;
if (testclassObject.id != this.id)
{
return false;
}
return true;
}
I know I can use reflection to set a objects property like this below.
public void SaveContent(string propertyName, string contentToUpdate, string corePageId)
{
var page = Session.Load<CorePage>(corePageId);
Type type = page.GetType();
PropertyInfo prop = type.GetProperty(propertyName);
prop.SetValue(page, contentToUpdate, null);
}
I'm having these classes below:
public class CorePage
{
public string BigHeader { get; set; }
public List<BigLinks> BigLinks { get; set; }
}
public class BigLinks
{
public string TextContent { get; set; }
}
My SaveContent()-method works obviously when the property to set is, for example public string BigHeader { get; set; }
But how can I do this if the the property I want to set is in the property:
public List<BigLinks> BigLinks { get; set; }
If public List<BigLinks> BigLinks { get; set; } is a list of 5 BigLinks objects, how can a set the value of, for example the third objects public string TextContent { get; set; }?
You have to get the property value using reflection and change the desired value like this:
var c = new CorePage() { BigLinks = new List<BigLinks> { new BigLinks { TextContent = "Y"}}};
var r = typeof(CorePage).GetProperty("BigLinks").GetGetMethod().Invoke(c, null) as List<BigLinks>;
r[0].TextContent = "X";
If you don't know the type of list item:
var itemInList = (typeof(CorePage).GetProperty("BigLinks").GetGetMethod().Invoke(c, null) as IList)[0];
itemInList.GetType().GetProperty("TextContent").SetValue(itemInList, "XXX", null);
Another option is casting to dynamic:
var itemInList = (typeof(CorePage).GetProperty("BigLinks").GetGetMethod().Invoke(c, null) as dynamic)[0].TextContent = "XXXTTT";
I am using this method to copy properties of one object to another and its working fine.
But today i found that its not working for arrays of different object.
please help me in this.
public static class CopyClass
{
/// <summary>
/// Copies source object properties to target object properties.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="target">The target.</param>
public static void CopyTo(object source, object target)
{
foreach (PropertyInfo propSource in source.GetType().GetProperties())
{
foreach (PropertyInfo propTarget in target.GetType().GetProperties())
{
if (propTarget.Name != propSource.Name) continue;
(propTarget.GetSetMethod()).Invoke(target,
new object[] { propSource.GetGetMethod().Invoke(source, null) });
}
}
}
}
Can you check with this function? I am not sure if this is working with Arrays but it sure does with plain members.
public static object ObjectCopyProperties(this object sourceObject, object targetObject)
{
if (sourceObject == null || targetObject == null)
return null;
var targetInstance = targetObject;
PropertyInfo newProp;
foreach (PropertyInfo prop in sourceObject.GetType().GetProperties())
{
if (prop.CanRead)
{
newProp = targetInstance.GetType().GetProperty(prop.Name);
if (newProp != null && newProp.CanWrite)
{
newProp.SetValue(targetInstance, prop.GetValue(sourceObject, null), null);
}
}
}
return targetInstance;
}
Given the following 2 classes
public class UserType1
{
public DateTime Created { get; set; }
public string First { get; set; }
public Gender Genter { get; set; }
public int Id { get; set; }
public string Last { get; set; }
public DateTime Updated { get; set; }
public string DontMatchType { get; set; }
public string Unique1 { get; set; }
}
public class UserType2
{
public DateTime Created { get; set; }
public string First { get; set; }
public Gender Genter { get; set; }
public int Id { get; set; }
public string Last { get; set; }
public DateTime Updated { get; set; }
public int DontMatchType { get; set; }
public string Unique2 { get; set; }
}
and the following code
UserType1 user1 = new UserType1
{
Id = 1,
First = "John",
Last = "Doe",
Genter = Gender.Male,
Created = DateTime.Now.AddDays(-1),
Updated = DateTime.Now,
DontMatchType = "won't map",
Unique1 = "foobar"
};
UserType2 user2 = CopyTo<UserType2>(user1);
you can see that this mapping function will only map matching name/types
public static T CopyTo<T>(object source)
where T : new()
{
if (source == null) throw new ArgumentException("surce is null", "source");
T target = new T();
source.GetType()
.GetProperties()
.Join(target.GetType().GetProperties()
, s => s.Name
, t => t.Name
, (s, t) => new
{
source = s,
target = t
})
.AsParallel()
.Where(inCommon => inCommon.source.PropertyType == inCommon.target.PropertyType
&& inCommon.source.CanRead && inCommon.target.CanWrite)
.ForAll(inCommon => inCommon.target.SetValue(target, inCommon.source.GetValue(source, null), null));
return target;
}
and you can use
public static IEnumerable<T> CopyTo<T>(IEnumerable<object> source)
where T : new()
{
return source.AsParallel().Select(CopyTo<T>);
}
to copy a collection like this
UserType1[] users1 = new[]
{
new UserType1
{
...
}
};
UserType2[] users2 = CopyTo<UserType2>(users1).ToArray();
This has the added benefit that it will not needlessly loop all the properties of object B for every property in object A, as its using a join to find the properties in common (by name and type).
Mapping can be tricky. You can get into a lot of situations of name/types and nesting. I would suggest Automapper as jjchiw mentioned.
If your object is serializable you can create an extension method which serialize the source object and deserialize it in return
public static class CloneExtensions
{
public static T Clone<T>(this T source)
{
if (!typeof(T).IsSerializable)
{
throw new ArgumentException("The type must be serializable.", "source");
}
if(source == default(T))
return default(T);
IFormatter formatter = new BinaryFormatter();
Stream stream = new MemoryStream();
using (stream)
{
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return (T)formatter.Deserialize(stream);
}
}
}