I'm trying to get expressions for the first level properties of a given class, through an array of strings, each one related to the property name to get the expression:
public List<MemberExpression> CreateMembers(string propertyPaths)
{
List<MemberExpression> test = new List<MemberExpression>();
var propertiesPath = propertyPaths.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var propertyPath in propertiesPath)
{
var mainParameter = Expression.Parameter(typeof(Process), "e");
var property = Expression.PropertyOrField(mainParameter, typeof(Process), propertyPath);
test.Add(property);
property = null;
mainParameter = null;
}
return test;
}
I'm trying to get the public virtual properties from this class:
public class Process
{
public int? CompanyId { get; set; }
public int? RecommendationId { get; set; }
public int? DiagnosisNodeId { get; set; }
[ForeignKey("DiagnosisId")]
public virtual Diagnosis Diagnosis { get; set; }
[ForeignKey("DiagnosisNodeId")]
public virtual DiagnosisNode DiagnosisNode { get; set; }
[ForeignKey("RecommendationId")]
public virtual Recommendation Recommendation { get; set; }
}
but after the first iteration the Expression.Property always throws an exception that the given property doesn't exist in the class Process.
Can someone help me with this?
an example of the propertyPaths is: "Diagnosis, DiagnosisNode"
The problem is the space after the comma. There is no property called " DiagnosisNode" (with a leading space). Either use .Trim() on the strings coming back from Split, or don't include the space in the first place.
Related
Although the thing I want to do seems be really trivial I can not find a way to achieve what I want. I know there exist multiple questions how to put class properties into the list together and separate it by a comma like that on SO, but none of them seemed to be relevant to my case.
I have a class Form defined as follows:
public class Form
{
public string CustomerName { get; set; }
public string CustomerAdress { get; set; }
public string CustomerNumber { get; set; }
public string OfficeAdress { get; set; }
public string Date { get; set; }
public Boolean FunctionalTest { get; set; }
public string Signature { get; set; }
public Form()
{
}
}
In the MainPage.xaml.cs, I create a List<Form> with the Form class properties and subsequently I would like to create a string with all of those class properties separated by a comma. For that case I use basic Join method with Select which converts any kinds of objects to string.
I do that by createCSV method inside MainPage.xaml.cs :
void createCSV()
{
var records = new List<Form>
{
new Form {CustomerName = customerName.Text,
CustomerAdress = customerAdress.Text,
CustomerNumber = customerNumber.Text,
OfficeAdress = officeAdress.Text,
Date = date.Date.ToString("MM/dd/yyyy"),
FunctionalTest = testPicker.ToString()=="YES" ? true : false,
Signature = signature.Text
}
};
string results = String.Join(",", (object)records.Select(o => o.ToString()));
}
The problem is instead of the desirable outcome which is:"Mark Brown,123 High Level Street,01578454521,43 Falmouth Road,12/15/2020,false,Brown"
I am getting: "System.Linq.Enumerable+SelectListIterator'2[MyApp.Form,System.String]"
PS. As you have noticed I am newbie in C#. Instead of non constructive criticism of the code, please for a valuable reply which would help me to understand what am I doing wrong.
Thanks in advance
In the Form class, You can override the ToString() method and use System.Reflection to get your comma string.
Form.cs
public class Form
{
public string CustomerName { get; set; }
public string CustomerAdress { get; set; }
public string CustomerNumber { get; set; }
public string OfficeAdress { get; set; }
public string Date { get; set; }
public bool FunctionalTest { get; set; }
public string Signature { get; set; }
public override string ToString()
{
string modelString = string.Empty;
PropertyInfo[] properties = typeof(Form).GetProperties();
foreach (PropertyInfo property in properties)
{
var value = property.GetValue(this); // you should add a null check here before doing value.ToString as it will break on null
modelString += value.ToString() + ",";
}
return modelString;
}
}
Code
List<string> CSVDataList = new List<string>();
List<Form> FormList = new List<Form>();
...
foreach (var data in FormList)
{
CSVDataList.Add(data.ToString());
}
Now you have a list of string CSVDataList with each Form object's data in comma style
P.S.
for DateTime
var value = property.GetValue(this);
if(value is DateTime date)
{
modelString += date.ToString("dd.MM.yyyy") + ",";
}
I have different classes sharing some properties of same type and name. I wish to assign same property values to each other. I explain my intention better in comments in the following pseudo-code. Is it possible in C#?
Ponder that there are a plethora of common properties but in unrelated classes, must we assign them one-by-one?
Second case is about sharing same properties but some of them may be nullable, who knows!
Side note: the classes already exist, cannot be altered, touched. Kinda sealed.
Can't it be done using nameofoperator and two for loops? Compare property names if matched, assign?
using System;
namespace MainProgram
{
class HomeFood
{
public DateTime Date { get; set; }
public string food1 { get; set; }
public string food2 { get; set; }
public int cucumberSize { get; set; }
}
class AuntFood
{
public string food2 { get; set; }
public int cucumberSize { get; set; }
public DateTime Date { get; set; }
public string food1 { get; set; }
// extra
public double? length { get; set; }
}
class GrandpaFood
{
public string? food2 { get; set; }
public int cucumberSize { get; set; }
public DateTime? Date { get; set; }
public string food1 { get; set; }
// extra
}
static class Program
{
public static void Main(string[] args)
{
var home = new HomeFood
{
Date = new DateTime(2020, 1, 1),
food1 = "cucumber",
food2 = "tomato",
cucumberSize = 123
};
var aunt = new AuntFood();
/*
First case: same types
Expected for-each loop
assigning a class's property values
to other class's property values
or for-loop no matter
foreach(var property in HomeFood's properties)
assign property's value to AuntFood's same property
*/
var home2 = new HomeFood();
var grandpa = new GrandpaFood
{
Date = new DateTime(2020, 1, 1),
food1 = "dfgf",
food2 = "dfgdgfdg",
cucumberSize = 43534
};
/*
Second case: similar to first case
with the exception of same type but nullable
or for-loop no matter
foreach(var property in GrandpaFood's properties)
assign property's value to GrandpaFood's same property
we don't care if it is null e.g.
Home2's same property = property's value ?? default;
*/
}
}
}
Based on the comments in the questions, this is just to show how it can be done with reflection.
Disclaimer, this is just a very simplified example on how to use reflection to sync properties. It does not handle any special cases (modifiers, read only, type mismatch, etc)
I would strongly suggest to use automapper to achieve the qp goals.
public class Type1
{
public string Property1 { get; set; }
public string Property2 { get; set; }
}
public class Type2
{
public string Property1 { get; set; }
public string Property3 { get; set; }
}
class Program
{
static void Main(string[] args)
{
var t1 = new Type1 { Property1 = "Banana" };
var t2 = new Type2();
var properties1 = typeof(Type1).GetProperties().ToList();
var properties2 = typeof(Type2).GetProperties().ToList();
foreach(var p in properties1)
{
var found = properties2.FirstOrDefault(i => i.Name == p.Name);
if(found != null)
{
found.SetValue(t2, p.GetValue(t1));
}
}
Console.WriteLine(t2.Property1);
}
}
The short answer is, apply OOP. Define a base Food class and inherit from it in any specific food classes you have. You can put all the shared props in the base class.
public class Food
{
public string food2 { get; set; }
// other shared stuff
}
class GrandpaFood : Food
{
// other specific stuff
}
As others have said, use some of the Object Oriented properties, like inheriting a super class of implement an interface.
In case you go for inheritance, consider making the super class (the one you inherit from) abstract. This means that the super class itself cannot be instantiated, which greatly reduces the risk of violating the Liskov Substitutional Principle. Also it often reflects the real problem better. In your example, this would also be the case, as “food” is not an actual thing in the real world, but rather a group of things.
i have 2 model classes
public class ProductOptionRequest
{
public string Name { set; get; }
public List<ProductValuesRequest> productValues { get; set; }
}
public class ProductValuesRequest
{
public string ValueName { get; set; }
}
public class ProductOptionValue
{
public int OptionId { get; set; }
public String ValueName { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
}
and wrote one bs method and passing parameter value as value names. but I'm unable to get those values in a list object as productValues. May I know the solution, please.
public async Task<ReturnString> SaveProductOption(ProductOptionRequest request)
{
request.productValues = new List<ProductValuesRequest>();
foreach (ProductValuesRequest valueRequest in request.productValues)
{
ProductOptionValue res = new ProductOptionValue();
res.ValueName = valueRequest.ValueName;
object response = await productOptionValueRepository.InsertAsync(res, true);
}
}
In the first line of your method, you are replacing the productValues property of request object with a new empty list, :
request.productValues = new List<ProductValuesRequest>();
Therefore, in foreach loop, you are iterating on an empty list.
Remove the first line and see if you still have any issues.
You are assigning an emplty list to productValues as
request.productValues = new List() and trying to iterate the empty list.
I'm making a web service call and getting data back that I am adding to a list. No problems doing that. My remedyinfo list has content that I can verify with break points in VS.
I cannot seem to figure out how to search the list for a value that matches a new variable, for instance I want to find if incidentID is equal to "INC000000001".
I've tried var foundItem = remedyinfo.Contains("searchvalue") but it always returns false.
I've tried the LINQ queries as suggested in other post:
var foundItem = myArray.SingleOrDefault(item => item.intProperty == someValue);
What I do notice is that the sample refers to comparing item.intProperty == somevalue) should work, I am not able to get any reference after item., only suggested Equals, GetType, GetHashCode and ToString. So, I cannot reference item.incidentID for example.
Any guidance is greatly appreciated.
var remedyinfo = new List<object> { };
remedyinfo.Add(new IncidentItem()
{
assignedgroup = assignedgroup,
incidentID = incidentID,
submitdate = offsetDate.ToString(),
priority = priority,
status = status,
assignee = assignee,
summarydesc = summarydesc,
notes = notes
});
[Serializable]
public class IncidentItem
{
public string assignedgroup { get; set; }
public string incidentID { get; set; }
public string submitdate { get; set; }
public string priority { get; set; }
public string status { get; set; }
public string assignee { get; set; }
public string summarydesc { get; set; }
public string notes { get; set; }
}
To check if list contains an item, there are different options. You can use Any in .Net 3.5 or higher:
if (remedyinfo.Any(incident => incident.incidentID == "Hello"))
// rest of code
If you have another IncidentItem and wish to check in list like
var anotherIncident = new IncidentItem();
if (remedyInfo.Contains(anotherIncident))
then you either have to implement IEquatable or override Equals and HashCode in IncidentItem class.
More on Equals and HashCode is here:
Correct way to override Equals() and GetHashCode()
This question is related to this question. I managed to get one step further, but I am now unable to initialize my whole object with default values in order to prevent it from being null at list level. The goal of this is to hand down the "null" values to my SQL query. Ultimately what I want is one record in my DB that will express: This row has been recorded, but the related values were "null".
I have tried Brian's fiddle and it does not seem to work for me to initialize the whole model with standard values.
Expectation: Upon object initialisation the "null" values should be used and then overwritten in case there is a value coming through JSON deserialisation.
Here is what I have tried. None of this will have the desired effect. I receive this error:
Application_Error: System.ArgumentNullException: Value cannot be null.
Every time I try to access one of the lists in the data model.
namespace Project.MyJob
{
public class JsonModel
{
public JsonModel()
{
Type_X type_x = new Type_X(); // This works fine.
List<Actions> action = new List<Actions>(); // This is never visible
/*in my object either before I initialise JObject or after. So whatever
gets initialised here never makes it to my object. Only Type_X appears
to be working as expected. */
action.Add(new Actions {number = "null", time = "null", station =
"null", unitState = "null"}) // This also does not prevent my
//JsonModel object from being null.
}
public string objectNumber { get; set; }
public string objectFamily { get; set; }
public string objectOrder { get; set; }
public string location { get; set; }
public string place { get; set; }
public string inventionTime { get; set; }
public string lastUpdate { get; set; }
public string condition { get; set; }
public Type_X Type_X { get; set; }
public List<Actions> actions { get; set; }
}
public class Actions
{
public Actions()
{
// None of this seems to play a role at inititialisation.
count = "null";
datetime = "null";
place = "null";
status = "null";
}
// public string count { get; set; } = "null"; should be the same as above
// but also does not do anything.
public string count { get; set; }
public string datetime { get; set; }
public string place { get; set; }
public string status { get; set; }
}
public class Type_X
{
public Type_X
{
partA = "null"; // This works.
}
public string partA { get; set; }
public string PartB { get; set; }
public string partC { get; set; }
public string partD { get; set; }
public string partE { get; set; }
}
}
This is how I now initialize the object based on Brian's answer.
JObject = JsonConvert.DeserializeObject< JsonModel >(json.ToString(), new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore});
When I try to iterate over Actions' content, it (logically) gives me above mentioned null error.
for (int i = 0, len = JObject.actions.Count(); i < len; i++)
My current understanding of constructor initialisations:
If I define values such as count = "null"; they should appear in any new object that is created.
If default values are present I would then also expect that a list that has items with default values (such as count for ex.) would be of Count() 1 and not null. How is that even possible?
This will get you out of your bind:
private List<Actions> _actions = new List<Actions>();
public List<Actions> actions { get => _actions; set => _actions = value ?? _actions; }
This causes trying to set actions to null to set it to the previous value, and it is initially not null so it can never be null.
I'm not absolutely sure I'm reading your question right, so here's the same fragment for partA:
private string _partA = "null";
public string partA { get => _partA; set => _partA = value ?? _partA; }
I have found that in some cases, initializing generic lists with their default constructor on your model increases ease of use. Otherwise you will always want to validate they are not null before applying any logic(even something as simple as checking list length). Especially if the entity is being hydrated outside of user code, i.e. database, webapi, etc...
One option is to break up your initialization into two parts. Part 1 being the basic initialization via default constructor, and part 2 being the rest of your hydration logic:
JObject = new List < YourModel >();
... < deserialization code here >
Alternatively you could do this in your deserialization code, but it would add a bit of complexity. Following this approach will allow you to clean up your code in other areas since each access will not need to be immediately proceeded by a null check.