WCF REST's Extention to IEnumerable Lambda Func<TSource, TKey> keySelector - c#

I am using WCF REST Preview 2 to test some REST services. The package has an extension to IEnumerable as ToDictionary(Func(TSource, TKey) keySelctor. Not sure how to define a lambda function to return keySelector?
Here is one example:
var items = from x in entity.Instances // a customized Entity class with list instances of MyClass
select new { x.Name, x};
Dictionary<string, MyClass> dic = items.ToDictionary<string, MyClass>(
(x, y) => ... // what should be here. I tried x.Name, x all not working
Not sure what should be the lambda Func should be to return a KeySelector?

Since items is of type IEnumerable<MyClass>, you should be able to do the following:
items.ToDictionary(x => x.Name)
You could even have done:
entity.Instances.ToDictionary(x => x.Name)
You don't need to specify the type parameters, since they can be correctly inferred.
Edit:
items.ToDictionary(x => x.Name) is actually incorrect, because items is not of type IEnumerable<MyClass>. It is actually an IEnumerable of the anonymouse type, that has 2 properties (Name, which contains the myClass.Name property, and x, which is of type MyClass).
In that case, assuming you can do:
var items = from instance in entity.Instances
select new { Name = instance.Name, // don't have to specify name as the parameter
Instance = instance
};
var dict = items.ToDictionary(item => item.Name,
item => item.Instance);
The second example is a bit easier to use in this case. Essentially, you don't get any value from the linq query to get items, if all you're trying to do is generate a dictionary.

Related

Create Object from Anonymous Type

I'm using EntityFramework to a list of objects from the database and I'm using Anonymous Types to eventually return the right object. Because there are several functions that has to do this 'Anonymous type conversion' I want to extract this to functions.
I can create a function to create a dynamic but can't create a function that converts a dynamic in a specific type because if the function has a parameter that contains a dynamic, the return type is a dynamic type too.
This works:
List<SomeObject> list = list
.Select(i => GetAnonymousType(i))
.Select(i => new SomeObject {Item1 = i.Item1, Item2 =i.Item2}).ToList();
This doesn't:
List<SomeObject> list = list
.Select(i => GetAnonymousType(i))
.Select(i => CreateSomeObjectFromDynamic(i)).ToList();
private static SomeObject CreateSomeObjectFromDynamic(dynamic i)
{
return new SomeObject {Item1 = i.Item1, Item2 = i.Item2};
}
See: https://dotnetfiddle.net/zLFlur
Is there a way I can use a function like: CreateSomeObjectFromDynamic to return the right type?
try this:
list = list
.Select(i => GetAnonymousType(i))
.Select(i => CreateSomeObjectFromDynamic(i) as SomeObject).ToList();
According to provided code at fiddle, your problem is not with CreateSomeObjectFromDynamic method, your problem is with GetAnonymousType method. This method returns dynamic and .NET cannot handle it. The compliler error says that:
Cannot implicitly convert type
'System.Collections.Generic.List<dynamic>' to
'System.Collections.Generic.List<SomeObject>'
Even changing query to
list = list
.Select(i => CreateSomeObjectFromDynamic(GetAnonymousType(i)))
.ToList();
will produce the same error. But, if you change return type of GetAnonymousType method to object as below:
private static object GetAnonymousType(SomeObject i)
{
return new { Item1 = i.Item1, Item2 = i.Item2 };
}
your problem will be solved. But, this works in memory, I am not sure that it will successfully be translated into SQL if you try to use it with IQueryable for example. Also, I do not recomend using dynamic, even though it works after chaning return type to object your code does not seem right. If you make a bit effort, I am sure that you will find another way, for example using inheritance, generics and etc.

How to create dictionary from a grouped elements?

I have a list with elements that I wanted to group by the value of a property. Later I would like to create a dictionary which key is this value that I used to group and the value a list (or IEnumerable) of the elements that are each group.
I am trying something like that:
Dictionary<long, Ienumerable<MyType>> dic = lstWithElements.GroupBy(x=>x.ID).ToDictionary(x=>x.????)
But in the ToDictionary method I don't have the ID property. So, how could I create my dictionary with the grouped items?
The overload of GroupBy that you're using returns an IEnumerable<IGrouping<long, MyType>>. IGrouping<long, MyType> provides a Key property of type long, representing the projected value by which elements were grouped, and also implements IEnumerable<MyType>.
So essentially, what you need is:
var dic = lstWithElements.GroupBy(x => x.ID).ToDictionary(x => x.Key);
Note: As pointed out in comments, this produces an IDictionary<long, IGrouping<long, MyType>>. This isn't really a problem, as long as you're only retrieving elements from the dictionary, and not trying to add new IEnumerable<MyType>s later on (which seems unlikely). If you do need precisely an IDictionary<long, IEnumerable<long, MyType>>, use the code outlined in this answer.
The ToDictionary method has a couple of overloads, but since your Dictionary uses an IEnumerable<MyType> for its Value, you're probably interested in the overload that accepts two parameters: a key selector, and an element selector.
Dictionary<long, IEnumerable<MyType>> dic = lstWithElements.GroupBy(x=>x.ID).ToDictionary(x=> x.Key, x => x.AsEnumerable());
Try this:
Dictionary<long, IGrouping<long,MyType>> dic = lstWithElements.GroupBy(x=>x.ID).ToDictionary(x=>x.Key)
I presume, that your data structure is at minimum this:
class MyType //or struct
{
long ID;
};
You want a list:
List<MyType> list;//with instances of MyType
either with different instances of MyType and same ID(making ID non-unique, is not best design perhaps) or some instances are in the list multiple times, what seems to be a better case, but either will work for question asked.
Now, GroupBy, what it does? List
List<MyType>
is transformed to
IEnumerable<MyType>
then GroupBy(x => x.ID) is grouping and providing:
IEnumerable<IGrouping<long, MyType>>
so we get elements of
IGrouping<long, MyType>
Now IGrouping knows everything IEnumerable does, interface inheritance, plus it has Key. So if you want your expected dictionary type:
Dictionary<long,IEnumerable<MyType>>
you have to do this:
var dictionary =
list
.GroupBy(x => x.ID)
.ToDictionary(x => x.ID, x => x.AsEnumerable())
;
ToDictionary allows to chose the Key from elements and also allows to transform Value stored for given key, so we can use this approach and call
x.AsEnumerable()
as IGrouping is inherited from IEnumerable.
Hope this longer explanation helps :).

Return a List with multiple results using LINQ and EF

I have this simple method that returns a user:
User usr = ReliableExecution.RetryWithExpression<User, User>(u => u.FirstOrDefault(x => x.UserEmail == userEmail));
Now I need to create a similar method, but I need to return a list
List<Asset> lst = ReliableExecution.RetryWithExpression<Asset, List<Asset>>(u => u.SelectMany(x => x.EventId == eventId));
My problem is with the [SelectMany(x => x.EventId == eventId)] part that doesn't compile and I can't understand exactly how to use LINQ to get multiple results.
I have specified "SelectMany" just for an example, it can be whatever you find correct.
This is the signature of RetryWithExpression for reference:
public static TValue RetryWithExpression<T, TValue>(Func<ObjectSet<T>, TValue> func, Int32 retryInfiniteLoopGuard = 0)
where T : class
I think your expression should be rewritten as follows:
List<Asset> lst = ReliableExecution
.RetryWithExpression<Asset, List<Asset>>(
u => u.Where(x => x.EventId == eventId).ToList()
);
In simple terms, SelectMany flattens a "list of lists of items A" into a "list of items B" using a functor that extracts a list of items B from each single item A; this is not what you want to do.
It seems like you want:
List<Asset> lst = ReliableExecution.RetryWithExpression<Asset, List<Asset>>
(u => u.SelectMany(x => x.Where(y => y.EventId == eventId)));
SelectMany expects the passed Func to return an IEnumerable, which it then flattens. You were passing through a list of Asset and then trying to select the EventId directly on the list. What you really wanted was to select all Assets in the list with matching EventId, hence the extra Where clause.

Using Linq to objects, ToDictionary method not satisfying type requirement

I need to pass a variable of type IDictionary<Guid, IEnumerable<Guid>> into a method. When I use the following code:
var userGroupDictionary = _selectedUsers.ToDictionary(u => u.UserGuid, u => u.GroupUsers.Select(gu => gu.Group.GroupGuid).ToList());
I get the following error:
cannot convert from System.Collections.Generic.Dictionary<System.Guid,System.Collections.Generic.List<System.Guid>>' to 'System.Collections.Generic.IDictionary<System.Guid,System.Collections.Generic.IEnumerable<System.Guid>>'
When I iterate through my collection it works however:
var userGroupDictionary = new Dictionary<Guid, IEnumerable<Guid>>();
foreach (var user in _selectedUsers.Where(user => !userGroupDictionary.ContainsKey(user.UserGuid)))
{
userGroupDictionary.Add(user.UserGuid, user.GroupUsers.Select(gu => gu.Group.GroupGuid).ToList());
}
Any ideas what's going on? Is the compiler unable to tell that Dictionary<Guid, List<Guid>> satisfies IDictionary<Guid, IEnumerable<Guid>>
The problem is that type inference is returning a Dictionary<Guid, List<Guid>>, which is the wrong type for the method you want to call. There's a bit more to it than that, but it involves a type variance discussion. You can solve the problem with the AsEnumerable extension:
var userGroupDictionary = _selectedUsers
.ToDictionary(
u => u.UserGuid,
u => u.GroupUsers.Select(gu => gu.Group.GroupGuid).ToList().AsEnumerable());
...or you can do a simple cast, as in the following:
var userGroupDictionary = _selectedUsers
.ToDictionary(
u => u.UserGuid,
u => (IEnumerable<Guid>)u.GroupUsers.Select(gu => gu.Group.GroupGuid).ToList());
If you merely omit the ToList() as others have recommended, the type inference will indeed render the type you expect, but the IEnumerable returned will have deferred-execution which may or may not be an issue for you. Leaving ToList in there but doing a cast of sorts will immediately evaluate the items instead of lazily evaluating them at the time of iteration.
For more detail on deferred execution, see the "laziness" (think lazy-evaluation) section of Jon Skeet's Edulinq series, part 44
Line below creates a dictionary where a key of type Guid and a value of type List<Guid>.
_selectedUsers.ToDictionary(
u => u.UserGuid,
u => u.GroupUsers.Select(gu => gu.Group.GroupGuid).ToList()
);
So just remove .ToList() and that's it, u.GroupUsers.Select() itself returns IEnumerable<Guid>.
And you able iterating on both Values since List implementes IEnumerable as well, this is enought to satisfy foreach iterator.
Is the compiler unable to tell that Dictionary<Guid, List<Guid>> satisfies IDictionary<Guid, IEnumerable<Guid>>
No, the compiler is able to tell that Dictionary<Guid, List<Guid>> doesn't satisfy IDictionary<Guid, IEnumerable<Guid>>. The latter provides, for example, a method Add(Guid, IEnumerable<Guid>), which your dictionary cannot support.
You can explicitly state the allowed values in your dictionary, for example using
_selectedUsers.ToDictionary<User, Guid, IEnumerable<Guid>>(
u => u.UserGuid,
u => u.GroupUsers.Select(gu => gu.Group.GroupGuid).ToList())
I think all of u.GroupUsers have Same groupID. You can get first groupID,
If you can Access groupID property on user class. You should use GroupID.
Like _selectedUsers.ToDictionary(u=>u.UserGuid,u.GroupID);
If you cant Access to GroupId on user class.
Use :
_selectedUsers.ToDictionary(
u => u.UserGuid,
u => u.GroupUsers.Select(gu => gu.Group.GroupGuid).First());

Lambda expression for querying an array of KeyValuePairs using Expression<Func<T, bool>>

I have an array of KeyValuePairs like this:
KeyValuePair<long,int>[] orderItems;
The 'long' component of this KeyValuePair corresponds to an Id (primary key) value on a database table.
Using the Entity Framework, I have defined a repository which provides the following method on its interface:
IEnumerable<T> GetMany(Expression<Func<T, bool>> where);
The above repository method allows me to query the database using lambda expressions. For example, if I want to query the database for all rows where the 'Category' column equals 'Cameras' I can say this:
var results = repository.GetMany(a => a.Category.Contains("Cameras")).ToList();
What I want to query for is all rows where the Id (primary key) is equal to the Key value from any element of the array of KeyValuePairs. So if the list of KeyValuePairs is a list of product Ids and their quantities , I just want to query the database for all products whose Id is in my array.
Can anyone venture an appropriate lambda expression?
**EDIT TO ORIGINAL
The suggestion to use:
var results = repository.GetMany(a => keys.Any(x => x == a.Id)).ToList();
is a good one and will work if I first create an array of the keys from the KeyValuePair array. So something like this:
long[] pids = new long[orderItems.Length];
for (int i = 0; i < orderItems.Length; i++ )
{
pids[0] = orderItems[i].Key;
}
var products = productRepository.GetMany(a => pids.Any(x => x == a.Id)).ToList();
This shows that the 'Any' clause is supported by EF. I still can't get this to work without using the array of longs though. Kudos to anyone clever enough to give a solution that doesn't require me to extract the 'key' value from the KeyValuePair into an array first.
If I use the keys.Any I get the following exception:
"Unable to create a constant value of type 'System.Collections.Generic.IEnumerable`1'. Only primitive types (for instance Int32, String and Guid) are supported in this context"
Try this:
var keys = orderItems.Select(x => x.Key).ToList();
var results = repository.GetMany(a => keys.Any(x => x == a.Id))
.ToList();
it's hard to use the GetMany-Method while having the EF do it's work with the Database in this case.
But you should have a query-Object named after your entities - I take here Categories (as a queryable object of a Type with an field/property Id) as a example. Then you can do:
var query = context.Categories;
foreach(var pair in orderedItems)
query = query.Union(context.Where(categorie => categorie.Id = pair.Key));
and finally using the query:
var results = query.ToList();
PS: to use this with your repository pattern you can just expose this queryable-Collection as IQueryable within your repository interface (or make a inherited interface from your generic repository for example ICategoryRepository)

Categories