How to assign values to properties dynamically - c#

I have below method on service layer which will be called from different controller with different actionType(parameter to my method).
public async Task<OutPutList[]> GetMyDataAsync(string actionType)
{
var request = new getMyDataRequest
{
getMyDatas = new getMyDatas
{
AFlag= false,
AFlagSpecified = false,
BFlag=false,
BFlagSpecified=false,
cFlag=false,
cFlagSpecified=false,
commonproperty= ""
}
};
return await _myService.method(request);
}
If the actionType is specified as "A" , the AFlag and AFlagSpecified properties value to be set as "True" . And if i specify actionType as B then BFlag and BFlagSpecified values to be set as true and AFlag will be false.how this can be done dynamically or in any smpler way? I am having around 12 actiontypes and if i create different request object then i need to write 12 if else condition. Can this be simplified with minimum code.

You can use Reflection to make your code dynamic.
To make you understand more, You can look below code
getMyDatas.cs
public class getMyDatas
{
public bool AFlag { get; set; }
public bool BFlag { get; set; }
public bool CFlag { get; set; }
}
Program.cs
using StackOverFlowTest;
using System;
using System.Reflection;
//input
string actionType = "A";
//Business
var Data = new getMyDatas();
PropertyInfo[] properties = typeof(getMyDatas).GetProperties();
foreach (PropertyInfo property in properties)
{
property.SetValue(Data, Convert.ChangeType(property.Name.StartsWith(actionType), property.PropertyType), null);
}
//Testing
Console.WriteLine(Data.AFlag);
Console.WriteLine(Data.BFlag);
Console.WriteLine(Data.CFlag);
Shortly, I check if property name starts with input

Related

How to reference property of another object if the current object's property is null

I have a public Dictionary<string, PostRenewalActionJobs> Jobs to store some actions I would like to trigger for specific accounts, the key of this dictionary being the account name.
public class PostRenewalActionJobs
{
public List<AlterDatabaseLinkJob> AlterDataBaseLink { get; set; }
public DatabaseConnectionCheckJob DatabaseConnectionCheck { get; set; }
public UnlockDatabaseAccountJob UnlockDatabaseAccount { get; set; }
public LinuxConnectionCheckJob LinuxConnectionCheck { get; set; }
public WindowsConnectionCheckJob WindowsConnectionCheck { get; set; }
public ReplacePasswordInFileJob ReplacePasswordInFile { get; set; }
}
The properties of PostRenewalActionJobs type (AlterDataBaseLink, DatabaseConnectionCheck, etc) can be defined for a specific account or for all accounts by using * as key in the dictionary:
By using below method I am able to retrieve the jobs for an account (if exists) or the general jobs:
public PostRenewalActionJobs GetJobsForAccount(string accountName)
{
return Jobs.ContainsKey(accountName) ? Jobs[accountName] : Jobs["*"];
}
I would like to have a dynamic way of getting a job from the all accounts object ("*") if the one from the specific account is null.
Something like below but whit out repeating the same code for all job types and also a solution that should work when new job types are introduced.
var dbConCheckJob = GetJobsForAccount("someAccount").AlterDataBaseLink;
if(dbConCheckJob == null || !dbConCheckJob.Any())
{
dbConCheckJob = GetJobsForAccount("*").AlterDataBaseLink
}
I was thinking to use some reflection, but I am not sure how to do it.
You don't need to use reflection. You can already determine whether to get the specific jobs for an account or the generic ones, you could then use a Func to get the job you want:
public TJob GetPostJobForAccount<TJob>(string accountName,
Func<PostRenewalActionJobs, TJob> jobSelector) where TJob : JobBase
{
var genericJobs = Jobs["*"];
var accountJobs = Jobs.ContainsKey(accountName) ? Jobs[accountName] : genericJobs;
// Account might be defined but without any job of the given type
// hence selecting from the defaults if need be
return jobSelector(accountJobs) ?? jobSelector(genericJobs);
}
var bobJob = GetPostJobForAccount("bob", x => x.WindowsConnectionCheck);
var aliceJob = GetPostJobForAccount("alice", x => x.UnlockDatabaseAccount);
I found a way to do it, not sure if there is a better way:
public TJob GetPostJobForAccount<TJob>(string accountName)
{
Type type = typeof(PostRenewalActionJobs);
var accountJobs = Jobs[accountName];
var generalJobs = Jobs["*"];
foreach (var item in type.GetProperties())
{
var itemType = item.PropertyType;
var currentType = typeof(TJob);
if (itemType != currentType)
{
continue;
}
var output = (TJob)accountJobs?.GetType()?.GetProperty(item.Name)?.GetValue(accountJobs, null);
if (output is null)
{
output = (TJob)accountJobs?.GetType()?.GetProperty(item.Name)?.GetValue(generalJobs, null);
}
return output;
}
return default;
}

