LinQ query - Add Where dynamically - linq

I am having a hard time solving this problem, need code for creating a dynamic linq query in C#, asp.net. I have 5 dropdown list that searches different column in same database table and return item filtered value to a single listbox. The problem is there is no sequence that which or all or any will be selected in DDLs but the combined filtered result should show up in listbox. I have a working query that is searching and returning result in one column at a time for each DDL selection separately. Have to add where clauses with AND to add other DDL selections dynamically to this query. Thanks
public ListItemCollection searchProject(ListItemCollection projList, String searchstr, String columnName)
{
DataSet DSToReturn = new DataSet();
ListItemCollection returnItems = new ListItemCollection();
DataTable results = (from d in ((DataSet)_MyDataset).Tables["Records"].AsEnumerable()
orderby d.Field<string>("Name") ascending
where (d.Field<string>(columnName) != null)
where d[columnName].ToString().ToLower().Contains(searchstr.ToLower())
select d).CopyToDataTable();
foreach (ListItem li in projList)
{
if ((from System.Data.DataRow row in results.Rows
where li.Value.Equals(row["value"].ToString(), StringComparison.InvariantCultureIgnoreCase)
select row["value"]).Count() > 0)
returnItems.Add(li);
}
return returnItems;
}

Here's some example code for how we do it ...
private void DataPortal_Fetch(GoalCriteria criteria)
{
using (var ctx = ContextManager<Data.ExodusDataContext>
.GetManager(Database.ApplicationConnection, false))
{
this.RaiseListChangedEvents = false;
this.IsReadOnly = false;
// set option to eager load child object(s)
var opts = new System.Data.Linq.DataLoadOptions();
opts.LoadWith<Data.Goal>(row => row.Contact);
opts.LoadWith<Data.Goal>(row => row.Sales);
opts.LoadWith<Data.Goal>(row => row.Customer);
ctx.DataContext.LoadOptions = opts;
IQueryable<Data.Goal> query = ctx.DataContext.Goals;
if (criteria.Name != null) // Name
query = query.Where(row => row.Name.Contains(criteria.Name));
if (criteria.SalesId != null) // SalesId
query = query.Where(row => row.SalesId == criteria.SalesId);
if (criteria.Status != null) // Status
query = query.Where(row => row.Status == (int)criteria.Status);
if (criteria.Statuses.Count != 0) // Statuses
query = query.Where(row => criteria.Statuses.Contains((GoalStatus)row.Status));
if (criteria.ContactId != null) // ContactId
query = query.Where(row => row.ContactId == criteria.ContactId);
if (criteria.CustomerId != null) // CustomerId
query = query.Where(row => row.CustomerId == criteria.CustomerId);
if (criteria.ScheduledDate.DateFrom != DateTime.MinValue) // ScheduledDate
query = query.Where(t => t.ScheduledDate >= criteria.ScheduledDate.DateFrom);
if (criteria.ScheduledDate.DateTo != DateTime.MaxValue)
query = query.Where(t => t.ScheduledDate <= criteria.ScheduledDate.DateTo);
if (criteria.CompletedDate.DateFrom != DateTime.MinValue) // ComplatedDate
query = query.Where(t => t.CompletedDate >= criteria.CompletedDate.DateFrom);
if (criteria.CompletedDate.DateTo != DateTime.MaxValue)
query = query.Where(t => t.CompletedDate <= criteria.CompletedDate.DateTo);
if (criteria.MaximumRecords != null) // MaximumRecords
query = query.Take(criteria.MaximumRecords.Value);
var data = query.Select(row => GoalInfo.FetchGoalInfo(row));
this.AddRange(data);
this.IsReadOnly = true;
this.RaiseListChangedEvents = true;
}
}
We just check for a null value assigned to our criteria object, if it's not null then we append it to the query.

Related

LinqKit Core PredicateBuilder not functioning correctly

I have this query:
var query = LinqKit.PredicateBuilder.New<Resume>();
if (selectedWorkFieldID != 0)
{
query = query.And(js => js.WorkFieldID == selectedWorkFieldID);
if (!(selectedJobIDs.Contains(0) && selectedJobIDs.Count() == 1))
{
foreach (int jobID in selectedJobIDs)
query = query.Or(js => js.JobID == jobID);
}
}
var finalQuery = context.Resumes.AsNoTracking().Include(r => r.ResumeSkills)
.ThenInclude(rs => rs.Skill).Include(r => r.JobSeeker).ThenInclude(r => r.Profile)
.AsExpandable().Where(query);
count = finalQuery.Count();
resumes = finalQuery.Skip(args.Skip.Value).Take(args.Top.Value).ToList<Resume>();
This query returns All resumes not filtered ones. When I debug, the debugger curser enters the foreach block that filters with or, and there is one jobID in selectedJobIDs but the query returns all resumes. it seems the predicate builder not working at all. How to solve this?
I changed code to this:
if (selectedWorkFieldID != 0)
{
query = query.And(js => js.WorkFieldID == selectedWorkFieldID);
if (!(selectedJobIDs.Contains(0) && selectedJobIDs.Count() == 1))
{
var query2 = LinqKit.PredicateBuilder.New<Resume>();
foreach (int jobID in selectedJobIDs)
query2 = query2.Or(js => js.JobID == jobID);
query.And(query2);
}
}
and it is corrected.

