Cannot convert from IEnumerable<List> to List - linq

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();

Related

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

AngleSharp - Find innermost nested tables

I try to get a list of all innermost tables with AngleSharp. This tables doesn't contain any tables.
With HtmlAgilityPack I've realised it this way:
var wrapper = html.DocumentNode.SelectSingleNode(".//td[#class='wrapper']");
var innerMostTables = wrapper.SelectNodes(".//table [not(descendant::table)]");
With AngleSharp I've tried this, but it doesn't work:
var parser = new HtmlParser();
var document = parser.Parse(html);
var wrapper = document.All.Where(d => d.ClassName == "wrapper");
var innerMostTables = wrapper.Where(w => w.Descendents()
.Select(c => c.NodeName == "table").Count() == 0);
I could solve the problem:
foreach (IElement ch in wrapper.Descendents()
.Where(d => d.NodeName == "TABLE" && d.Descendents()
.Where(d2 => d2.NodeName == "TABLE").Count() == 0))
{
Console.WriteLine(ch.OuterHtml);
}

cannot convert from 'System.Collections.Generic.IEnumerable<System.Collections.Generic.IEnumerable to System.Collections.Generic.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 = wsNameList.Select(x => x.Where(ame => getTranTypeWsName != null && ame.Name == getTranTypeWsName.WsMethodName)).ToList();
my MsgElement list:
_msgElementObsList = new ObservableCollection<MessageElementViewModel>(msgElementList);
this.messageElements = _msgElementList;
NotifyPropertyChanged("MessageElements");
}
Here it is throwing exception Cannot convert from 'System.Collections.Generic.IEnumerable<System.Collections.Generic.IEnumerable to System.Collections.Generic.List).
You have a List of IEnumerable not a List of MessageElementViewModel. That is why you are throwing an error.
Not sure which one you need but you could fix your Select function like this.
var msgElementList = wsNameList.Select(x => x.Where(ame => getTranTypeWsName != null && ame.Name == getTranTypeWsName.WsMethodName).First()).ToList();
or
var msgElementList = wsNameList.SelectMany(x => x.Where(ame => getTranTypeWsName != null && ame.Name == getTranTypeWsName.WsMethodName)).ToList();
Can you rework your msgElementList to
var msgElementList = wsNameList.Where(x => x.Name == getTranTypeWsName.WsMethodName).ToList();?
The getTranTypeWsName != null doesn't belong in there I don't think because it doesn't compare with any of the lambda's members.

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!

LinQ query - Add Where dynamically

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.

Resources