I have a method which returns IEnumerable
public static IEnumerable<object> GetProps<T>(T obj)
{
var result = obj.GetType().GetProperties()
.Select(x => new { property = x.Name, value = x.GetValue(obj) })
.Where(x => x.value == null)
.ToList();
return result;
}
Above code will return result as [{"property":"YearOfBirth","value":null}]
I;m now trying to get property valueYearOfBirth from the returned result.
Can someone please suggest/help ?
The type of:
new { property = x.Name, value = x.GetValue(obj) }
is an anonymous type and you can't access fields or properties of that anonymous type outside of the function where it was defined, without using reflection. Here's how you would access its properties using reflection:
foreach (object prop in GetProps(obj))
{
string propertyName = prop.GetType().GetProperty("property").GetValue(prop);
object propertyValue = prop.GetType().GetProperty("value").GetValue(prop);
}
But that's not really the best solution. You don't care about the property value, since you're just returning ones where it's null. So a better solution is IEnumerable<string>:
public static IEnumerable<string> GetProps<T>(T obj)
{
var result = obj.GetType().GetProperties()
.Select(x => new { property = x.Name, value = x.GetValue(obj) })
.Where(x => x.value == null)
.Select(x => x.property)
.ToList();
return result;
}
If you really want to return the property name with its value, I suggest using ValueTuple syntax instead of anonymous types, which will let you access the property and value fields of the ValueTuple (requires C# 7):
public static IEnumerable<(string property, object value)> GetProps<T>(T obj)
{
var result = obj.GetType().GetProperties()
.Select(x => ( x.Name, x.GetValue(obj) ) })
.Where(x => x.Item2 == null)
.ToList();
return result;
}
var yearOfBirth = GetProps(someObject)
.FirstOrDefault(x => x.property == "YearOfBirth")?.value;
Something like that.
You could alternatively just do:
dynamic someObjectDynamic = someObject;
var yearOfBirth = someObjectDynamic.YearOfBirth;
Related
I am trying to have a dynamic Retrieve function where I could pass in a .Select() selector of a custom type.
public IEnumerable<BookableResourceBooking> RetrieveBookingsForWorkOrder(Guid workOrderId, Expression<Func<BookableResourceBooking, dynamic>> selector)
{
return (IEnumerable<BookableResourceBooking>)
CrmServiceContext.BookableResourceBookingSet
.Where(x => x.msdyn_WorkOrder.Id == workOrderId)
.Select(selector);
}
And I call it like this:
.RetrieveBookingsForWorkOrder(
workOrderId,
x => new BookableResourceBooking() { Id = x.Id, StartTime = x.StartTime, EndTime = x.EndTime })
This throws an error:
Microsoft.Xrm.Sdk.InvalidPluginExecutionException: 'Unable to cast object of type 'Microsoft.Xrm.Sdk.Linq.Query`1[System.Object]' to type 'System.Collections.Generic.IEnumerable`1[GiGA.Common.Crm.BookableResourceBooking]'.
If I put the selector there manually, it works:
.Select(x => new BookableResourceBooking() { Id = x.Id, StartTime = x.StartTime, EndTime = x.EndTime }); // this is fine
How should I call the function with the selector in the parameter?
The type dynamic is just object as far as the compiled IL is concerned. It just tells the compiler not to throw errors on methods that aren't found.
So this code:
public IEnumerable<BookableResourceBooking> RetrieveBookingsForWorkOrder(
Guid workOrderId, Expression<Func<BookableResourceBooking, dynamic>> selector)
{
return (IEnumerable<BookableResourceBooking>)
CrmServiceContext.BookableResourceBookingSet
.Where(x => x.msdyn_WorkOrder.Id == workOrderId)
.Select(selector);
}
Is the same as this:
public IEnumerable<BookableResourceBooking> RetrieveBookingsForWorkOrder(
Guid workOrderId, Expression<Func<BookableResourceBooking, object>> selector)
{
return (IEnumerable<BookableResourceBooking>)
CrmServiceContext.BookableResourceBookingSet
.Where(x => x.msdyn_WorkOrder.Id == workOrderId)
.Select(selector);
}
So the .Select(selector) is returning an IEnumerable<object> and you're trying to cast it to (IEnumerable<BookableResourceBooking>).
The question is why is selector defined this way?
I have this viewModel where I like to check the accessGroupList has any value of True and set baccess base on that value. If they are both false it then baccess would be false but if one of them is true baccess would be true.
MemberViewModel result = new MemberViewModel();
result.IsPractices = true;
result.IsUser = false;
var accessGroupList = new List<string>();
accessGroupList.Add("IsUser");
accessGroupList.Add("IsBestPractices");
var baccess = result.GetType().GetProperties().First(o => o.Name == accessGroupList).GetValue(result, null);
bool? baccess = Property as bool?;
I create this simple console project. You can do this, remove comment from where for using in your project
class Program
{
static void Main(string[] args)
{
var cl = new MyClass();
cl._item1 = false;
cl._item2 = false;
var a = cl.GetType().GetProperties()
//.Where(x => accessGroupList.Contains(x.Name))
.Select(x => new
{
name = x.Name,
value = (bool)x.GetValue(cl, null)
})
.Any(x => x.value);
Console.WriteLine(a);
}
}
public class MyClass
{
public bool _item1 { get; set; }
public bool _item2 { get; set; }
}
First of all note that accessGroupList is list and you need to use Contains or Any to compare it with property name. Then you can select the value of those property that appeared in accessGroupList
var baccess = result.GetType().GetProperties()
.Where(o => accessGroupList.Contains(o.Name))
.Select(t=>(bool)t.GetValue(result, null));
var baccess = result.GetType().GetProperties()
.Where(o => accessGroupList.Any(propName => Equals(propName, o.Name))
.Select(x => (bool)x.GetValue(result, null))
.Any(val => val);
Your problem is that you were using .First (which will only return one item) but then in there, you're also comparing the property name to the list itself. You need to do another linq operation to get the appropriate properties out, then you can check if any of those properties have a value of true
How can I write the below code with is T
public IList<IElement> OfClass(Type type)
{
return list
.Where(o => o.GetType() == type)
.ToList();
}
Something like this:
public IList<IEtabsElement> OfClass(....)
{
return list
.Where(o => o is ...)
.ToList();
}
UPDATE
This is my solution, so far. Is it okay?
public IList<IElement> OfClass<T>()
{
return list
.Where(o => o is T)
.ToList();
}
You can create a generic method instead:
public IList<T> OfClass<T>()
{
return list
.Where(o => o.GetType() == typeof(T))
.ToList();
}
This would work, but is the same as the existing method OfType, for example:
var myvehicles = new List<Vehicle> { new Car(), new Bike()};
var mycars = myvehicles.OfType<Car>();
I have a DataGrid in my View with ItemsSource="{Binding GetValues, Mode=TwoWay}".
In my ViewModel:
private List getValues = new List();
public List<Language> GetValues
{
get { return getValues; }
set { SetField("GetValues", ref getValues, value); }
}
In this case, everything is fine:
DatabaseDataContext myDB = new DatabaseDataContext();
var query = myDB.Languages.Where(u => u.Valid == true).ToList();
GetValues = query;
But I want to get in my DataGrid only one column, so tried this:
DatabaseDataContext myDB = new DatabaseDataContext();
var query = myDB.Languages
.Where(u => u.Valid == true)
.Select(u => new { Name = u.Title });
GetValues = query.ToList();
"Cannot implicitly convert type
'System.Collections.Generic.List<>' to
'System.Collections.Generic.List'"
Is there a good way to solve this?
You get the error because the type of GetValues is List<Language>, but you try to set it to dynamically created anonymous type. To solve the problem you should create a new class which represents your needed data.
public class LanguageTitle
{
public string Name { get; set; }
// Other needed properties
}
Then add property to your view model:
public List<LanguageTitle> LanguageTitles
{
get { return languageTitles; }
set { SetField("GetLanguageTitles", ref languageTitles, value); }
}
In your query select LanguageTitle:
DatabaseDataContext myDB = new DatabaseDataContext();
var query = myDB.Languages
.Where(u => u.Valid == true)
.Select(u => new LanguageTitle { Name = u.Title });
LanguageTitles = query.ToList();
And bind your DataGrid to this property:
ItemsSource="{Binding LanguageTitles, Mode=TwoWay}"
Why would have a object that have:
public List<Language> GetValues
{
get { return getValues; }
set { SetField("GetValues", ref getValues, value); }
}
but you don't want all the properties filled from that object. It is a bad practise so re-use object because their almost simular.
You might want to change your property GetValues into
public List<string> GetValues {get;set;}
Then in your linq-query you can use:
GetValues = myDB.Languages
.Where(u => u.Valid == true)
.Select(u => u.Title)
.ToList();
Problem is that the query returns a anonymous object and you try to assign this list of anonymous object to a list of Language type.
Solution Change your Linq query to select a object of type Language rather than anonymous object.
This must help
var query = myDB.Languages
.Where(u => u.Valid == true)
.Select(u => new Language { Title = u.Title });
Then when you do a .ToList() on it the result and the data type of GetValues will match.
public struct CardGrouping
{
public string Name { get; set; }
public int Count { get; set; }
}
public List<CardGrouping> GetCardGrouping(IQueryable<Areas.RetailShop.Models.FPSinformation> queryable, Expression<Func<Areas.RetailShop.Models.FPSinformation, string>> groupingFunction)
{
return queryable.GroupBy(groupingFunction)
.Where(x => x.Key != null)
.Select(x => new CardGrouping
{
Name = x.Key,
Count = x.Sum(groupingFunction)
}).ToList();
}
I'm trying to do something like this but getting an Error
IQueryable<FPSinformation> does not contain a definition for 'GroupBy'
and the best extension method overload
ParallelEnumerable.GroupBy<string, int>(ParallelQuery<string>,
Func<string, int>) requires a receiver of type ParallelQuery<string>
What I'm doing wrong?
EDIT
var data1 = fpslist.GroupBy(x => x.Ration_Card_Type1)
.Select(x => new
{
CardType_Name = x.Key,
CardType_Count = x.Sum(y => y.Ration_Card_Count1)
}).ToList();
This is the actual code which I'm trying to optimise
Change string to Areas.RetailShop.Models.FPSinformation in fun
public List<CardGrouping> GetCardGrouping(List<Areas.RetailShop.Models.FPSinformation> queryable,
Expression<Func<Areas.RetailShop.Models.FPSinformation, string>> groupingFunction,
Func<Areas.RetailShop.Models.FPSinformation, int> sumFunction)
{
if (queryable.AsQueryable() != null)
{
var data = queryable.AsQueryable().GroupBy(groupingFunction).Where(x => x.Key != null).Select(x => new CardGrouping
{
Name = x.Key == null ? "" : x.Key.ToString(),
Count = x.Sum(sumFunction)
}).ToList();
return data;
}
return null;
}
There are 2 problems with this code.
First, to make it compile, the groupingFunction should be a Func<FPSinformation, int> - the type of input is not string, it's FPSinformation.
This change will make it compile, but the compiler will choose the Enumerable.GroupBy extension method. The Queryable.GroupBy requires an Expression<Func> parameter, not a Func - so it should be Expression<Func<FPSinformation, int>>
public List<CardGrouping> GetCardGrouping(IQueryable<FPSinformation> queryable,
Expression<Func<FPSinformation, int>> groupingFunction)
You're grouping it by an int, so the .Where(x => x.Key != null) doesn't make sense - x.Key cannot be null.