Linq To Select All controls where DI contains some text from ControlCollection

Linq has always befuddled me. I am trying to extract all controls from an ASP.Net form page where the ID of the control contains a specific string. The control collection is hierarchical and I want to return any matching controls from all levels. Am I anywhere in the ballpark here? I could really use some help/education. The collection parameter is the collection of controls from the page and controlID is the text I am searching for.
public static Control FindControlsByControlID(ControlCollection collection, string controlID)
{
IEnumerable<Control> controls = collection.Cast<Control>();
IEnumerable<Control> matchedControls = controls
.SelectMany(p => p.Controls.Cast<Control>()
.SelectMany(c => c.Controls.Cast<Control>())
.Where(d => d != null ? d.ID != null ? d.ID.Contains(controlID) : false : false))
.Where(a => a != null ? a.ID != null ? a.ID.Contains(controlID) : false : false);
ConcurrentQueue<Control> cq;
if (matchedControls != null)
cq = new ConcurrentQueue<Control>(matchedControls);
else
return null;
...
Thanks in advance!
Use an extension method to get all child controls:
public static class ControlExt {
public static IEnumerable<Control> AndSubControls(this Control aControl) {
var work = new Queue<Control>();
work.Enqueue(aControl);
while (work.Count > 0) {
var c = work.Dequeue();
yield return c;
foreach (var sc in c.Controls.Cast<Control>()) {
yield return sc;
if (sc.Controls.Count > 0)
work.Enqueue(sc);
}
}
}
}
Now you can test all the subcontrols in your ControlCollection:
IEnumerable<Control> matchedControls = controls.SelectMany(c => c.AndSubControls())
.Where(a => a != null && a.ID != null && a.ID.Contains(controlID));

LINQ: query very slow

I have the following database structure (just showing the pertinent tables):
I need to get a listing of all the elements in the table "Propietario" and each element must have a list of all its related elements in "Clave". Specifically, I need the field "CodigoClave" from "Clave".
Also, I need to be able to filter the results any of the fields.
This is what I've come up with so far:
int pageSizeP = (pageSize == null || pageSize == 0 || pageSize > 15) ? 15 : (int)pageSize;
int pageNumberP = (pageNumber == null || pageNumber == 0) ? 1 : (int)pageNumber;
var propietariosCount = context.Propietarios.Count();
var propietariosQuery = context.Propietarios
.AsEnumerable()
.OrderBy(p => p.Nombre).ThenBy(p => p.Apellido1).ThenBy(p => p.Apellido2)
.Select(p => new
{
Id = p.Id,
Nombre = p.Nombre,
Apellido1 = p.Apellido1,
Apellido2 = p.Apellido2,
NIF = p.NIF,
Claves = string.Join(", ", context.PropietariosFincas
.Where(pf => pf.PropietarioId == p.Id)
.Join(context.Expedientes
.Include(e => e.Clave),
pf => pf.FincaId,
e => e.FincaId,
(pf, e) => (e.Clave.CodigoClave + ((e.Clave.Alias == null) ? "" : " - " + e.Clave.Alias))
).Distinct().ToList())
// Getting an array of Claves would do;
// I could turn it into a string in the JS app
// But it's just as slow
});
if (criteriosBusqueda != "")
{
string[] aCriterios = criteriosBusqueda.Split(new string[] { "," }, StringSplitOptions.None);
CompareOptions compareOptions = CompareOptions.IgnoreCase;
if (aCriterios[0] != "")
propietariosQuery = propietariosQuery
.Where(p => p.Nombre != null)
.Where(p => p.Nombre.Replace("/", "__").ContainsIgnoreAccents(aCriterios[0], compareOptions));
if (aCriterios[1] != "")
propietariosQuery = propietariosQuery
.Where(p => p.Apellido1 != null)
.Where(p => p.Apellido1.Replace("/", "__").ContainsIgnoreAccents(aCriterios[1], compareOptions));
if (aCriterios[2] != "")
propietariosQuery = propietariosQuery
.Where(p => p.Apellido2 != null)
.Where(p => p.Apellido2.Replace("/", "__").ContainsIgnoreAccents(aCriterios[2], compareOptions));
if (aCriterios[3] != "")
propietariosQuery = propietariosQuery
.Where(p => p.NIF != null)
.Where(p => p.NIF.Replace("/", "__").ToLower().Contains(aCriterios[3].ToLower()));
if (aCriterios[4] != "")
propietariosQuery = propietariosQuery
.Where(p => p.Claves != null)
.Where(p => p.Claves.Replace("/", "__").ToLower().Contains(aCriterios[4].ToLower()));
}
var propietariosList = propietariosQuery.Skip((pageNumberP - 1) * pageSizeP).Take(pageSizeP)
.ToList();
var datosPropietarios = new
{
pageSize = pageSizeP,
pageNumber = pageNumberP,
recordsNumber = propietariosQuery.Count()
titulares = propietariosList
};
return Ok(datosPropietarios);
It works, but it's too slow (it takes several minutes). I've managed to make the initial query (the first page, unfiltered) fast by making recordsNumber = context.Propietarios.Count() but that breaks the pagination when filtering. And anyway, if I filter or if I go to a page near the end, it is again really slow.
How can I make the query faster?
My guess is that the query is pretty much traversing all the tables, so I don't know if there's even a solution that doesn't involve changing the data model.
It's because you are calling AsEnumerable, you are fetching all the data from Propietarios table to memory and the rest of operations you do after you call that method are executed using Linq to Objects instead of Linq to Entities, so your query is not translated to sql. Try removing it
Take a look of this post in case you need more info how AsEnumerable works with EF

Cannot convert from IEnumerable<List> to List

I am working on these lists to get an item that matches the selected item from the combobox.
private void InitializaMessageElement()
{
if (_selectedTransactionWsName != null)
{
get a transaction webservice name matching the selected item from the drop down here the output=TestWS which is correct
var getTranTypeWsName = TransactionTypeVModel
.GetAllTransactionTypes()
.FirstOrDefault(transTypes =>
transTypes.WsMethodName == _selectedTransactionWsName);
Loop the list of wsnames from the treenode list. Here it gives me all the node I have which is correct.
var wsNameList = MessageElementVModel
.GetAllTreeNodes().Select(ame =>
ame.Children).ToList();//. == getTranTypeWsName.WsMethodName);
find the getTranTypeWsName.WsMethodName in the wsNameList. Here is where I have the problem:
var msgElementList = MessageElementVModel.GetAllTreeNodes()
.Select(ame => ame.Children).Where(c =>
{
c.Where(d => getTranTypeWsName != null && d.Name == getTranTypeWsName.WsMethodName);
return false;
});
my MsgElement list:
var _msgElementList = new ObservableCollection<MessageElementViewModel>(msgElementList);
this.messageElements = _msgElementList;
NotifyPropertyChanged("MessageElements");
}
Here it is returning wrong data. It returns all the list I have a non filtered one.
Why is my out put inconsistent? I am new to LINQ.
This part of your code seems problematic:
var msgElementList = wsNameList.SingleOrDefault(x =>
{
x.Where(ame => ame.Name == getTranTypeWsName.WsMethodName);
return true;
});
You were expecting a list but you used the SingleOrDefault(), which is meant to be used in retrieving a single record.
Try this, which will retrieve msgElementList. No need to get all tree nodes first and store it in wsNameList:
// Assumed that Name is a property of Children
var msgElementList = MessageElementVModel.GetAllTreeNodes()
.Select(ame => ame.Children).Where(c => c.Name == getTranTypeWsName.WsMethodName).ToList();
Here is a solution:
var msgElementList = wsNameList.SelectMany(x => x.Where(ame => getTranTypeWsName != null && ame.Name == getTranTypeWsName.WsMethodName)).ToList();

Deep LINQ projections, how?

Let's say I have a model created with EF 4.0
User
Roles
Permissions
Each entity has a DeleteDate property.
I want to get a specific user (with Name =...) and have the tree filled with items where DeletedDate == null..
This must be done with anonymous type projection as result, but I don't know how to accomplish this with a hierachy deeper than 2..
This is what I already have:
public MyProjection MyCall(string givenName)
{
var result = from s in context.Users
where (s.Name == givenName &&
s.DeletedDate == null)
select new
{
s,
roles = from r in s.Roles
where r.DeletedDate == null
select r
};
var outcome = result.FirstOrDefault();
if (outcome != null)
{
var myProjection = new MyProjection()
{
User = outcome.s,
Roles = outcome.roles
};
return myProjection;
}
return null;
}
Depending on your structure you could do something like this:
var result = m.Users.Where(u => u.DeletedDate == null)
.Select( u => new
{
u,
roles = u.Roles.Where(r => r.DeletedDate == null)
.Select(r => new
{
r,
permissions = r.Permissions.Where(p => p.DeletedDate == null)
})
}).FirstOrDefault(item => item.u.Name == givenName);
If you retrieve with the following:
var result = from s in MyUsers
where s.DeletedDate == null
select new aUser{
Roles = (from r in s.Roles
where r.DeletedDate == null
select r).ToList()
};
And then create a TreeView:
TreeView treeView = new TreeView();
Then set the ItemsSource of the TreeView to the IEnumerable:
treeView.ItemsSource = result;
Then build a HierarchicalDataTemplate in your TreeView to represent your Lists (similar to this or for more in depth this), then voila!

Resources