I am trying to filter my linq query, using distinct() method but I keep getting all the data records (including duplication). I have tried the following variations, which all seem to be failing.
int total = Data.Count();
// Data = Data.GroupBy(member => member.Tag).Select(x => x.OrderBy(y => y.Name).First());
// Data = Data.OrderByDescending(c => c.UploadDate);
Data = Data.Distinct().OrderBy(value => value.Tag);
var data = Data.ToList();
How can I filter my query by showing all the data fieldnames which are filtered by unique tags field name? My tag fieldname does contain NULL data as well.
Here is my entire method, for further reference:
[Authorize]
[HttpPost]
private HttpResponseMessage method(HttpContext request, Query query)
{
if (User.IsInRole("admin") || User.IsInRole("art"))
{
IQueryable<database_B> Data = null;
if (!string.IsNullOrEmpty(query.name))
{
var ids = query.name.Split(',');
// var dataMatchingTags = db.database_B.Where(c => ids.Any(id => c.Name.Contains(id)));
if (Data == null)
Data = dataMatchingTags;
else
Data = Data.Union(dataMatchingTags);
}
if (Data == null) // If no tags or name is being queried, apply filters to the whole set of products
Data = db.database_B;
if (query.endDate != null)
{
Data = Data.Where(c => c.UploadDate <= query.endDate);
}
if (query.startDate != null)
{
Data = Data.Where(c => c.UploadDate >= query.startDate);
}
int total = Data.Count();
// Data = Data.GroupBy(member => member.Tag).Select(x => x.OrderBy(y => y.Name).First());
// Data = Data.OrderByDescending(c => c.UploadDate);
Data = Data.Distinct().OrderBy(value => value.Tag);
var data = Data.ToList();
if (!data.Any())
{
var message = string.Format("No data found");
return Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
}
// return Request.CreateResponse(HttpStatusCode.OK, data);
return Request.CreateResponse(HttpStatusCode.OK, new { total, data });
}
Thank you for any further help.
You need something like that ?
http://www.codeproject.com/Articles/535374/DistinctBy-in-Linq-Find-Distinct-object-by-Propert
If database_B is a class (as opposed to a struct) which does not implement IEquatable<database_B> in a suitable way, Distinct will treat different objects as different, regardless of member values. A possible solution would be to implementet IEquatable<database_B> to reflect the comparision which is desired for equality.
Alternatively, a different overload of Distinct can be used, where it is possible to give a custom comparision as an argument.
Your class database_B has to implement Equals- and GetHashCode-Method in order to tell the Distinct under which circumstances two instances are considered equal and may therefor be filtered out.
Related
So I have a search-input and checkboxes that passes the values to the controller when there are inputs. And I want to use these values to get something back from the database. The search-input is a string and it works and intended. Here is the code for the search-input:
public async Task<ViewResult> Index(string searchString, List<int> checkedTypes)
{
var products = from p in _db.Products select p;
ViewData["CurrentFilter"] = searchString;
if (!string.IsNullOrEmpty(searchString))
{
products = products.Where(p => p.Name.ToLower().Contains(searchString));
}
return View(products);
}
However the checkboxes values are stored in a list. So basically I want to do the same as the code above, but with a list. So basically an idea is like this:
if(checkedTypes != null)
{
foreach (var i in checkedTypes)
{
products = products.Where(p => p.TypeId == i));
}
}
If I do it like the code above, I just get the last (i) from the loop. Another solution I did was this:
if(checkedTypes != null)
{
var temp = new List<Product>();
foreach (var i in checkedTypes)
{
temp.AddRange(products.Where(p => p.TypeId == i));
}
products = temp.AsQueryable();
}
But when I did it like that I get this error:
InvalidOperationException: The provider for the source IQueryable doesn't implement IAsyncQueryProvider. Only providers that implement IAsyncQueryProvider can be used for Entity Framework asynchronous operations.
So anyone have a solution that I can use? Or is there a better way to handle checkboxes in the controller?
Assuming you are using EF Core (also the same is true for linq2db) - it supports translating filtering with local collection, i.e. Where(x => checkedTypes.Contains(x.SomeId)).
If you have "and" logic to filter by searchString and checkedTypes than you can conditionally add Where clause:
if (!string.IsNullOrEmpty(searchString))
{
products = products.Where(p => p.Name.ToLower().Contains(searchString));
}
if(checkedTypes != null)
{
products = products.Where(p => checkedTypes.Contains(p.TypeId));
}
P.S.
Also you should be able to change your first line to:
var products = _db.Products.AsQueryable();
Let's suppose I receive a collection of strings from user. I need to convert them to GUID sequences for further processing. There is a chance, that user may enter invalid data (not correct GUID sequence), so I need to validate input. Additionally, I can run business-process if only all uploaded data are correct GUID values. Here is my code:
IEnumerable<string> userUploadedValues = /* some logic */;
bool canParseUserInputToGuid = userUploadedValues.All(p => Guid.TryParse(p, out var x));
if(canParseUserInputToGuid)
var parsedUserInput = userUploadedValues.Select(p=> Guid.Parse(p));
This logic works pretty well, but I don't like it as actually I am doing work twice. In second line, Guid.TryParse(p, out var x) already writing parsed GUID sequence to the X variable. Is there an approach to combine validating and mapping logic - if sequence elements satisfy for condition (All) then map this elements to a new collection (Select) in one query? It is important for me also in terms of performance, as it is possible that client will send large amount of data (1, 000, 000+ elements) and doing twice work here is a bit inefficient.
You can do something like this in one Select:
var parsedUserInput = userUploadedValues.Select(p => Guid.TryParse(p, out var x) ? x : default)
.Where(p => p != default);
For this one, you need to be sure if there is no Guid.Empty input from the user.
Otherwise, you can return a nullable Guid if parsing doesn't succeed:
var parsedUserInput = userUploadedValues.Select(p => Guid.TryParse(p, out var x) ? x : default(Guid?))
.Where(p => p != null);
Another solution by creating an extension method, for example:
public static class MyExtensions
{
public static Guid? ToGuid(this string arg)
{
Guid? result = null;
if(Guid.TryParse(arg, out Guid guid))
result = guid;
return result;
}
}
and usage:
var parsedUserInput2 = userUploadedValues.Select(p => p.ToGuid())
.Where(p => p != null);
But keep in mind that in this cases, you will have a collection of nullable Guids.
Your out var x variable will be Guid.Empty in the case where it is not a valid Guid. So you can just do this:
IEnumerable<string> userUploadedValues = new[]
{
"guids.."
};
var maybeGuids = userUploadedValues.Select( x => {
Guid.TryParse( x, out var #guid );
return #guid;
} );
if ( maybeGuids.All( x => x != Guid.Empty ) )
{
//all the maybe guids are guids
}
You can optimize the validation and conversion like below,
IEnumerable<string> userUploadedValues = /* some logic */;
var parsedGuids = userUploadedValues.Where(p => Guid.TryParse(p, out var x));
if(userUploadedValues.Count() != parsedGuids.Count())
{
//Some conversion failed,
}
If the count of both the lists same, then you have all the converted GUIDs in the parsedGuids.
Sometimes the non-LINQ method is just easier to read and no longer.
var parsedUserInput = new List<string>();
foreach(var value in userUploadedValues)
{
if (Guid.TryParse(value, out var x)) parsedUserInput.Add(x);
else...
}
I have a table named dbo.EmployeeType with three records:
PK_EmployeetypeID EmployeeTypeName
1 Project Manager
2 Business Analyst
3 Developer
I have this piece of Linq code:
public static string GetTypeByID(int id)
{
using (ProjectTrackingEntities1 db = new ProjectTrackingEntities1())
{
var type = db.EmployeeTypes.Select(o => new LOOKUPEmployeeType
{
PK_EmployeeTypeID = id,
EmployeeTypeName = o.EmployeeTypeName
});
return type.FirstOrDefault().EmployeeTypeName;
}
}
No matter what id I send to it, it returns Project Manager, and I'm confused as to why.
You need to apply a filter, otherwise you're just returning the first record and hard coding the ID. Try this:
public static string GetTypeByID(int id)
{
using (ProjectTrackingEntities1 db = new ProjectTrackingEntities1())
{
//Here we apply a filter, the lambda here is what creates the WHERE clause
var type = db.EmployeeTypes
.FirstOrDefault(et => et.PK_EmployeeTypeID == id);
if(type != null)
{
return type.EmployeeTypeName;
}
else
{
return "";
}
}
}
Note that using FirstOrDefault means if there are no matches, or multiple matches, type will be null and you will get an empty string returned.
Set a breakpoint on type = ... and inspect it. You have no Where in there so you get all - and Select just makes LOOKUPEmployeeTypes out of all of them.
FirstOrDefault then returns the first of those 3 which is always the ProjManager
Fix:
var type = db
.EmployeeTypes
.Where( o => o.Id == id)
.Select(o => new LOOKUPEmployeeType
{
PK_EmployeeTypeID = id,
EmployeeTypeName = o.EmployeeTypeName
});
In your code you only return the first value. You need to tell EF which value you need to return.
Let us assume you need the value with Id=2. Instead of Select(), use Single(x => x.Id == 2) or First(x => x.Id == 2).
Previously, I had great help on my previous question, thank you vyrp
,
How do I create and populate a dynamic object using a dynamically built lambda expression
I'm now looking to search the dynamic object, and as before, I don't know the objects properties, and therefore what I'm searching until runtime.
Here's the code that builds the dynamic object:
// Get list of optional fields
var optFieldList = await _tbList_FieldRepository.GetAsync(lf => lf.ListID == listId && lf.DisplayInList == true);
// order list of optional fields
optFieldList.OrderBy(lf => lf.DisplayOrder);
// Get base Data excluding Inactive if applicable
IEnumerable<tbList_Data> primaryData = await _tbList_DataRepository.GetAsync(ld => ld.ListID == listId && (ld.IsActive == includeInactive ? ld.IsActive : true));
// Build IEnumerable<dynamic> from base results plus any optional fields to be displayed in table
var results = primaryData.Select(pd => {
dynamic result = new System.Dynamic.ExpandoObject();
result.Id = pd.ID;
result.PrimaryData = pd.PrimaryData;
result.DisplayOrder = pd.DisplayOrder;
result.IsActive = pd.IsActive;
foreach (var optField in optFieldList)
{
switch (optField.FieldType.ToLower()) {
case "text":
((IDictionary<string, object>)result).Add(optField.FieldName, pd.tbList_DataText.Where(ld => ld.DataRowID == pd.ID && ld.ListColumnID == optField.ID).Select(ld => ld.DataField).DefaultIfEmpty("").First());
break;
}
}
return result;
});
For the purpose of testing, I have 2 dynamic fields, "PhoneNumber" and "FuelType"
I can search the known field(s) i.e. PrimaryData, no problem, as below.
results = results.Where(r => r.PrimaryData.Contains(searchString));
And the following will work if I know the field PhoneNumber at design time
results = results.Where(r => r.PhoneNumber.Contains(searchString));
but what I want to do, is something like:
results = results.Where(r => r.PrimaryData.Contains(searchString)
|| foreach(var optField in optFieldList)
{
r.optField.FieldName.Contains(searchString)
})
ending up with
results = results.Where(r =>
r.PrimaryData.Contains(searchString)
|| r.PhoneNumber.Contains(searchString) ||
r.FuelType.Contains(searchString));
but obviously that code doesn't work. I've tried a bunch of different attempts, none successful, so I'm looking for suggestions. Thanks
Since you know that the dynamic element of your query is actually ExpandoObject, hence IDictionary<string, object>>, you can safely cast it to dictionary interface and use it to access the property values by name, while Enumerable.Any method can be used to simulate dynamic || condition:
results = results.Where(r => r.PrimaryData.Contains(searchString)
|| optFieldList.Any(f =>
{
object value;
return ((IDictionary<string, object>)r).TryGetValue(f.FieldName, out value)
&& value is string && ((string)value).Contains(searchString);
}));
I'm having some trouble with finding the right syntax to accomplish the following:
Is it possible with LINQ (Lambda Expression) to .GroupBy data and instead of using the usual .Sum() or .Count() I want the resulting data to be a List of Int.
I defined my own class named: Filter_IDs. Its constructor needs two parameters:
public int? type; // Represents the object_type column from my database
public List<int?> objects; // Represents the object_id column from my database
I want to load data from my database into this object. The following LINQ query should result in a List of Filter_IDs:
The following LINQ query should result in a List of Filter_IDs:
List<Filter_IDs> filterids = ef.filterLine
.GroupBy(fl => fl.objectType)
.Select(fl => new Filter_IDs { type = fl.Key, objects = fl.Select(x => x.object_id).ToList() })
.ToList();
Using this query gives no building error but gives an 'NotSupportedException' on RunTime.
The database looks like this to give you a better understanding of the data:
http://d.pr/i/mnhq+ (droplr image)
Thanks in advance,
Gerben
I think the problem is the DB is not able to call ToList in the select, nor to create a new Filter_ID.
Try something like this :
List<Filter_IDs> filterids = ef.filterLine.Select(o => new { objectType = o.objectType, object_id=o.object_id})
.GroupBy(fl => fl.objectType).ToList()
.Select(fl => new Filter_IDs { type = fl.Key, objects = fl.Select(x => x.object_id).ToList() })
.ToList();
Maybe you want
IList<Filter_IDs> filterIds = ef.filterline
.Select(fl => fl.objectType).Distinct()
.Select(ot => new Filter_IDs
{
type = ot,
objects = ef.filterline
.Where(fl => fl.objectType == ot)
.Select(fl =>objectType)
.ToList()
}).ToList();
Get the distinct list objectType and use that to subquery for each list of object_id.
However, it seems more efficient to me to just enumerate the values in order,
var results = new List<Filter_IDs>();
var ids = new List<int>();
var first = true;
int thisType;
foreach (var fl in ef.filterLines
.OrderBy(fl => fl.objectType)
.ThenBy(fl => fl.object_Id))
{
if (first)
{
thisType = fl.objectType;
first = false;
}
else
{
if (fl.objectType == thisType)
{
ids.Add(fl.object_Id);
}
else
{
results.Add(new Filter_IDs
{
Type = thisType,
objects = ids
});
thisType = fl.objectType;
ids = new List<int>();
}
}
}
You can use GroupBy on client side:
List<Filter_IDs> filterids = ef.filterLine
.Select(fl=>new {fl.ObjectType, fl.object_id})
.AsEnumerable()
.GroupBy(fl => fl.objectType)
.Select(fl => new Filter_IDs { type = fl.Key, objects = fl.Select(x => x.object_id).ToList() })
.ToList();