I am trying to access another model's(Property_Master) property in current model(PropertyDetails_Master) for conditional validation purpose by inheriting IValidatableObject. But When I create object of another model(object of Property_Master), It returns null values of property. How do I get value of property of another model?
In my code
public class Property_Master : IValidatableObject
{
//Another Properties
[DisplayName("Property Type")]
[Required(ErrorMessage = "* Please Select Property Type.")]
public string PR_PropertyType
{
get { return strPropertyType;}
set { strPropertyType=value;}
}
}
In another model, I want to use above PR_PropertyType for to check condition in PropertyDetails_Master model.
public class PropertyDetails_Master : IValidatableObject
{
Property_Master pm = new Property_Master();
[DisplayName("Flat No.")]
public string PR_FlatNo { get; set; }
[DisplayName("Floor No.")]
public Nullable<int> PR_Floor { get; set; }
[DisplayName("No. Of Bedrooms")]
[Required(ErrorMessage = "* Please Enter No. Of Bedrooms.")]
public Nullable<int> PD_Bedrooms { get; set; }
[DisplayName("No. Of Bathrooms")]
[Required(ErrorMessage = "* Please Enter No. Of Bathrooms.")]
public Nullable<int> PD_Bathrooms { get; set; }
if (pm.PR_PropertyType=="Flat")
{
if (this.PR_FlatNo == "" || this.PR_FlatNo == null)
{
yield return new ValidationResult("* Flat No. Should Filled", new[] { "PR_FlatNo" });
}
if (this.PR_Floor == 0 || this.PR_Floor == null)
{
yield return new ValidationResult("* Floor No. Should Filled", new[] { "PR_Floor" });
}
}
}
Now in above code when I check using debugger at pm.PR_PropertyType, it returns null. So, How can I access that PR_PropertyType property in PropertyDetails_Master class?
Related
I'm basically trying to use reflection to flatten any class into a dictionary so that I can generically use and bind them in Blazor. I then need to be able to create an instance of the class and populate it with the data from the dictionary (which will have been updated by a component).
e.g
public class Order
{
public Guid Id { get; set; }
public Customer Customer { get; set; }
public string Address { get; set; }
public string Postcode { get; set; }
public List<string> Test { get; set; }
public List<Test> Test2 { get; set; }
}
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName => $"{FirstName} {LastName}";
public Gender Gender { get; set; }
public List<string> Test { get; set; }
}
Should become:
{
"Id": "",
"Customer.FirstName": "",
"Customer.LastName": "",
"Customer.Gender": "",
"Customer.Test": "",
"Address": "",
"Postcode": "",
"Test": "",
"Test2": ""
}
For some reason when I iterate the properties of the Order class, Test2 is missed. The loop shows the property in the collection when I put a breakpoint, it just seems to skip it. I've never seen this happen before.
Code: https://dotnetfiddle.net/g1qyVQ
I also don't think the current code with handle further nested depth which I would like it to be able to work with any POCO object really.
Also if anyone knows a better way to do what I'm trying, I would love to find an easier way. Thanks
First of all, good job on linking the code sample. Without that, I would have passed by this question in about three seconds. :D
In GetAllProperties(), your entire loop is inside a giant try catch block, where the catch returns the dictionary as it is so far, without checking what the exception is. So if you don't get everything you expect, you've probably hit an error.
Amend the catch block:
catch (Exception ex) { Console.WriteLine(ex.ToString()); return result; }
Now, you can see the problem:
System.ArgumentException: An item with the same key has already been added. Key: Test
Your object has more than one property named "Test," but Keys in a Dictionary must be unique.
Summary: Errors aren't the enemy, they're your best friend. Don't use try / catch to bypass errors. If you do, you may get "mysterious, never seen that happen before!" results.
For anyone interested, here is where I'm at now:
https://dotnetfiddle.net/3ORKNs
using JsonFlatten;
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.Json;
namespace RecursiveClassProperties
{
public static class Program
{
static void Main(string[] args)
{
var item = CreateDefaultItem(typeof(Order));
Console.WriteLine(JsonSerializer.Serialize(item, new JsonSerializerOptions { WriteIndented = true }));
var json = JsonSerializer.Serialize(item);
var properties = JObject.Parse(json).Flatten();
Console.WriteLine(JsonSerializer.Serialize(properties, new JsonSerializerOptions { WriteIndented = true }));
var formProperties = properties.ToDictionary(x => x.Key, x => new FormResponse(string.Empty));
Console.WriteLine(JsonSerializer.Serialize(formProperties, new JsonSerializerOptions { WriteIndented = true }));
}
private static object CreateFormItem(Type type, Dictionary<string, FormResponse> formProperties, object result = null)
{
result = CreateDefaultItem(type);
return result;
}
private static object CreateDefaultItem(Type type, object result = null, object nested = null, bool isBase = false)
{
void SetProperty(PropertyInfo property, object instance)
{
if (property.PropertyType == typeof(string)) property.SetValue(instance, string.Empty);
if (property.PropertyType.IsEnum) property.SetValue(instance, 0);
if (property.PropertyType == typeof(Guid)) property.SetValue(instance, Guid.Empty);
}
if (result is null)
{
result = Activator.CreateInstance(type);
isBase = true;
}
var properties = type.GetProperties();
foreach (var property in properties)
{
if (!Attribute.IsDefined(property, typeof(FormIgnoreAttribute)) && property.GetSetMethod() is not null)
{
if (property.PropertyType == typeof(string) || property.PropertyType.IsEnum || property.PropertyType == typeof(Guid))
{
if (isBase) SetProperty(property, result);
else if (nested is not null && nested.GetType() is not IList && !nested.GetType().IsGenericType) SetProperty(property, nested);
}
else
{
var _nested = default(object);
if (isBase)
{
property.SetValue(result, Activator.CreateInstance(property.PropertyType));
_nested = property.GetValue(result);
}
if (nested is not null)
{
property.SetValue(nested, Activator.CreateInstance(property.PropertyType));
_nested = property.GetValue(nested);
}
CreateDefaultItem(property.PropertyType, result, _nested);
}
}
}
return result;
}
}
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
public class FormIgnoreAttribute : Attribute { }
public class FormResponse
{
public FormResponse(string value) => Value = value;
public string Value { get; set; }
}
public class Order
{
public Guid Id { get; set; }
public Customer Customer { get; set; }
public string Address { get; set; }
public string Postcode { get; set; }
public Test Test { get; set; }
public List<Gender> Genders { get; set; }
public List<string> Tests { get; set; }
}
public enum Gender
{
Male,
Female
}
public class Test
{
public string Value { get; set; }
public List<Gender> Genders { get; set; }
public List<string> Tests { get; set; }
}
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName => $"{FirstName} {LastName}";
public Gender Gender { get; set; }
public Test Test { get; set; }
public List<Gender> Genders { get; set; }
public List<string> Tests { get; set; }
}
}
The idea is that I can assign values to formProperties, pass it to CreateFormItem() and get a populated object back. The reason I'm doing this is because I have a Blazor component Table which has a typeparam TItem, basically think of it as Table<TItem> for those unfamiliar with Blazor. The table is then supplied a list of objects which it can then render.
Flattening the object in this way will both allow me to easily display all properties and subproperties of the class in the table, but most importantly bind the input of a "new item" form which will return the new object to a delegate outside of the component (back in normal .NET) to submit to a creation controller (to put it in the DB). The reason having a Dictionary<string, FormResponse> is important is that with a generic type, you aren't able to bind the input of the form to the "model". You are however able to bind the input to a string property of a class, even if it's not a string. Hence FormResponse.Value.
I will next need to have CreateFormItem() return the object with the actual data from the form. Sorry if this is a bit longwinded, couldn't think of a more concise way to explain it.
Thanks :)
I have an enumeration
public enum GTMType
{
[Display(Name = "CHANNEL_CHANNEL")]
ChannelChannel,
[Display(Name = "CHANNEL_WHOLESALE")]
ChannelWholesale,
[Display(Name = "ENTERPRISE_DIRECT")]
EnterpriseDirect,
[Display(Name = "ENTERPRISE_AGENT")]
EnterpriseAgent,
[Display(Name = "ENTERPRISE_SYSTEM_INTEGRATOR")]
EnterpriseSystemIntegrator
}
when I make an API call to another system to get data, The system returns the value which is a display attribute value.
public Account GetDataForAccountByID(string id)
{
AccountModel accountModel = GetDataFromAnotherSystem(id);
//after the call is successfull accountModel looks like
//{Email: "abc#xyz.com",GTMType:"CHANNEL_CHANNEL"}
var account = new Account
{
EmailAddress: = accountModel.Email,
GTMType = accountModel.GTMType
};
}
public class AccountModel
{
public string Email { get; set; }
public string GTMType { get; set; }
}
public class Account
{
public string EmailAddress { get; set; }
public GTMType GTMType { get; set;
}
How can I convert the string value which is of display attribute value can be converted into an enum.
You can use Enum.GetValues to iterate over possible values of the enum and then GetField representing the given value and get its attributes.
GTMType ParseGTMTypeFromAnotherSystem(string gtmType)
{
var type = typeof(GTMType);
foreach (var value in Enum.GetValues(type))
{
var fieldInfo = type.GetField(Enum.GetName(type, value));
DisplayAttribute[] attributes = fieldInfo.GetCustomAttributes(typeof(DisplayAttribute), false) as DisplayAttribute[];
if (attributes.Length != 1)
{
throw new Exception("Enum definition is wrong.");
}
var displayName = attributes[0].Name;
if (gtmType == displayName)
{
return value as GTMType;
}
}
throw new Exception("Unable to parse.");
}
You may want to handle the exception cases differently, e.g. iterate over all DisplayAttributes in case there's more than one and ignore fields that have none, or in case of a failed parse return a default value - depends on your use case.
I have a class Student that contains the list of property 'TextPair' as shown below:
public class Student
{
public List<TextPair> Hobbies { get; set; }
public List<TextPair> Languages { get; set; }
public List<TextPair> Majors { get; set; }
}
public class TextPair
{
[StringLength(2, ErrorMessage = "The value length is invalid")]
public string Value { get; set; }
public string Text { get; set; }
}
Here, I validate the value for maximum length 2 using the StringLength AttributeValidator and decorate in the property 'Value' inside TextPair model.
The problem for me is that the length is always fixed and length is always mandatory.
In my use case, I want the different flavor of Value in different part of the application (or, different property of same type) to support different lengths.
I was looking for something like below where I could pass the validation in my class where I declare my property 'TextPair'
[i.e. I don't want to make the validation mandatory always and also not hard-code the value 2]
public class Student
{
//Any length of the value is accepted for hobbies
public List<TextPair> Hobbies{ get; set; }
[ValuesLength(Length = 2, ErrorMessage = "Language code length must be 2 characters max")]
public List<TextPair> Languages { get; set; }
[ValuesLength(Length = 128, ErrorMessage = "The major should be within 128 characters length")]
public List<TextPair> Majors{ get; set; }
}
Is there any efficient way to approach this solution?
Maybe try subclassing StringLengthAttribute to create your desired ValuesLength attribute. Please note that this code is not tested and is just suggestion to final implementation.
public class ValuesLength : StringLengthAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var isValid = true;
var pair = value as TextPair;
if (pair != null && pair.Value != null)
{
var pairValue = pair.Value;
isValid = pairValue.Length < MaximumLength && pairValue.Length > MinimumLength;
}
return IsValid ? ValidationResult.Success : new ValidationResult(ErrorMessage);
}
}
One of the solution approached is as follows:
public class Student
{
//Any length of the value is accepted for hobbies
public List<TextPair> Hobbies{ get; set; }
[ValuesLength(MaximumLength = 2, ErrorMessage = "Language code length must be 2 characters max")]
public List<TextPair> Languages { get; set; }
[ValuesLength(MaximumLength = 128, ErrorMessage = "The major should be within 128 characters length")]
public List<TextPair> Majors{ get; set; }
}
My Custom Attribute validation is checking the list and verifying if anyone of the element values are exceeding the provided length as:
public class ValuesLengthAttribute : ValidationAttribute
{
public int MaximumLength { get; set; }
public override Boolean IsValid(object value)
{
Boolean isValid = true;
var list = value as List<TextPair>;
if (list != null && list.Count>0)
foreach (var item in list)
{
if (item.Value.Length > MaximumLength)
isValid = false;
}
return isValid;
}
}
I have list of json data i have deserialize in list of class object but if proerties name does not match with json data it takes value null.
Json data come from the url as user dependent user can enter any data then what i will do for validation to handle null when properties not match.
sampleJson list of data is like:
[{"adsname":"Francis","adsimageurl":"Andrew Love.jpg","ontop":false,"key":30012647,"onscan":true,"adscode":6689390,"brandname":{"adsbrand":"Beth Moon"},"category":"New ads","adsscription":"Weinstein Jacob Sutton","from":"2016-12-30T00:00:00","to":"2016-12-30T00:00:00"},{"adsname":"McKay","adsimageurl":"Lorraine Spencer.jpg","ontop":false,"key":136301519,"onscan":true,"adscode":346146503,"brandname":{"adsbrand":"Russell Warner"},"category":"New ads","adsscription":"Stanton Thomas Moran","from":"2016-12-30T00:00:00","to":"2016-12-30T00:00:00"},{"adsname":"Berger","adsimageurl":"Lois Norton.jpg","ontop":false,"key":32971839,"onscan":false,"adscode":334075948,"brandname":{"adsbrand":"Becky Park"},"category":"New ads","adsscription":"Gallagher Matthew Pitts","from":"2016-12-30T00:00:00","to":"2016-12-30T00:00:00"},{"adsname":"Boswell","adsimageurl":"Constance Scarborough.jpg","ontop":false,"key":183877654,"onscan":true,"adscode":230154009,"brandname":{"adsbrand":"Yvonne Hardy"},"category":"New ads","adsscription":"Riddle Nancy Atkins","from":"2016-12-30T00:00:00","to":"2016-12-30T00:00:00"}]
My model propeties class like
public class AdsImportEntity
{
[JsonProperty(PropertyName = "title")]
public string AdsTitle { get; set; }
[JsonProperty(PropertyName = "description")]
public string Description { get; set; }
[JsonProperty(PropertyName = "barcode")]
public string Barcode { get; set; }
[JsonProperty(PropertyName = "top")]
public bool? Top { get; set; }
[JsonProperty(PropertyName = "fromdatetime")]
public System.DateTime? FromDatetime { get; set; }
[JsonProperty(PropertyName = "todatetime")]
public DateTime? ToDatetime { get; set; }
[JsonProperty(PropertyName = "httpimageurl")]
public string HttpImageUrl { get; set; }
}
My Question is If In list of object if object all properties contain null value then remove from list.
To filter in this specific case you could simply apply a little LINQ:
adsImportEntityList = Converter.Deserialize<List<AdsImportEntity>>(adsJson)
.Where(x => !(x.AdsTitle == null
&& x.Description == null
&& ...))
.ToList();
If needed in multiple places the filter could use a help:
static bool NotAllFieldsNull(AdsImportEntity x) {
return !(x.AdsTitle == null
&& x.Description == null
&& ...);
}
adsImportEntityList = Converter.Deserialize<List<AdsImportEntity>>(adsJson)
.Where(NotAllFieldsNull)
.ToList();
If this is needed for a know set of types then overload NotAllFieldsNull. If it needs to work for any (reference) type you'll need reflection.
While I'm sure you could reflect through the properties, I would add another property to the model that checks if there are any values.
e.g.
public bool HasValues
{
get
{
return !string.IsNullOrWhiteSpace(this.AdsTitle) ||
!string.IsNullOrWhiteSpace(this.Description) ||
this.ToDateTime.HasValue ||
... etc ...
}
}
Then when I have my list, I would just remove them with Linq like:
adsImportEntityList = adsImportEntityList.Where((e) => e.HasValues).ToList();
I'm building Backend for Mobile Application with ASP.NET MVC Framework.
I have two Objects:
public class CarLogItem : EntityData
{
public CarLogItem(): base()
{
Time = DateTime.Now;
}
public DateTime Time { get; set; }
public int RPM { get; set; }
public int Speed { get; set; }
public int RunTime { get; set; }
public int Distance { get; set; }
public int Throttle { get; set; }
[ForeignKey("Trip")]
public String Trip_id { get; set; }
// Navigation property
public TripItem Trip { get; set; }
}
and
public class TripItem : EntityData
{
public TripItem() : base()
{
UserId = User.GetUserSid();
StartTime = DateTime.Now;
logItems = new List<CarLogItem>();
}
public string UserId { get; set; }
public List<CarLogItem> logItems {get;set;}
public DateTime StartTime { get; set; }
}
and I have controller, which add new CarLogItem to database.
public class CarLogItemController : TableController<CarLogItem>
{
// POST tables/CarLogItem
public async Task<IHttpActionResult> PostCarLogItem(CarLogItem item)
{
var lastItem = db.CarLogItems.OrderByDescending(x => x.Time).FirstOrDefault();
//lastItem = (Query().Where(logitem => true).OrderBy(logitem => logitem.Time)).Last();
//checking if lastItem.Trip isn't null because
// I have entities with Trip field is null, but all of them should have it.
if (lastItem != null && lastItem.Trip != null && item.RunTime > lastItem.RunTime)
{
item.Trip = lastItem.Trip;
}
//In order to test adding of new TripItem entity to database
// I compare item.RunTime with 120, so it always true
else if (lastItem == null || item.RunTime < 120) // < lastItem.RunTime)
{
var newTrip = new TripItem();
item.Trip = newTrip;
}
else
{
throw new ArgumentException();
}
CarLogItem current = await InsertAsync(item);
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
}
When I'm trying to add new CarLogItem with Trip = null it's ok, but when Trip is particular object it fails with following Exception:
The entity submitted was invalid: Validation error on property 'Id': The Id field is required
How properly to add new CarLogItem with nested TripItem?
I think that you need to populate the Id property on your TripItem, e.g.
var newTrip = new TripItem(){ Id = Guid.NewGuid() }
You need a primary key field in every entity class, like Id or CarLogItemId (ClassName + "Id"). Or just have a property with [Key] attribute:
[Key]
public string/int/Guid/any-db-supported-type MyProp { get; set; }
Entity Framework relies on every entity having a key value that it
uses for tracking entities. One of the conventions that code first
depends on is how it implies which property is the key in each of the
code first classes. That convention is to look for a property named
“Id” or one that combines the class name and “Id”, such as “BlogId”.
The property will map to a primary key column in the database.
Please see this for more details.
I also suspect this to be a problem:
public Lazy<CarLogItem> logItems { get; set; }
You don't have to mark navigation property as Lazy<>. It is already lazy (unless you have configuration that disables lazy loading). Please try to remove Lazy<> and see if it works this way.