My Model, example:
public class Obj
{
public Obj()
{ }
public int? IdRestricao { get; set; }
public int? IdTipoRestringido { get; set; }
public string CodRestringido { get; set; }
public string NomeRestringido { get; set; }
public int? IdTipoRestricao { get; set; }
public string CodRestricao { get; set; }
public string NomeRestricao { get; set; }
public DateTime? PeriodoInicio { get; set; }
public DateTime? PeriodoFim { get; set; }
public int? IdStatus { get; set; }
}
Request
www.url.com.br?IdRestricao=1&IdTipoRestringido=2&CodRestringido=3&NomeRestringido=4&IdTipoRestricao=5&CodRestricao=6&NomeRestricao=7&PeriodoInicio=8&PeriodoFim=9
code:
var model = obj.*tostring()*//
something like
var request = new RestRequest($"/api/{**model**}", Method.GET);
edit1:
I'm wondering if there is a library or something that transforms my object in my request as in the examples above
You can override ToString to format the data from the Obj class into GET parameters.
You could write a utility function to convert all object properties into nameof and the associated value into key-value pairs.
Something such as this answer to convert your Obj into a dictionary, and then another method to convert the dictionary into Prop=value could be done.
For a one off something like this would be suitable:
public override string ToString()
{
return $"IdRestricao={IdRestricao}&IdTipoRestringido={IdTipoRestringido}&CodRestringido={CodRestringido}...
}
I found the answer to what I was looking for, it follows below in case anyone needs it in the future
private static string ToQueryString(this object request, string separator = ",")
{
if (request == null)
throw new ArgumentNullException("request");
// Get all properties on the object
var properties = request.GetType().GetProperties()
.Where(x => x.CanRead)
.Where(x => x.GetValue(request, null) != null)
.ToDictionary(x => x.Name, x => x.GetValue(request, null));
// Get names for all IEnumerable properties (excl. string)
var propertyNames = properties
.Where(x => !(x.Value is string) && x.Value is IEnumerable)
.Select(x => x.Key)
.ToList();
// Concat all IEnumerable properties into a comma separated string
foreach (var key in propertyNames)
{
var valueType = properties[key].GetType();
var valueElemType = valueType.IsGenericType
? valueType.GetGenericArguments()[0]
: valueType.GetElementType();
if (valueElemType.IsPrimitive || valueElemType == typeof(string))
{
var enumerable = properties[key] as IEnumerable;
properties[key] = string.Join(separator, enumerable.Cast<object>());
}
}
// Concat all key/value pairs into a string separated by ampersand
return string.Join("&", properties
.Select(x => string.Concat(
Uri.EscapeDataString(x.Key), "=",
Uri.EscapeDataString(x.Value.ToString()))));
}
Related
I have an object which contains few int/string properties and some List<T> properties where T are some other classes in the project itself. Is there a cleaner way to determine if only those List<T> properties are empty or null? Maybe using a Linq statement?
I tried searching about it but can't find a short and clean way. Should i opt for reflection?? Can someone provide a sample related to this?
public class A
{
..some properties..
List<ClassA> ListA { get; set; }
List<ClassB> ListB { get; set; }
List<ClassC> ListC { get; set; }
List<ClassD> ListD { get; set; }
..some properties..
}
EDIT 1:
So far i have managed to write a clean code to check if list properties are null. But how can i check if they are empty. I need to convert object to List but i dont know the type of List it is
var matchFound = myObject.GetType().GetProperties()
.Where(x => x.PropertyType == typeof(List<>))
.Select(x => x.GetValue(myObject))
.Any(x => x != null);
EDIT 2:
I ended up using this, a one liner that works fine:
var matchFound = myObject.GetType().GetProperties()
.Where(x =>(x.GetValue(myObject) as IList)?.Count()>0);
Here is what i would do.
/// <summary>
/// caching a Dyctionary of IList types for faster browsing
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private static readonly Dictionary<Type, Type> CachedActualType = new Dictionary<Type, Type>();
// Get Internal type of IList.
// When the type is not a list then it will return the same type.
// if type is List<T> it will return the type of T
public static Type GetActualType(this Type type)
{
if (CachedActualType.ContainsKey(type))
return CachedActualType[type];
if (type.GetTypeInfo().IsArray)
CachedActualType.Add(type, type.GetElementType());
else if (type.GenericTypeArguments.Any())
CachedActualType.Add(type, type.GenericTypeArguments.First());// this is almost always find the right type of an IList but if it fail then do the below. dont really remember why this fail sometimes.
else if (type.FullName?.Contains("List`1") ?? false)
CachedActualType.Add(type, type.GetRuntimeProperty("Item").PropertyType);
else
CachedActualType.Add(type, type);
return CachedActualType[type];
}
And then
var matchFound = myObject.GetType().GetProperties()
.Where(x => x.PropertyType.GetActualType() != x.PropertyType &&
(x.GetValue(myObject) as IList)?.Count()>0);
You can actually do even better and dont need to check for type and only try to cast the value.
The value will always be null if the type is not an IList
var matchFound = myObject.GetType().GetProperties()
.Where(x =>(x.GetValue(myObject) as IList)?.Count()>0);
You can use reflection for your requirement, i have just tried it.
class Test
{
}
class UserDetails
{
public List<Test> Test1 { get; set; }
public List<Test> Test2 { get; set; }
public string firstname { get; set; }
public string surname { get; set; }
public string city { get; set; }
public string state { get; set; }
}
Use this query to search, you can customize where condition for your requirement
UserDetails yourObject = new UserDetails();
yourObject.Test1 = new List<Test> { new Test() };
var result = typeof(UserDetails).GetProperties()
.Select(prop => prop)
.Where(property =>
{
if (property.PropertyType == typeof(List<Test>))
{
var value = (List<Test>)property.GetValue(yourObject, null);
return value == null || value.Count == 0;
}
return false;
}).ToList(); // this will return 1 because 1 property has count > 1
Update if use Templete
class UserDetails<T>
{
public List<T> Test1 { get; set; }
public List<T> Test2 { get; set; }
public string firstname { get; set; }
public string surname { get; set; }
public string city { get; set; }
public string state { get; set; }
}
Query
UserDetails<Test> yourObject = new UserDetails<Test>();
yourObject.Test1 = new List<Test> { new Test() };
var result = typeof(UserDetails<Test>).GetProperties()
.Select(prop => prop)
.Where(property =>
{
if (property.PropertyType == typeof(List<Test>))
{
var value = (List<Test>)property.GetValue(yourObject, null);
return value == null || value.Count == 0;
}
return false;
}).ToList();
You need quite a bit of reflection:
// the type to test
public class TestData
{
public string A { get; set; }
public List<string> B { get; set; }
public List<int> C { get; set; }
}
// an helper class used to generate checking functions
public static class ListTester
{
public static Func<T, bool> MakeClassChecker<T>()
where T : class
{
var checkFunctions = EnumerateListProperties<T>()
.Select(MakePropertyChecker<T>)
.ToList();
return instance => checkFunctions.All(f => f(instance));
}
public static IEnumerable<PropertyInfo> EnumerateListProperties<T>()
{
return typeof(T).GetProperties(Instance | Public | NonPublic)
.Where(prop => IsListClosedType(prop.PropertyType));
}
public static Func<T, bool> MakePropertyChecker<T>(PropertyInfo prop)
where T : class
{
var propType = prop.PropertyType;
var listItemType = propType.GenericTypeArguments[0];
var listEmptyChecker = (Func<object, bool>) ListCheckerFactoryMethod
.MakeGenericMethod(listItemType).Invoke(null, new object[0]);
return instance => instance != null && listEmptyChecker(prop.GetValue(instance));
}
private static MethodInfo ListCheckerFactoryMethod
= typeof(ListTester).GetMethod(nameof(ListCheckerFactory), Static | Public);
public static Func<object, bool> ListCheckerFactory<T>()
{
return list => list == null || ((List<T>) list).Count == 0;
}
public static bool IsListClosedType(Type type)
{
return type != null &&
type.IsConstructedGenericType &&
type.GetGenericTypeDefinition() == typeof(List<>);
}
}
[Test]
public void TestTemp()
{
var props = ListTester.EnumerateListProperties<TestData>();
CollectionAssert.AreEquivalent(props.Select(prop => prop.Name), new[] {"B", "C"});
var allListsAreNullOrEmpty = ListTester.MakeClassChecker<TestData>();
Assert.That(allListsAreNullOrEmpty(new TestData()), Is.True);
Assert.That(allListsAreNullOrEmpty(new TestData() {B = new List<string>()}), Is.True);
Assert.That(allListsAreNullOrEmpty(new TestData() {B = new List<string>() {"A"}}), Is.False);
}
Now, for the important bits: you search for properties of closed generic types of List<>.
The selection of the properties is done in IsListClosedType.
Then, for each property, we make a checking function using MakePropertyChecker.
The job of MakePropertyChecker is to build via MakeGenericMethod a version of ListCheckerFactory
of the appropriate type.
You want to check all properites that are of type List<something>
This method can do the trick:
bool IsGenericList(Type t)
{
return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>);
}
Now you can modify your Linq query so it returns if at least one of List members is not null or empty
var matchFound = myObject.GetType().GetProperties()
.Where(p => IsGenericList(p.PropertyType))
.Select(p => p.GetValue(myObject) as IEnumerable)
.Any(list => list != null && list.Cast<object>().Any());//Cast<object> needed to be able to use Linq Any()
I have a simple web service calling a sql view table through entity framework. I can bring all the columns in string fine but not the column in numeric like UID_NUM(numeric(38,8), null) in SQL. I have AddressALL class to set columns like below and error out at p.UID_NUM in LINQ.
public class GISAddressWebService : System.Web.Services.WebService
{
[WebMethod]
public AddressALL[] getAddress()
{
try
{
List<view_COBADDRESS> address = new List<view_COBADDRESS>();
using (GISAddressEntities database = new GISAddressEntities())
{
return database.view_COBADDRESS
.Where(p => p.UNIT_NUM == "103")
.Select(p => new AddressALL { UID_NUM = p.UID_NUM, ADD_FULL = p.ADD_FULL, POSTALCITY = p.POSTALCITY, ZIP5 = p.ZIP5}).ToArray();
}
}
catch (Exception)
{
return null;
}
}
}
public class AddressALL
{
public double UID_NUM { get; set; }
public string TLID { get; set; }
public string ADD_FULL { get; set; }
public string POSTALCITY { get; set; }
public string STATE { get; set; }
public string ZIP5 { get; set; }
public string IN_OUT { get; set; }
}
The decimal has more significant figures than the double, therefore it can be more precise and it also takes up slightly more memory. Because of this difference fou must explicitly program this change of type through (double)p.UID_NUM.
return database.view_COBADDRESS
.Where(p => p.UNIT_NUM == "103")
.Select(p => new AddressALL { UID_NUM = System.Convert.ToDouble(p.UID_NUM), ADD_FULL = p.ADD_FULL, POSTALCITY = p.POSTALCITY, ZIP5 = p.ZIP5}).ToArray();
MSDN
The obvious solution, instead of
.Select(p => new AddressALL
{
UID_NUM = p.UID_NUM,
ADD_FULL = p.ADD_FULL,
POSTALCITY = p.POSTALCITY,
ZIP5 = p.ZIP5
});
write
.Select(p => new AddressALL
{
UID_NUM = Convert.ToDouble(p.UID_NUM),
ADD_FULL = p.ADD_FULL,
POSTALCITY = p.POSTALCITY,
ZIP5 = p.ZIP5
});
In your select statement .Select(p => new AddressALL{ ... }) you are doing a projection that is trying to pick a new object of type AddressALL for each p, and you are using the object initializer syntax {...} to match the properties of your source objects p with the properties of your target type AddressALL.
Your error message however suggests your p.UID_NUM is of type decimal, while the UID_NUM property on your AddressALL is of type double. Therefore you have to convert the values to the necessary target type.
i am having some difficulties creating a proper select.
i have my custom class:
internal class classA{
internal string FieldName { get; set; }
internal string FieldValue { get; set; }
internal bool IncludeInChecksum { get; set; }
}
what i am trying to do is to build and concatinate a querystring using the class list from above.
for the following object:
List<classA> requestParams = ...
the query string should look like:
a=1&b=2&c=3
ordered by FieldName ascending
where IncludeInChecksum = true
FieldName = FieldValue
preMD5= requestParams
.Where(x=>x.IncludeInChecksum)
.OrderBy(y=>y.FieldName)
.Aggregate((i,j)=>(i.FieldName+ "=" +j.FieldValue));
this is where i am stuck.
thanks in advance
I will try to get all name=value strings with the help of the Select() method. And then convert the result to array. After that just use String.Join() method to get the desired result.
Join(String, String[]) concatenates all the elements of a string array, using the specified separator between each element.
var preMD5= requestParams
.Where(x => x.IncludeInChecksum)
.OrderBy(y => y.FieldName)
.Select(z => string.Format("{0}={1}", z.FieldName, z.FieldValue))
.ToArray();
preMD5 = string.Join("&", preMD5);
Aggregate aggregates values from different rows. You need to combine values from different fields. For this you use Select:
requestParms.Where(...).OrderBy(...).Select(f=>f.FieldName+"="+f.FieldValue)
This will return an IEnumerable of name=value strings. You can use string.Join to combine them into one string.
I know it's been answered,
but still I wanted to provide my solution.
I used a dedicated method to build the query string parameter,
and an extension method to concat it all.
Hope this helps.
public class classA
{
internal string FieldName { get; set; }
internal string FieldValue { get; set; }
internal bool IncludeInChecksum { get; set; }
public classA(string fieldName, string fieldValue, bool includeInChecksum)
{
this.FieldName = fieldName;
this.FieldValue = fieldValue;
this.IncludeInChecksum = includeInChecksum;
}
public string ToQueryString()
{
return string.Format("{0}={1}",
this.FieldName,
this.FieldValue);
}
public void Test()
{
var list = new List<classA> {
new classA("A", "1", true) ,
new classA("D", "4", true) ,
new classA("B", "2", false) ,
new classA("C", "3", true)
};
var result = list.
Where(o => o.IncludeInChecksum).
OrderBy(o => o.FieldName).
Select(o => o.ToQueryString()).
ToStringEx("&");
}
}
public static class ExtensionMethods
{
public static string ToStringEx<T>(this IEnumerable<T> items, string separetor = ",")
{
if (items == null)
{
return "null";
}
return string.Join(separetor, items.Select(o => o != null ? o.ToString() : "[null]").ToArray());
}
}
I have my entities defined as follows,
[Table("AuditZone")]
public class AuditZone
{
public AuditZone()
{
AuditZoneUploadedCOESDetails = new List<UploadedCOESDetails>();
AuditZonePostcode = new List<Postcodes>();
}
[Key]
[DoNotAudit]
public int Id { get; set; }
public string Description { get; set; }
public bool Valid { get; set; }
[DoNotAudit]
public DateTime CreatedDate { get; set; }
[DoNotAudit]
public int? CreatedBy { get; set; }
[DoNotAudit]
public DateTime? ModifiedDate { get; set; }
[DoNotAudit]
public int? ModifiedBy { get; set; }
public virtual UserProfile CreatedByUser { get; set; }
public virtual UserProfile ModifiedByUser { get; set; }
public virtual ICollection<UploadedCOESDetails> AuditZoneUploadedCOESDetails { get; set; }
public virtual ICollection<Postcodes> AuditZonePostcode { get; set; }
}
}
Some of the fields have an attribute called DoNotAudit.
I need to be able to get a list of the fields in the table that do not have that DoNotAudit attribute.
I tried the following, but it doesn't work. Any ideas ?
public IEnumerable<string> GetFields(string tableName)
{
var table = typeof(AISDbContext).GetProperties().Select(n => n.PropertyType) // here we get all properties of contect class
.Where(n => n.Name.Contains("DbSet") && n.IsGenericType) // here we select only DBSet collections
.Select(n => n.GetGenericArguments()[0])
.Where(n => n.Name == tableName);
var doNotAuditList = table.GetType()
.GetProperties()
.Where(p => p.GetCustomAttributes(typeof(DoNotAudit), false).Any())
.Select(p => p.Name)
.ToList();
return doNotAuditList;
}
Updated query
var doNotAuditList = table.First().GetProperties()
.Where(p=> p.PropertyType.FindInterfaces(new TypeFilter((t,o) => t == typeof(IEnumerable)), null).Length == 0)
.Where(n => n.GetCustomAttributes(true).OfType<DoNotAudit>().FirstOrDefault() == null)
.Select(p => p.Name)
.ToList();
table is already the type you need, so table.GetType() is not correct. Just use table.GetProperties() as below:
var doNotAuditList = table.First()
.GetProperties()
.Where(p => p.GetCustomAttributes(typeof(DoNotAudit), false).Any() == false
&& p.GetGetMethod().IsVirtual == false)
.Select(p => p.Name)
.ToList();
EDIT:
table is of type IQueriable<Type>, so a call to .First() is needed to get the actual object.
EDIT
Updated Linq query to ignore all virtual properties and properties marked DoNotAudit.
Reflection of the Type typeof(Poco).GetProperties() can lead to different results to what is actually tracked in EF. EF will ignore some types, some types may have Ignore annotations.
Complex Types need special attention.
If you want the EF view of the model, then access the MetadataWorkspace.
Some basic dump Metadata to debug console code...
[TestMethod]
public void EFToolsTest() {
// http://msdn.microsoft.com/en-us/library/system.data.metadata.edm.dataspace(v=vs.110).aspx
var context = new YourContext(); // DbContext type
ObjectContext objContext = ((IObjectContextAdapter)context).ObjectContext;
MetadataWorkspace workspace = objContext.MetadataWorkspace;
var xyz = workspace.GetItems<EntityType>(DataSpace.SSpace);
foreach (var ET in xyz) {
foreach (var sp in ET.Properties) {
Debug.WriteLine(sp.Name + ":" + sp.MaxLength);// just as an example
}
}
}
or via the DataSpace.OSpace
public static void DumpContextManagedTypeProps() {
var context = new YourContent();
ObjectContext objContext = ((IObjectContextAdapter)context).ObjectContext;
MetadataWorkspace workspace = objContext.MetadataWorkspace;
IEnumerable<EntityType> managedTypes = workspace.GetItems<EntityType>(DataSpace.OSpace);
foreach (var managedType in managedTypes.Where(mt=>mt.Ful) {
Console.WriteLine(managedType.FullName);
// propertyInfo and other useful info is available....
foreach ( var p in managedType.Properties) {
Console.WriteLine(p.Name );
}
}
return result;
}
I am getting a error in the line below.
temp.day1_veh_p = string.Join(Environment.NewLine, day1.Where(x => x.plannedTriips == 1).Select(x => new {value=x.vehicleNumber+":"+x.shiftCompletedOn }).Cast<string>().ToArray());
Th error Message being
Unable to cast object of type '<>f__AnonymousType0`1[System.String]' to type 'System.String'.
The list day1 is of type
public class tripDetails
{
public string accountID { get; set; }
public string supplierName { get; set; }
public string supplierCode { get; set; }
public DateTime shiftFrom { get; set; }
public DateTime shiftTo { get; set; }
public int plannedTriips { get; set; }
public int actualTrips { get; set; }
public DateTime forDate { get; set; }
public string vehicleNumber { get; set; }
public string shiftCompletedOn { get; set; }
public class Comparer : IEqualityComparer<tripDetails>
{
public bool Equals(tripDetails x, tripDetails y)
{
return x.supplierCode == y.supplierCode;
}
public int GetHashCode(tripDetails obj)
{
return (obj.supplierCode).GetHashCode();
}
}
}
What exactly Am i doing wrong??
The problem is the new { value = ... }
Replace:
Select(x => new {value=x.vehicleNumber+":"+x.shiftCompletedOn }).Cast<string>()
with
Select(x => x.vehicleNumber+":"+x.shiftCompletedOn)
and you're sorted. You won't need the Cast<string>() at all.
Your original code creates, for each record, a new instance of an anonymous type that has a member called value with the string desired; the second version just creates the string.
In a way, it is no different to trying this:
class Foo
{
public string Bar {get;set;}
}
...
var foo = new Foo { Bar = "abc" };
string s = (string)foo; // doesn't compile
Yes, an anonymous type is not a string, so replace this
.Select(x => new { value = x.vehicleNumber + ":" + x.shiftCompletedOn })
with
.Select(x => x.vehicleNumber + ":" + x.shiftCompletedOn)
Then you can use the query(you don't need to create a new array) for string.Join.
It's also helpful to use multiple lines, it makes your code much more readable:
var vehicles = day1.Where(x => x.plannedTriips == 1)
.Select(x => x.vehicleNumber + ":" + x.shiftCompletedOn);
string str = string.Join(Environment.NewLine, vehicles);
Replace this
(x => new {value=x.vehicleNumber+":"+x.shiftCompletedOn }).Cast<string>()
by this
(x => String.Format("{0}\:{1}", x.vehicleNumber, x.shiftCompletedOn))
When you are doing new { ... } you are creating items of anonymous type and then (Cast<string()) trying explicitly cast to string and such conversion is not defined - youn ends up with appropriate exception.