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.
Related
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;
I have a method on the back end, that gets values related to a foreign key of the table.
Those foreign keys can be nullable, but one of those keys always will have value.
Here is method
public async Task<ListResultDto<QuoteListDto>> GeQuotesTabData(int? landlordId, int? agentId,
int? propertyTenantId)
{
if (landlordId.HasValue)
{
var query = _quoteRepository.GetAll()
.Where(x => x.LandlordId == landlordId);
}
if (agentId.HasValue)
{
var query = _quoteRepository.GetAll()
.Where(x => x.AgentId == agentId);
}
if (propertyTenantId.HasValue)
{
var query = _quoteRepository.GetAll()
.Where(x => x.PropertyTenantId == propertyTenantId);
}
return new ListResultDto<QuoteListDto>(await query.ProjectTo<QuoteListDto>(ObjectMapper)
.OrderBy(x => x.Id).ToListAsync());
}
At this row, I get an error Cannot resolve symbol query
return new ListResultDto<QuoteListDto>(await query.ProjectTo<QuoteListDto>(ObjectMapper)
.OrderBy(x => x.Id).ToListAsync());
How do I need to rewrite my method?
Declare and initialise your variable. Additionally I would re-write you method like so:
public async Task<ListResultDto<QuoteListDto>> GeQuotesTabData(int? landlordId, int? agentId,
int? propertyTenantId)
{
var query = _quoteRepository.GetAll();
if (landlordId.HasValue)
{
query = query.Where(x => x.LandlordId == landlordId);
}
if (agentId.HasValue)
{
query = query.Where(x => x.AgentId == agentId);
}
if (propertyTenantId.HasValue)
{
query = query .Where(x => x.PropertyTenantId == propertyTenantId);
}
return new ListResultDto<QuoteListDto>(await query.ProjectTo<QuoteListDto>(ObjectMapper)
.OrderBy(x => x.Id).ToListAsync());
}
Also taken from this answer, you can create a WhereIf extension to clean up the if statements.
public static IQueryable<TSource> WhereIf<TSource>(
this IQueryable<TSource> source,
bool condition,
Expression<Func<TSource, bool>> predicate)
{
if (condition)
return source.Where(predicate);
else
return source;
}
Making your code look like this:
public async Task<ListResultDto<QuoteListDto>> GeQuotesTabData(int? landlordId, int? agentId,
int? propertyTenantId)
{
var list = await _quoteRepository.GetAll()
.WhereIf(landlordId.HasValue, x => x.LandlordId == landlordId)
.WhereIf(agentId.HasValue, x => x.AgentId == agentId)
.WhereIf(propertyTenantId.HasValue, x => x.PropertyTenantId == propertyTenantId)
.ProjectTo<QuoteListDto>(ObjectMapper)
.OrderBy(x => x.Id)
.ToListAsync();
return new ListResultDto<QuoteListDto>(list);
}
Your problem is variable scope. When you define a variable it is only visible in the scope you define it in.
You define three different query variables in a local scope. None of them are accessible where you try to use it.
You need to define it before using it, something like this:
public async Task<ListResultDto<QuoteListDto>> GeQuotesTabData(int? landlordId, int? agentId,
int? propertyTenantId)
{
IQueryable<Quote> query = null;
if (landlordId.HasValue)
{
query = _quoteRepository.GetAll().Where(x => x.LandlordId == landlordId);
}
if (agentId.HasValue)
{
query = _quoteRepository.GetAll().Where(x => x.AgentId == agentId);
}
if (propertyTenantId.HasValue)
{
query = _quoteRepository.GetAll().Where(x => x.PropertyTenantId == propertyTenantId);
}
return new ListResultDto<QuoteListDto>(await query.ProjectTo<QuoteListDto>(ObjectMapper)
.OrderBy(x => x.Id).ToListAsync());
}
Of course all of your queries should be of the same type. Otherwise you will have to define and execute them in the local scopes.
You should probably also add some error handling of the case where query is null, when you try to use it.
I'm trying to take a method and make it generic, and I'm a little stuck because the method uses Linq to look at elements. Here's the example method:
private List<int> GetListFromIDS(string ids, IEnumerable<SubSpace_Function> data)
{
if (string.IsNullOrEmpty(ids))
return null;
var list = ids
.Split(new char[] { ',' })
.Where(x => !string.IsNullOrWhiteSpace(x))
.Select(x => int.Parse(x.Trim()));
return data
.Where(x => list.Contains(x.Function_Id)))
.Select(x => x.Function_Id)
.ToList();
}
The parts that change are the type (SubSpace_Function) and the property to lookup Function_ID.
I know I can just change the SubSpace_Function part to T in the generic method signature, but since each type will have it's own property to lookup, I'm not sure how to 'pass' in something like Function_Id.
It's pretty easy to do with Func:
private List<int> GetListFromIDS<T>(string ids, IEnumerable<T> data, Func<T, IEnumerable<int>, bool> filterExpression, Func<T, int> selectExpression)
{
if (string.IsNullOrEmpty(ids))
return null;
var list = ids
.Split(',') // simplify
.Where(x => !string.IsNullOrWhiteSpace(x))
.Select(x => int.Parse(x.Trim()));
return data
.Where(x => filterExpression(x, list))
.Select(selectExpression)
.ToList();
}
And call using:
var data = GetListFromIDS<SubSpace_Function>(
"123,123,123",
someList,
(x, list) => list.Contains(x.Function_Id),
x => x.Function_Id);
Another way is to call the select Func inline:
private List<int> GetListFromIDS<T>(string ids, IEnumerable<T> data, Func<T, int> selectExpression)
{
if (string.IsNullOrEmpty(ids))
return null;
var list = ids
.Split(',') // simplify
.Where(x => !string.IsNullOrWhiteSpace(x))
.Select(x => int.Parse(x.Trim()));
return data
.Where(x => list.Contains(selectExpression(x)))
.Select(selectExpression)
.ToList();
}
And call using:
var data = GetListFromIDS<SubSpace_Function>(
"123,123,123",
someList,
x => x.Function_Id);
I know this focused on generics, but I took the approach of using an interface instead:
interface ISubSpaceFunction
{
int FunctionId { get; }
}
class Foo : ISubSpaceFunction
{
public int FunctionId => FooMethodForFunctionId();
private int FooMethodForFunctionId()
{
//do foo function id stuff
throw new NotImplementedException();//so it compiles
}
}
class Bar : ISubSpaceFunction
{
public int FunctionId => BarMethodForFunctionId();
private int BarMethodForFunctionId()
{
//do bar function id stuff
throw new NotImplementedException();//so it compiles
}
}
static class MyClass
{
private static List<int> GetListFromIds(string idsString, IEnumerable<ISubSpaceFunction> subSpaceFunctions)
{
var ids = string.IsNullOrWhiteSpace(idsString) ?
Enumerable.Empty<int>() :
idsString.Split(new[] { ',' })
.Where(x => !string.IsNullOrWhiteSpace(x))
.Select(x => x.Trim())
.Select(int.Parse);
var idSet = new HashSet<int>(ids);
return subSpaceFunctions.Select(ssf => ssf.FunctionId)
.Where(ids.Contains)
.ToList();
}
}
class Example
{
public void Test()
{
string ids = "1, 2, 3, 4, 5";
var subSpaceFunctions = new ISubSpaceFunction[] { new Foo(), new Bar() };
var results = MyClass.GetListFromIds(ids, subSpaceFunctions);
}
}
My attitude on this and related matters is that the code to get the Property value for each particular type has to go somewhere, so it might as well go in the Type's class. This ensures that if the Property's value is needed elsewhere, there is no duplication. This also allows for mixing multiple types that satisfy ISubSpaceFunction, as is done in the example, and you could easily have the interface also specify some common method to be used elsewhere.
I also prefer returning empty collections over null when writing these kinds of LINQ based transformation methods in order to minimize null checking "down the pipeline," but a "fail fast" use case may call for a null return value.
I have method like this :
GetUsallyOpeningClosingHour(Func<OpeningDay, TimeSpan> groupByRule)
{
var openingClosingHours = listOfSpecificDayOfWeek.GroupBy(groupByRule).OrderByDescending(x => x.Key);
}
and the problem is that I can't stick all the time with OrderByDescending depends on groupByRule parameter sometimes it has to be orderByDescending or OrderBy
I don't want to depend on this parameter, so I could pass another one for that,
Right now I call my method this way:
GetUsallyOpeningClosingHour(x => x.From)
or
GetUsallyOpeningClosingHour(x => x.To)
How can I pass orderBy type as well ?
The simplest way is adding a parameter, which will specify an order in your collection.
public void GetUsallyOpeningClosingHour(
Func<OpeningDay, TimeSpan> groupByRule,
bool orderByDesc = false)
{
var groupedDays = listOfSpecificDayOfWeek.GroupBy(groupByRule);
var openingClosingHours =
orderByDesc
? groupedDays.OrderByDescending(x => x.Key)
: groupedDays.OrderBy(x => x.Key);
}
It could be a boolean or custom enum (I prefer enum, because it actually specifies a kind of ordering operation, while boolean specifies whether collection should be ordered by desc or not).
public enum OrderingType
{
Ascending,
Descending,
None
}
Or you could provide an additional Func, which will perform an ordering operation. But its signature will be awkward.
public static void GetUsallyOpeningClosingHour(
Func<OpeningDay, TimeSpan> groupByRule,
Func<IEnumerable<IGrouping<TimeSpan, OpeningDay>>,
IEnumerable<IGrouping<TimeSpan, OpeningDay>>> orderBy)
{
var groupedDays = listOfSpecificDayOfWeek.GroupBy(groupByRule);
var openingClosingHours = orderBy(groupedDays);
}
I guess you could create your own OrderBy extension that let you select ascending/descending based on a parameter.
Something like this:
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
bool descending
)
{
return descending ? source.OrderByDescending(keySelector)
: source.OrderBy(keySelector);
}
You can also use an enum instead of the boolean to make things more readable when calling this method.
This is the most direct way to parameterise for OrderBy and OrderByDescending. Fortunately the type can be inferred for you by Visual Studio; unfortunately the type is long to write out. I added the static void and the initializer for listOfSpecificDayOfWeek so that this is easy to paste into a program for testing.
static void GetUsallyOpeningClosingHour(
Func<OpeningDay, TimeSpan> groupByRule,
Func<IEnumerable<IGrouping<TimeSpan, OpeningDay>>,
Func<IGrouping<TimeSpan, OpeningDay>, TimeSpan>,
IOrderedEnumerable<IGrouping<TimeSpan, OpeningDay>>> order)
{
IEnumerable<OpeningDay> listOfSpecificDayOfWeek = null;
var openingClosingHours = order(listOfSpecificDayOfWeek.GroupBy(groupByRule), x => x.Key);
}
You can call this function like this:
GetUsallyOpeningClosingHour(x => x.From, Enumerable.OrderByDescending);
GetUsallyOpeningClosingHour(x => x.From, Enumerable.OrderBy);
As other answers indicate, you can also just use a boolean flag to indicate ascending or descending order.
You would have to pass in a parameter as there's no way for the method to know which direction you want to sort in based only on the parameter (eg. From/To).
public [return-type] GetUsallyOpeningClosingHour(Func<OpeningDay, TimeSpan> groupByRule, bool isAscending)
{
var openingClosingHours = listOfSpecificDayOfWeek.GroupBy(groupByRule);
if (isAscending)
{
openingClosingHours = openingClosingHours.OrderBy(x => x.Key);
}
else
{
openingClosingHours = openingClosingHours.OrderByDescending(x => x.Key);
}
// Return openingClosingHours? It's not clear how you're using this variable.
}
public List<Book> Books(string orderField, bool desc, int skip, int take)
{
var propertyInfo = typeof(Book).GetProperty(orderField);
return _context.Books
.Where(...)
.OrderBy(p => !desc ? propertyInfo.GetValue(p, null) : 0)
.ThenByDescending(p => desc ? propertyInfo.GetValue(p, null) : 0)
.Skip(skip)
.Take(take)
.ToList();
}
this is my code sample:
public IQueryable<T> GetAllbySearch(
int pageNumber = 1, int pageSize = 10,
Dictionary<string, dynamic> filterParams = null,
Func<IQueryable<T>, IIncludableQueryable<T, object>> include = null,
bool allIncluded = false
, Func<IQueryable<T>, IOrderedQueryable<T>> order = null)
{
var query = _entity.AsQueryable();
if (include != null && !allIncluded)
{
query = include(query);
}
if (allIncluded && include == null)
{
foreach (var property in _context.Model.FindEntityType(typeof(T)).GetNavigations()
.Where(r => !r.IsCollection()))
query = query.Include(property.Name);
}
if (filterParams != null && filterParams.Any())
{
if (filterParams.Any(r => r.Value != null))
{
var expression = GetSearchFilter(filterParams);
if (order != null)
{
return order(query.Where(expression));
}
else
{
return query.Where(expression));
}
}
}
if (order != null)
{
return order(query);
}
else
{
return query;
}
}
I have this C# code that works just fine, which grabs any two fields and puts them in a list for a Drop Down List:
var myDDL = GetDDLValues<Product>( myContact, "contactid", "companyname");
I would like it to take the two string parameters in something other than strings. This would be really nice to do:
GetDDLValues<Product>( myContact, p => p.contactid, p => p.companyname)
This is the code it calls (reflection by sweko Thanks!):
private object GetProperty(object obj, string propertyName)
{
PropertyInfo pi = obj.GetType().GetProperty(propertyName);
object value = pi.GetValue(obj, null);
return value;
}
public IList<DDLValues> GetDDLValues<T>(IList<T> objectListToMap,
string textProperty, string valueProperty)
{
if( objectListToMap != null && objectListToMap.Count > 0)
{
Mapper.CreateMap< T, DDLValues>()
.ForMember( dest => dest.text,
opt => opt.MapFrom(src => textProperty))
.ForMember( dest => dest.value,
opt => opt.MapFrom(src => valueProperty));
return Mapper.Map<IList<T>, IList<DDLValues>>(objectListToMap);
}
else
{
return null;
}
}
To build a dynamic query from string:
public class Product
{
public long ID { get; set; }
public string Name { get; set; }
public DateTime Date { get; set; }
}
static void Main(string[] args)
{
List<Product> products = (from i in Enumerable.Range(1, 10)
select new Product { ID = i, Name = "product " + i, Date = DateTime.Now.AddDays(-i) }).ToList(); //the test case
const string SortBy = "Date"; // to test you can change to "ID"/"Name"
Type sortType = typeof(Product).GetProperty(SortBy).PropertyType; // DateTime
ParameterExpression sortParamExp = Expression.Parameter(typeof(Product), "p"); // {p}
Expression sortBodyExp = Expression.PropertyOrField(sortParamExp, SortBy); // {p.DateTime}
LambdaExpression sortExp = Expression.Lambda(sortBodyExp, sortParamExp); // {p=>p.DateTime}
var OrderByMethod = typeof(Enumerable).GetMethods().Where(m => m.Name.Equals("OrderBy") && m.GetParameters().Count() == 2).FirstOrDefault().MakeGenericMethod(typeof(Product), sortType);
var result = OrderByMethod.Invoke(products, new object[] { products, sortExp.Compile() });
}
tvanfosson is right that a Select() is simplest; if you want to use AutoMapper, you want something like this..
public IList<DDLValues> GetDDLValues<T>(IList<T> objectListToMap,
Func<T, string> textSelector, Func<T, string> valueSelector)
{
if( objectListToMap == null || objectListToMap.Count == 0)
return null;
Mapper.CreateMap< T, DDLValues>()
.ForMember( dest => dest.text,
opt => opt.MapFrom(textSelector))
.ForMember( dest => dest.value,
opt => opt.MapFrom(valueSelector));
return Mapper.Map<IList<T>, IList<DDLValues>>(objectListToMap);
}
If DDLValues has a constructor that takes the text and value properties it should be relatively simple.
IList<DDLValues> GetDDLValues<T>( IList<T> source, Func<T,DDLValues> selector )
where T : class
{
return source.Select( selector )
.ToList();
}
Called as
var ddlList = GetDDLValues( contacts, p => new DDLValues( c.Name, p.ContactID.ToString() ) );
This presumes that DDLValues has a constructor like:
public DDLValues( string text, string value ) { ... }
If you're pulling from the database in your source, then you may also need to materialize the query using ToList() before you apply the selector to ensure it doesn't try to convert the selector into a SQL expression.