Compare 2 lists using linq - linq

I'm a linq noob.... can someone please some me how to achieve this using linq... I'm trying to compare 2 lists in both directions...
internal void UpdateUserTeams(int iUserID)
{
UserTeamCollection CurrentTeams = GetUserTeams(iUserID);
UserTeamCollection UpdatedTeams = this;
foreach (UserTeam ut in CurrentTeams)
{
if(!UpdatedTeams.ContainsTeam(ut.ID))
{
RemoveTeamFromDB();
}
}
foreach (UserTeam ut in UpdatedTeams)
{
if (!CurrentTeams.ContainsTeam(ut.ID))
{
AddTeamToDB();
}
}
}
public bool ContainsTeam(int iTeamID)
{
return this.Any(t => t.ID == iTeamID);
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Linqage
{
class Program
{
static void Main(string[] args)
{
UserTeamCollection currentTeams = new UserTeamCollection()
{
new UserTeam(1),
new UserTeam(2),
new UserTeam(3),
new UserTeam(4),
new UserTeam(5)
};
UserTeamCollection updatedTeams = new UserTeamCollection()
{
new UserTeam(2),
new UserTeam(4),
new UserTeam(6),
new UserTeam(8)
};
currentTeams.Except(updatedTeams).All(u =>
{
//Console.WriteLine("Item ID: {0}",u.ID);
//RemoveFromDB()
return true;
});
updatedTeams.Except(currentTeams).All(u =>
{
//Console.WriteLine("Item ID: {0}", u.ID);
//AddToDB()
return true;
});
}
}
public class UserTeamCollection
: List<UserTeam>
{
}
//Either overwrite the GetHashCode and Equals method OR create a IComparer
public class UserTeam
{
public int ID { get; set; }
public UserTeam(int id)
{
ID = id;
}
public override bool Equals(object obj)
{
UserTeam iOther = obj as UserTeam;
if (iOther != null)
{
return this.ID == iOther.ID;
}
return false;
}
public override int GetHashCode()
{
return ID.GetHashCode();
}
}
}

So converting your initial question to an english requirement:
foreach (UserTeam ut in CurrentTeams) // for each current team
{
if(!UpdatedTeams.ContainsTeam(ut.ID)) // that is not in the updated teams list
{
RemoveTeamFromDB(); // remove it from the database
}
}
foreach (UserTeam ut in UpdatedTeams) //for each of the teams in the updated teams list
{
if (!CurrentTeams.ContainsTeam(ut.ID)) //if the current team does not contain the updated team
{
AddTeamToDB(); //add the team to the database
}
}
Therefore, you want to do:
//select all current teams that are not in updated teams list
CurrentTeam.Except(UpdatedTeams).All(team => { RemoveTeamFromDB(team); return true; });
//select all updated teams that are not in the current teams list
UpdatedTeam.Except(CurrentTeams).All(team => { AddTeamToDB(team); return true; });
Make sure your UserTeam object has proper overrides for the Equals and GetHashCode methods, so that comparison between two UserTeams is accurate :)

You would normally use Enumerable.Except both ways to get the differences. Then add and remove as needed.

var addedTeams = UpdatedTeams.Except(CurrentTeams);
var removedTeams = CurrentTeams.Except(UpdatedTeams);

You're trying to get the outer parts from a full outer join. Here's a rough way to achieve that.
ILookup<int, UserTeam> currentLookup = CurrentTeams
.ToLookup(ut => ut.ID);
ILookup<int, UserTeam> updatedLookup = UpdatedTeams
.ToLookup(ut => ut.ID);
List<int> teamIds = CurrentTeams.Select(ut => ut.ID)
.Concat(UpdatedTeams.Select(ut => ut.ID))
.Distinct()
.ToList();
ILookup<string, UserTeam> results =
(
from id in teamIds
let inCurrent = currentLookup[id].Any()
let inUpdated = updatedLookup[id].Any()
let key = inCurrent && inUpdated ? "No Change" :
inCurrent ? "Remove" :
inUpdated ? "Add" :
"Inconceivable"
let teams = key == "Remove" ? currentLookup[id] :
updatedLookup[id]
from team in teams
select new {Key = key, Team = team)
).ToLookup(x => x.Key, x => x.Team);
foreach(UserTeam ut in results["Remove"])
{
RemoveTeamFromDB();
}
foreach(UserTeam ut in results["Add"])
{
AddTeamToDB();
}