Check all properties in List

I have List of class as :-
public class Requirement
{
public string Id { get; set; }
public string desc { get; set; }
}
List lstRequirement
I have 3 records in this list for Id and desc.
I wanted to check if any of item is not remaining null.
For that I used below :-
bool IsHavingValidTags = lstRequirement.All(_=> _.Id!=null && _.desc!=null);
This condition is working fine with above Linq.
But I wanted to make it as Generic.
Eg. In future there may get added 5 more properties in Requirement class.
After addition of properties I also have to make changes in Linq.
How can I make this Linq condition generic for all properties?
I want to check any of the property is not remaining null in List.
Please help..!!!
I tried With =>
bool IsHavingValidTags = lstRequirement.All(_ => _ != null);
But not giving desired result.
EDIT 1 :
You can write an extension method that uses reflection like the following:
public static class Extensions
{
public static bool AreAllPropertiesNotNullForAllItems<T>(this IEnumerable<T> items)
{
var properties = typeof(T).GetProperties();
return items.All(x => properties.All(p => p.GetValue(x) != null));
}
}
then use like this:
bool IsHavingValidTags = lstRequirement.AreAllPropertiesNotNullForAllItems();
EDIT:
PropertyInfo.GetValue(object obj) method overload was introduced in .NET Framework 4.5. If you are using .NET Framework 4.0 you need to call p.GetValue(x, null)
Instead of this you should make those field not null. this will never allow those field inserted null. keep validations. like bellow.
[Required(ErrorMessage = "First name is required")]
public string first_name { get; set; }
[Required(ErrorMessage = "Last name is required")]
public string last_name { get; set; }
You can use foreach loop to loop through all the object in the list. Then use reflection to get all the properties in each item in the list, then you can loop through those properties to perform your null check.
Foreach (var x in lstRequirement){
List prop = x.GetType().GetProperties();
Foreach (var y in prop){
If (y == null){
IsHavingValidTag = true;
//Then you can return you method here or throw an Exception
}
}
Hope this helps.
You should add an static method to check the Properties of the Class. I will show you the following example.
Instead of your code :
bool IsHavingValidTags = lstRequirement.All(_ => _ != null);
use the following codes:
bool flg = list.All(m => CheckProperties(m));
public static bool CheckProperties<T>(T source)
{
bool rtnFlg = true;
Type t = typeof(T);
var properties = t.GetProperties();
foreach (var prop in properties)
{
var value = prop.GetValue(source, null);
if (value == null)
{
return false;
}
}
return rtnFlg;
}

Object reference not set to an instance of an object. C# GetProperty()

I have a console aplication where I am trying to obtain the values of the properties of an object dynamically:
class Program
{
static void Main(string[] args)
{
DtoCartaCompromiso test = new DtoCartaCompromiso() { CodProducto = 1,
DescProducto = "aaa",
CodProveedor = 2,
DescProveedor = "bbb",
FechaExpiracion = DateTime.Now,
FechaMaxEntrega = DateTime.Now,
NumLote = "22" };
var testlist = new List<DtoCartaCompromiso>();
testlist.Add(test);
List<Header> columns = new List<Header>() { new Header{Name= "CodProducto"},new Header{Name= "NumLote"},new Header{Name= "DescProducto"},new Header{Name= "CodProveedor"},new Header{Name= "DescProveedor"},new Header{Name= "FechaExpiracion"},new Header{Name= "FechaExpiracion"},new Header{Name= "FechaMaxEntrega"} };
foreach (var d in testlist)
{
foreach (var col in columns)
{
string value = ((d.GetType().GetProperty(col.Name).GetValue(d, null)) ?? "").ToString();
Console.WriteLine(value);
}
}
Console.Read();
}
}
public class DtoCartaCompromiso
{
public int CodProducto;
public string NumLote;
public string DescProducto;
public int CodProveedor;
public string DescProveedor;
public Nullable<DateTime> FechaExpiracion;
public Nullable<DateTime> FechaMaxEntrega;
}
public class Header
{
public string Name;
}
i am getting the error "Object reference not set to an instance of an object" when I get to the line:
string value = ((d.GetType().GetProperty(col.Name).GetValue(d, null)) ?? "").ToString();
the error seems to occur when I get to the GetProperty() method, but I dont understand why
The problem is that you don't have properties there in your classes, they are public fields really. A public property looks like
public string PropertyName { get; set; }
but in your case there is lack of both getters and setters.
Change GetProperty() to GetField() and it will work. Or make your fields properties. Personally, I would go with the second option since it is a better idea to use properties instead of public fields.
Best guess without knowing more about your application, is the following sub-exression results in null:
d.GetType().GetProperty(col.Name)
At that point, the subsequent .GetValue() call will fail with your reported error.
Your property is probably not public. Make it public or pass System.Reflection.BindingFlags.NonPublic to your GetProperty call.
More info: http://msdn.microsoft.com/en-us/library/zy0d4103(v=vs.110).aspx

Refactoring Many Methods into One

I dont know how to name the question properly, so fell free to change it. My question is, I have around 10 methods that look like:
[WebMethod(EnableSession = true)]
public string ReadUserAdditional()
{
EUser user = (EUser)Session["user"];
var json = new { result = true, user.Image, user.Biography };
return new JavaScriptSerializer().Serialize(json);
}
[WebMethod(EnableSession = true)]
public string ReadUserBasicInformation()
{
EUser user = (EUser)Session["user"];
var json = new { result = true, user.Name, user.Username};
return new JavaScriptSerializer().Serialize(json);
}
The methods are very similar, but they return different fields. Im thinking about refactoring all methods into one, receveing the fields to return as parameters. Is it a good idea? How can I do that? Reflection?
First of all you need to know that object and dictionary are presented in json simmilar.
[WebMethod(EnableSession = true)]
public string ReadUserAdditional()
{
return GetUserInfo(new []
{
new FieldInfo {Name = "Image", u => u.Image},
new FieldInfo {Name = "Biography", u => u.Biography}
});
}
private string GetUserInfo(FieldInfo[] infos)
{
EUser user = (EUser)Session["user"];
var dict = new Dictionary<string, object>{ { "result", true } };
foreach(var info in infos)
{
dictionary.Add(info.Name, info.Accessor(user));
}
return new JavaScriptSerializer().Serialize(dict );
}
public class FieldInfo
{
public Func<EUser, object> Accessor { get; set; }
public string Name { get; set;}
}
I don't think it's a terrible idea, especially if you have tons of these methods and want to simplify your API.
A few downsides:
1) Reflection comes at a perf cost. This probably doesn't matter a whole lot unless you're the size of Twitter.
2) There would potentially be security concerns if the data had any properties you do NOT wanting users getting access to, such as some sort of internal database keys or what not. Make sure every property on your class is one you're totally okay becoming public information.
You can use a lambda to refactor away the duplication:. This would reduce all your methods to a single line of code:
[WebMethod(EnableSession = true)]
public string ReadUserAdditional()
{
return GetUserJSON(x => new { result = true, x.Image, x.Biography });
}
[WebMethod(EnableSession = true]
public string ReadUserBasicInformation()
{
return GetUserJSON(x => new { result = true, x.Name, x.UserName });
}
private string GetUserJSON(Func<EUser, string> jsonFields)
{
EUser user = (EUser)Session["user"];
var json = jsonFields(user);
return new JavaScriptSerializer().Serialize(json);
}
Another approach is to use Automapper or similar library to project your data.
[WebMethod(EnableSession = true)]
public string ReadUserAdditional()
{
return GetUserInfo<UserAdditionalDto>();
}
private string GetUserInfo<TDto>(FieldInfo[] infos)
{
EUser user = (EUser)Session["user"];
var dto = Mapper.Map<TDto>(user); // Mapper is Automapper entry class.
return new JavaScriptSerializer().Serialize(dto );
}
public class UserAdditionalDto
{
public string Image { get; set; }
public string Biography { get; set;}
}

Is it possible to modify the attribute of a property at runtime?

Is it possible to modify the attribute of a property at runtime?
let's say I have some class:
public class TheClass
{
[TheAttribute]
public int TheProperty { get; set; }
}
Is there a way to do this?
if (someCondition)
{
// disable attribute. Is this possible and how can this be done?
}
No this is not possible. You cannot modify attribute values from metadata, or metadata in general, at runtime
Strictly speaking the above is not true. There are certain APIs which do allow allow for some metadata generation and modification. But they are very scenario specific, (ENC, profiling, debugging) and should not be used in general purpose programs.
It depends; from a reflection perspective: no. You can't. But if you are talking about attributes used by System.ComponentModel in things like data-binding, they you can use TypeDescriptor.AddAttributes to append extra attributes. Or other customer models involving custom descriptors. So it depends on the use-case.
In the case of xml serialization, it gets more interesting. Firstly, we can use fun object models:
using System;
using System.Xml.Serialization;
public class MyData
{
[XmlAttribute]
public int Id { get; set; }
[XmlAttribute]
public string Name { get; set; }
[XmlIgnore]
public bool NameSpecified { get; set; }
static void Main()
{
var ser = new XmlSerializer(typeof(MyData));
var obj1 = new MyData { Id = 1, Name = "Fred", NameSpecified = true };
ser.Serialize(Console.Out, obj1);
Console.WriteLine();
Console.WriteLine();
var obj2 = new MyData { Id = 2, Name = "Fred", NameSpecified = false };
ser.Serialize(Console.Out, obj2);
}
}
The bool {name}Specified {get;set;} pattern (along with bool ShouldSerialize{name}()) is recognised and used to control which elements to include.
Another alternative is to use the non-default ctor:
using System;
using System.Xml.Serialization;
public class MyData
{
[XmlAttribute]
public int Id { get; set; }
public string Name { get; set; }
static void Main()
{
var obj = new MyData { Id = 1, Name = "Fred" };
XmlAttributeOverrides config1 = new XmlAttributeOverrides();
config1.Add(typeof(MyData),"Name",
new XmlAttributes { XmlIgnore = true});
var ser1 = new XmlSerializer(typeof(MyData),config1);
ser1.Serialize(Console.Out, obj);
Console.WriteLine();
Console.WriteLine();
XmlAttributeOverrides config2 = new XmlAttributeOverrides();
config2.Add(typeof(MyData), "Name",
new XmlAttributes { XmlIgnore = false });
var ser2 = new XmlSerializer(typeof(MyData), config2);
ser2.Serialize(Console.Out, obj);
}
}
Note though that if you use this second approach you need to cache the serializer instance, as it emits an assembly every time you do this. I find the first approach simpler...
Attributes are baked into code at compilation time. The only way you can define new attributes at run time is to generate new code at runtime (using Reflection.Emit, for example). But you cannot change the attributes of existing code.
You can put Boolean variable in the class to disable/enable the property instead of disabling it at run time.
You might want to look at this http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/5b0d356d-d006-43ff-bfcd-aa90dd8de6db
And Dave Morton's explanation on this blog http://blog.codinglight.com/2008/10/changing-attribute-parameters-at.html
Sounds like you want to consider implementing IXmlSerializable
You can implement IDataErrorInfo, then check range in Validate method.
public string this[string property] {
get { return Validate(property); }
}
public string Error { get; }
protected virtual string Validate(string property) {
var propertyInfo = this.GetType().GetProperty(property);
var results = new List<ValidationResult>();
var result = Validator.TryValidateProperty(
propertyInfo.GetValue(this, null),
new ValidationContext(this, null, null) {
MemberName = property
},
results);
if (!result) {
var validationResult = results.First();
return validationResult.ErrorMessage;
}
return string.Empty;
}
In sub class
protected override string Validate(string property) {
Debug.WriteLine(property);
if (property == nameof(YourProperty)) {
if (_property > 5) {
return "_property out of range";
}
}
return base.Validate(property);
}

Categories

Resources