Related

ASP.NET MVC5 add 2 Sync Times to the Application

I Added the 2 Sync Times to my DB as 2 new columns and inserted values as below:
USE [DB]
ALTER TABLE [dbo].[TableName]
ADD ColumnName2 time, ColumnName3 time
This was for adding the columns.
For inserting the row values I did:
USE DB
INSERT INTO TableName (ColumnName2, ColumnName3)
VALUES ('20:30:00', '23:30:00')
This was the data for the fixed times in the rows of those columns.
I also went through all the layers of the application such as (controller, models, views, queries, services, interfaces, and so forth. NOW when I try to update any of the new times added they default to the first time that already existed on the table as a COLUMN with time in the row.
I could not post the image for the time fields from the application because it is not permitted. However, the image is in a little panel and consists of 3 fields (textboxfor) with a time picker for each one.
Any help would be greatly appreciated.
Thanks
Now I thought I would post some of the example code to see if this helps
// My controller method for those sync times
[HttpPost]
[Page(PageName.UpdateSystemConfigTime)]
public ActionResult UpdateTime(SystemMaintenanceViewModel model)
{
var dateTime = DateTime.ParseExact(model.SystemConfiguration.SynchronizationTime, "h:mm tt", CultureInfo.InvariantCulture);
var dateTime2 = DateTime.ParseExact(model.SystemConfiguration.SynchronizationTime2, "h:mm tt", CultureInfo.InvariantCulture);
var dateTime3 = DateTime.ParseExact(model.SystemConfiguration.SynchronizationTime3, "h:mm tt", CultureInfo.InvariantCulture);
//model.ProcessTime
if (model.SystemConfiguration.SynchronizationTime != null &&
model.SystemConfiguration.SynchronizationTime2 != null &&
model.SystemConfiguration.SynchronizationTime3 != null);
{
var sysConfig = new DTO.SystemSync.SystemConfiguration
{
SyncTime = dateTime.TimeOfDay,
SyncTime2 = dateTime2.TimeOfDay,
SyncTime3 = dateTime3.TimeOfDay
};
configService.UpdateSyncTime(sysConfig);
configService.UpdateSyncTime2(sysConfig);
configService.UpdateSyncTime3(sysConfig);
}
return RedirectToAction("Index");
}
////My Private method
private SystemConfiguration GetSystemConfig()
{
var model = new SystemConfiguration();
var config = configService.GetSyncTime();
configService.GetSyncTime2();
configService.GetSyncTime3();
if (config == null) return model;
var ts = config.SyncTime;
if (ts != null)
{
model.SynchronizationTime = ts.ToString();
}
var ts2 = config.SyncTime2;
if (ts2 != null)
{
model.SynchronizationTime2 = ts2.ToString();
}
var ts3 = config.SyncTime3;
if (ts3 != null)
{
model.SynchronizationTime3 = ts3.ToString();
}
return model;
============================================================================
/// My configuration command
namespace --.--.Commands
{
public class ConfigurationCommand : CommandBase, IConfigurationCommand
{
static ConfigurationCommand()
{
ConfigureAutoMapper();
}
private static void ConfigureAutoMapper()
{
Mapper.CreateMap<SystemConfiguration, entity.SystemConfiguration>()
.ForMember(dest => dest.SyncTime, opt => opt.ResolveUsing<TimeSpanToSqlTimeResolver>())
.ForMember(dest => dest.SyncTime2, opt => opt.ResolveUsing<TimeSpanToSqlTimeResolver>())
.ForMember(dest => dest.SyncTime3, opt => opt.ResolveUsing<TimeSpanToSqlTimeResolver>());
}
public void UpdateSyncTime(SystemConfiguration timeOfDay)
{
Guard.NotNull(timeOfDay);
var mapped = Mapper.Map<entity.SystemConfiguration>(timeOfDay);
var config = Context.SystemConfigurations.SingleOrDefault();
//if this is the first time, then we need to insert
if (config == null)
{
var newConfig = new entity.SystemConfiguration
{
SyncTime = mapped.SyncTime
};
Context.SystemConfigurations.Add(newConfig);
}
else
{
config.SyncTime = mapped.SyncTime;
}
SaveChanges();
}
public void UpdateSyncTime2(SystemConfiguration timeOfDay)
{
Guard.NotNull(timeOfDay);
var mapped = Mapper.Map<entity.SystemConfiguration>(timeOfDay);
var config = Context.SystemConfigurations.SingleOrDefault();
if (config == null)
{
var newConfig = new entity.SystemConfiguration
{
SyncTime2 = mapped.SyncTime2
};
Context.SystemConfigurations.Add(newConfig);
}
else
{
config.SyncTime2 = mapped.SyncTime2;
}
SaveChanges();
}
public void UpdateSyncTime3(SystemConfiguration timeOfDay)
{
Guard.NotNull(timeOfDay);
var mapped = Mapper.Map<entity.SystemConfiguration>(timeOfDay);
var config = Context.SystemConfigurations.SingleOrDefault();
if (config == null)
{
var newConfig = new entity.SystemConfiguration
{
SyncTime3 = mapped.SyncTime3
};
Context.SystemConfigurations.Add(newConfig);
}
else
{
config.SyncTime3 = mapped.SyncTime3;
}
SaveChanges();
}
}
}
=========================================================================================================
// My configuration service
namespace --.--.--.SystemSync
{
public class ConfigurationService : IConfigurationService
{
private IConfigurationQuery query;
private IConfigurationCommand command;
public ConfigurationService(IConfigurationQuery query,IConfigurationCommand command)
{
this.query = query;
this.command = command;
}
public void UpdateSyncTime(SystemConfiguration timeOfDay)
{
command.UpdateSyncTime(timeOfDay);
}
public void UpdateSyncTime2(SystemConfiguration timeOfDay)
{
command.UpdateSyncTime2(timeOfDay);
}
public void UpdateSyncTime3(SystemConfiguration timeOfDay)
{
command.UpdateSyncTime3(timeOfDay);
}
public SystemConfiguration GetSyncTime()
{
return query.GetSyncTime();
}
public SystemConfiguration GetSyncTime2()
{
return query.GetSyncTime2();
}
public SystemConfiguration GetSyncTime3()
{
return query.GetSyncTime3();
}
public List<PageResource> GetPages()
{
return query.GetPages().ToList();
}
}
}
You made a comment about fixed times, Are you looking for something like this?
CREATE TABLE [dbo].[Zamen](
[Id] [int] IDENTITY(1,1) NOT NULL,
[time1] [time](3) NOT NULL,
[time2] [time](3) NOT NULL,
[Content] [varchar](100) NULL,
CONSTRAINT [PK_Zamen] PRIMARY KEY CLUSTERED
(
[Id] ASC
))
GO
ALTER TABLE [dbo].[Zamen] ADD CONSTRAINT [DF_Zamen_time1] DEFAULT (getdate()) FOR [time1]
GO
ALTER TABLE [dbo].[Zamen] ADD CONSTRAINT [DF_Zamen_time2] DEFAULT (getdate()) FOR [time2]
GO
Those alter table statements allow the time to be automatically inserted. So when you do this:
INSERT INTO Zamen (Content) VALUES ('demo')
The current times are placed into the values.
*After seeing the code you added, some input:
In your UpdateTime Action Method, a problem that stands out is you are calling UpdateTimeSync three times, but passing it all three variables each time. I would suggest to refactor your update method -- instead of three update methods, use one update method for all time variables.

Integrating AspNet.WebApi with AspNet.SignalR

I want to integrate Microsoft.AspNet.SignalR version="2.1.2" with Microsoft.AspNet.WebApi version="5.2.2" so that the API can communicate in REAL-TIME. I found one VERY NICE SAMPLE that implements/works exactly the same way I want, but the sample uses jquery.signalR-0.5.0.js. Some of the earlier implementations have changed, and so far here is what I have done in a failed effort to upgrade the solution to use the latest signalr, asp.net web api and owin.
I left the Hub as it is
using SignalR.Hubs;
namespace NdcDemo.Hubs
{
// This hub has no inbound APIs, since all inbound communication is done
// via the HTTP API. It's here for clients which want to get continuous
// notification of changes to the ToDo database.
[HubName("todo")]
public class ToDoHub : Hub { }
}
I also left the ApiControllerWithHub class as it is
using System;
using System.Web.Http;
using SignalR;
using SignalR.Hubs;
namespace NdcDemo.Controllers
{
public abstract class ApiControllerWithHub<THub> : ApiController
where THub : IHub
{
Lazy<IHubContext> hub = new Lazy<IHubContext>(
() => GlobalHost.ConnectionManager.GetHubContext<THub>()
);
protected IHubContext Hub
{
get { return hub.Value; }
}
}
}
For the ToDoController, I changed the
Hub.Clients.addItem(item), Hub.Clients.updateItem(toUpdate),
Hub.Clients.deleteItem(id)
to
Hub.Clients.All.addItem(item), Hub.Clients.All.updateItem(toUpdate),
Hub.Clients.All.deleteItem(id)
and this is now the full ToDoController class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Web.Http;
using NdcDemo.Hubs;
using NdcDemo.Models;
namespace NdcDemo.Controllers
{
[InvalidModelStateFilter]
public class ToDoController : ApiControllerWithHub<ToDoHub>
{
private static List<ToDoItem> db = new List<ToDoItem>
{
new ToDoItem { ID = 0, Title = "Do a silly demo on-stage at NDC" },
new ToDoItem { ID = 1, Title = "Wash the car" },
new ToDoItem { ID = 2, Title = "Get a haircut", Finished = true }
};
private static int lastId = db.Max(tdi => tdi.ID);
public IEnumerable<ToDoItem> GetToDoItems()
{
lock (db)
return db.ToArray();
}
public ToDoItem GetToDoItem(int id)
{
lock (db)
{
var item = db.SingleOrDefault(i => i.ID == id);
if (item == null)
throw new HttpResponseException(
Request.CreateResponse(HttpStatusCode.NotFound)
);
return item;
}
}
public HttpResponseMessage PostNewToDoItem(ToDoItem item)
{
lock (db)
{
// Add item to the "database"
item.ID = Interlocked.Increment(ref lastId);
db.Add(item);
// Notify the connected clients
Hub.Clients.addItem(item);
// Return the new item, inside a 201 response
var response = Request.CreateResponse(HttpStatusCode.Created, item);
string link = Url.Link("apiRoute", new { controller = "todo", id = item.ID });
response.Headers.Location = new Uri(link);
return response;
}
}
public ToDoItem PutUpdatedToDoItem(int id, ToDoItem item)
{
lock (db)
{
// Find the existing item
var toUpdate = db.SingleOrDefault(i => i.ID == id);
if (toUpdate == null)
throw new HttpResponseException(
Request.CreateResponse(HttpStatusCode.NotFound)
);
// Update the editable fields and save back to the "database"
toUpdate.Title = item.Title;
toUpdate.Finished = item.Finished;
// Notify the connected clients
Hub.Clients.updateItem(toUpdate);
// Return the updated item
return toUpdate;
}
}
public HttpResponseMessage DeleteToDoItem(int id)
{
lock (db)
{
int removeCount = db.RemoveAll(i => i.ID == id);
if (removeCount <= 0)
return Request.CreateResponse(HttpStatusCode.NotFound);
// Notify the connected clients
Hub.Clients.deleteItem(id);
return Request.CreateResponse(HttpStatusCode.OK);
}
}
}
}
And then I put app.MapSignalR();
But the demo doesn't work...the API doesn't contact clients...Where am I going wrong?
I would still appreciate any more simpler recommendations based on this solution.
Solution from OP.
Answer:
After a a cup of camomile tea, i found out that the clients had to include the KEYWORD CLIENT before the dynamic methods in Todo.js... So, here is what that needs to be modified so that the sample works
hub.client.addItem = function (item) {
alert("i just received something...");
viewModel.add(item.ID, item.Title, item.Finished);
};
hub.client.deleteItem = function (id) {
viewModel.remove(id);
};
hub.client.updateItem = function (item) {
viewModel.update(item.ID, item.Title, item.Finished);
};
And it works!

EmitMapper and List

It's the first time that I use EmitMapper.
I have a list of object ex: Customer and I would like to map this list in a ienumerable of CustomerDTO how can I do that?
Tnx
It's straightforward if you have a list and want to convert it to list of DTOs:
var mapper = ObjectMapperManager.DefaultInstance.GetMapper<Customer, CustomerDTO>();
IEnumerable<CustomerDTO> dtos = listOfCustomer.Select(mapper.map);
The preblem is when the list is in another object, for example User and UserDTO:
class User {
public List<Customer> Customers { get; set; }
}
class UserDTO {
public IEnumerable<CustomerDTO> Customers { get; set; }
}
It seems that EmitMapper does not support conversion from List to Enumerable. A way to support it would be:
var customerMapper = ObjectMapperManager
.DefaultInstance.GetMapper<Customer, CustomerDTO>();
var mapper = ObjectMapperManager.DefaultInstance
.GetMapper<User, UserDTO>(
new DefaultMapConfig()
.ConvertUsing<List<Customer>, IEnumerable<CustomerDTO>>(
a => a.Select(customerMapper.Map))
);
This can be done creating a custom class, implementing the interface "ICustomConverterProvider" and adding a ConvertGeneric to the "DefaultMapConfig".
Looking on the source code of EmitMapper, i found a class named "ArraysConverterProvider", which is the default generic converter from ICollections to Arrays.
Adapting the code from this class to work with IEnumerable collections:
class GenericIEnumerableConverterProvider : ICustomConverterProvider
{
public CustomConverterDescriptor GetCustomConverterDescr(
Type from,
Type to,
MapConfigBaseImpl mappingConfig)
{
var tFromTypeArgs = DefaultCustomConverterProvider.GetGenericArguments(from);
var tToTypeArgs = DefaultCustomConverterProvider.GetGenericArguments(to);
if (tFromTypeArgs == null || tToTypeArgs == null || tFromTypeArgs.Length != 1 || tToTypeArgs.Length != 1)
{
return null;
}
var tFrom = tFromTypeArgs[0];
var tTo = tToTypeArgs[0];
if (tFrom == tTo && (tFrom.IsValueType || mappingConfig.GetRootMappingOperation(tFrom, tTo).ShallowCopy))
{
return new CustomConverterDescriptor
{
ConversionMethodName = "Convert",
ConverterImplementation = typeof(GenericIEnumerableConverter_OneTypes<>),
ConverterClassTypeArguments = new[] { tFrom }
};
}
return new CustomConverterDescriptor
{
ConversionMethodName = "Convert",
ConverterImplementation = typeof(GenericIEnumerableConverter_DifferentTypes<,>),
ConverterClassTypeArguments = new[] { tFrom, tTo }
};
}
}
class GenericIEnumerableConverter_DifferentTypes<TFrom, TTo> : ICustomConverter
{
private Func<TFrom, TTo> _converter;
public IEnumerable<TTo> Convert(IEnumerable<TFrom> from, object state)
{
if (from == null)
{
return null;
}
TTo[] result = new TTo[from.Count()];
int idx = 0;
foreach (var f in from)
{
result[idx++] = _converter(f);
}
return result;
}
public void Initialize(Type from, Type to, MapConfigBaseImpl mappingConfig)
{
var staticConverters = mappingConfig.GetStaticConvertersManager() ?? StaticConvertersManager.DefaultInstance;
var staticConverterMethod = staticConverters.GetStaticConverter(typeof(TFrom), typeof(TTo));
if (staticConverterMethod != null)
{
_converter = (Func<TFrom, TTo>)Delegate.CreateDelegate(
typeof(Func<TFrom, TTo>),
null,
staticConverterMethod
);
}
else
{
_subMapper = ObjectMapperManager.DefaultInstance.GetMapperImpl(typeof(TFrom), typeof(TTo), mappingConfig);
_converter = ConverterBySubmapper;
}
}
ObjectsMapperBaseImpl _subMapper;
private TTo ConverterBySubmapper(TFrom from)
{
return (TTo)_subMapper.Map(from);
}
}
class GenericIEnumerableConverter_OneTypes<T>
{
public IEnumerable<T> Convert(IEnumerable<T> from, object state)
{
if (from == null)
{
return null;
}
return from;
}
}
This code is just a copy with a minimum of adaptation as possible and can be applyed to objects with many levels of hierarchy.
You can use the above code with the following command:
new DefaultMapConfig().ConvertGeneric(
typeof(IEnumerable<>),
typeof(IEnumerable<>),
new GenericIEnumerableConverterProvider());
This saved my day and I hope to save yours too! hehehe

Hierarchical structure iteration and LINQ

Assume that we have class
public class RMenuItem
{
public List<RMenuItem> ChildrenItems { get; }
public decimal OperationID { get; }
public string Name { get; }
}
as you can see - each menuitem could have children items - as usual in menu.
My task is to iterate through each items of this list and apply some action to it. Classical decision is to write recursive iteration. But I'm interesting if LINQ could make my task easier? For example, I suppose that we can write query that can get flat list of objects, which i can iterate simply with foreach. But my attempts in this way weren't successful yet.
So any help appreciated!
It's possible:
public void PrintAllNames(RMenuItem rootItem)
{
Action<RMenuItem> print = null;
print = m =>
{
Console.WriteLine(m.Name);
m.ChildrenItems.ForEach(print);
};
print(rootItem);
}
Notice how it's necessary to declare print so that print can use itself. This is directly comparable to a recursive method, which I'd rather use:
public void PrintAllNames(RMenuItem rootItem)
{
Console.WriteLine(rootItem.Name);
rootItem.ChildrenItems.ForEach(PrintAllNames);
}
(although for a more complex situation, maybe the functional solution would make the most sense)
I suggest 2 ways of achieving this. You can opt with an utility method to get all the items or you can implement the Visitor Pattern, though it implies changing the RMenuItem class.
Utility method:
static IEnumerable<RMenuItem> GetAllMenuItems(IList<RMenuItem> items)
{
if (items == null)
throw new ArgumentNullException("items");
Queue<RMenuItem> queue = new Queue<RMenuItem>(items);
while (queue.Count > 0)
{
var item = queue.Dequeue();
if (item.ChildrenItems != null)
{
foreach (var child in item.ChildrenItems)
{
queue.Enqueue(child);
}
}
yield return item;
}
}
I prefer an imperative way to a recursive because we can use iterator blocks.
Visitor Pattern:
public interface IRMenuItemVisitor
{
void Visit(RMenuItem item);
}
public class PrintRMenuItemVisitor : IRMenuItemVisitor
{
public void Visit(RMenuItem item)
{
Console.WriteLine(item);
}
}
public interface IRMenuItem
{
void Accept(IRMenuItemVisitor visitor);
}
public class RMenuItem : IRMenuItem
{
// ...
public void Accept(IRMenuItemVisitor visitor)
{
visitor.Visit(this);
if (ChildrenItems != null)
{
foreach (var item in ChildrenItems)
{
item.Accept(visitor);
}
}
}
}
Usage:
RMenuItem m1 = new RMenuItem
{
Name = "M1",
ChildrenItems = new List<RMenuItem> {
new RMenuItem { Name = "M11" },
new RMenuItem {
Name = "M12",
ChildrenItems = new List<RMenuItem> {
new RMenuItem { Name = "M121" },
new RMenuItem { Name = "M122" }
}
}
}
};
RMenuItem m2 = new RMenuItem
{
Name = "M2",
ChildrenItems = new List<RMenuItem> {
new RMenuItem { Name = "M21" },
new RMenuItem { Name = "M22" },
new RMenuItem { Name = "M23" }
}
};
IList<RMenuItem> menus = new List<RMenuItem> { m1, m2 };
foreach (var menu in GetAllMenuItems(menus))
{
Console.WriteLine(menu);
}
// or
IList<RMenuItem> menus = new List<RMenuItem> { m1, m2 };
foreach (var menu in menus)
{
menu.Accept(new PrintRMenuItemVisitor());
}
You could difine a Flatten method in your class (or as an extension if you prefer) like this
public IEnumerable<RMenuItem> Flatten()
{
foreach (var item in ChildrenItems)
{
yield return item;
}
return ChildrenItems.SelectMany(item => item.Flatten());
}
then doing somthing with each elements will be as simple as
RMenuItem rootItem ;
// do somthing with the root item
foreach (var item in rootItem.Flatten())
{
// do somthing
}
Indeed you can do that using LINQ, SelectMany flats out the list, just some example
menuItemsList.SelectMany(x => x.ChildrenItems).Where(c => c.someChildProperty);
Thanks
Edit:
In response to the comments, I was just giving an example of SelectMany previously. Thanks for pointing out.
menuItemsList.SelectMany(x => x.ChildrenItems.Select(p => p)).Where(c => c.someChildProperty);
OR something like this
menuItemsList.SelectMany(x => x.ChildrenItems).Select(p => p).Where(c => c.someChildProperty);
Edit2
Ahh .. now I understood what you want ..
We can just slightly modify my above query to do what you want
menuItemsList
.SelectMany(x => { //do something with x like printing it
x.ChildrenItems
})
.Select(p => { // do something with p like printing it
p
});
Basically you can do what you want the element inside the {}
Thanks

How to retrieve ordering information from IQueryable object?

Let's say, I have an instance of IQueryable. How can I found out by which parameters it was ordered?
Here is how OrderBy() method looks like (as a reference):
public static IOrderedQueryable<T> OrderBy<T, TKey>(
this IQueryable<T> source, Expression<Func<T, TKey>> keySelector)
{
return (IOrderedQueryable<T>)source.Provider.CreateQuery<T>(
Expression.Call(null,
((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(
new Type[] { typeof(T), typeof(TKey) }
),
new Expression[] { source.Expression, Expression.Quote(keySelector) }
)
);
}
A hint from Matt Warren:
All queryables (even IOrderedQueryable's) have expression trees underlying them that encode the activity they represent. You should find using the IQueryable.Expression property a method-call expression node representing a call to the Queryable.OrderBy method with the actual arguments listed. You can decode from the keySelector argument the expression used for ordering. Take a look at the IOrderedQueryable object instance in the debugger to see what I mean.
This isn't pretty, but it seems to do the job:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Windows.Forms;
public class Test
{
public int A;
public string B { get; set; }
public DateTime C { get; set; }
public float D;
}
public class QueryOrderItem
{
public QueryOrderItem(Expression expression, bool ascending)
{
this.Expression = expression;
this.Ascending = ascending;
}
public Expression Expression { get; private set; }
public bool Ascending { get; private set; }
public override string ToString()
{
return (Ascending ? "asc: " : "desc: ") + Expression;
}
}
static class Program
{
public static List<QueryOrderItem> GetQueryOrder(Expression expression)
{
var members = new List<QueryOrderItem>(); // queue for easy FILO
GetQueryOrder(expression, members, 0);
return members;
}
static void GetQueryOrder(Expression expr, IList<QueryOrderItem> members, int insertPoint)
{
if (expr == null) return;
switch (expr.NodeType)
{
case ExpressionType.Call:
var mce = (MethodCallExpression)expr;
if (mce.Arguments.Count > 1)
{ // OrderBy etc is expressed in arg1
switch (mce.Method.Name)
{ // note OrderBy[Descending] shifts the insertPoint, but ThenBy[Descending] doesn't
case "OrderBy": // could possibly check MemberInfo
members.Insert(insertPoint, new QueryOrderItem(mce.Arguments[1], true));
insertPoint = members.Count; // swaps order to enforce stable sort
break;
case "OrderByDescending":
members.Insert(insertPoint, new QueryOrderItem(mce.Arguments[1], false));
insertPoint = members.Count;
break;
case "ThenBy":
members.Insert(insertPoint, new QueryOrderItem(mce.Arguments[1], true));
break;
case "ThenByDescending":
members.Insert(insertPoint, new QueryOrderItem(mce.Arguments[1], false));
break;
}
}
if (mce.Arguments.Count > 0)
{ // chained on arg0
GetQueryOrder(mce.Arguments[0], members, insertPoint);
}
break;
}
}
static void Main()
{
var data = new[] {
new Test { A = 1, B = "abc", C = DateTime.Now, D = 12.3F},
new Test { A = 2, B = "abc", C = DateTime.Today, D = 12.3F},
new Test { A = 1, B = "def", C = DateTime.Today, D = 10.1F}
}.AsQueryable();
var ordered = (from item in data
orderby item.D descending
orderby item.C
orderby item.A descending, item.B
select item).Take(20);
// note: under the "stable sort" rules, this should actually be sorted
// as {-A, B, C, -D}, since the last order by {-A,B} preserves (in the case of
// a match) the preceding sort {C}, which in turn preserves (for matches) {D}
var members = GetQueryOrder(ordered.Expression);
foreach (var item in members)
{
Console.WriteLine(item.ToString());
}
// used to investigate the tree
TypeDescriptor.AddAttributes(typeof(Expression), new[] {
new TypeConverterAttribute(typeof(ExpandableObjectConverter)) });
Application.Run(new Form
{
Controls = {
new PropertyGrid { Dock = DockStyle.Fill, SelectedObject = ordered.Expression }
}
});
}
}

Resources