This question already has answers here:
How to pass anonymous types as parameters?
(11 answers)
Closed 9 years ago.
var emp = (from a in AdventureWorks.PersonPhones
join b in AdventureWorks.People
on a.BusinessEntityID equals b.BusinessEntityID
join c in AdventureWorks.PhoneNumberTypes
on a.PhoneNumberTypeID equals c.PhoneNumberTypeID
select new { a, b, c }).OrderBy(n => n.c.Name);
I have this linq query which selects values in a anonymous type class.
I just want to pass this query to somemethod() and call toList() on this query stored in "emp" in that method.
Thanks!
It's possible to do this in a way that doesn't care if it's a anonymous type or not - and if you pass a method for picking out the relevant property you can even do some work on it.
private static void Process<T>(IEnumerable<T> data, Func<T, string> selector) {
foreach (var item in data) {
Console.WriteLine(selector(item));
}
}
var items = from i in db.table
select new { property = i.originalProperty, ignored = i.ignored };
Process(items, (item => item.property));
An alternative to this would be to just select the property you want into a new list
var items = from i in db.table
select new { prop1 = i.prop1, prop2 = i.prop2 };
// ... cut ... do something that requires everything in items ...
IEnumerable<string> justFirstProperties = items.Select(i => i.prop1);
Process(justFirstProperties);
You can't pass anonymous types around in a strongly typed way (only as object). You would have to create a type to represent the anonymous type structure, use reflection on the object type, or do the ToList() etc. work in this method.
You could pass it as IQueryable<dynamic>
public void SomeQueryMethod(IQueryable<dynamic> query)
{
var result = query.First();
var a = result.a; //dynamic resolution
}
....
SomeQueryMethod(emp); //this should work
But in the method all property access on the result objects will be "dynamic", without compile time checks.
Related
For example, I have this code:
IQueryable<MyModel> q = new List<MyModel>().AsQueryable(); // this is just an example, this is obviously not a list
var query = from item in q select new { item.Property };
var oneItem = query.FirstOrDefault(x => x.SomeProperty == somevalue);
var allItems = query.ToArray();
Now in a bit more complex situation, I need to get oneItem and allItems in two different methods. So to follow DRY, i'd like to move my query to a private method and then in the consuming ones just call this.GetQuery().FirstOrDefault() or .ToArray() as required.
However, when I try to have the method as IQueryable<dynamic> I get the 'An expression tree may not contain a dynamic operation' error. If I change it to IQueryable<object> then my filtering in the oneItem doesn't work.
You need to return
IQueryable<MyObject>
You can make your methods/classes dry by using genrics eg
IQuerable<T> GetQueryable()
Then the consumer can specify what T should be and your away.
You can't use dynamic with linq. See here to understand why.
For two methods to communicate they must understand the same type so you really want to project into a named type.
If you insist on using dynamic programming it can be done but you will need a lot of casting because dynamic is not a type but just a way of treating object:
IQueryable<MyModel> q = new List<MyModel>().AsQueryable(); // this is just an example, this is obviously not a list
IQueryable<object> query = from item in q select (object)new { item.Property };
var oneItem = query.FirstOrDefault(x => ((dynamic)x).SomeProperty == somevalue);
object[] allItems = query.ToArray();
I've simple Linq2Sql query:
var result = from t in MyContext.MyItems
select new MyViewModelClass()
{
FirstProperty = t,
SecondProperty = new SomeLinq2SqlEntity()
}
The problem is that it seems that new SomeLinq2SqlEntity() is executed only once for the sequence, so all instances of MyViewModelClass in result of the query share the link to one object.
Update: Here is how I quickly check it:
result[0].SecondProperty.MyField = 10;
Using debugger I can check that MyField was set to 10 in all instances.
When I replace LINQ query with foreach, it works as expected:
var result = from t in MyContext.MyItems select t;
var list = new List<MyViewModelClass>();
foreach (var item in result)
{
list.add(new MyViewModelClass()
{
FirstProperty = item,
SecondProperty = new SomeLinq2SqlEntity()
});
}
I haven't found the root of the problem, but the post marked as asnwer provides good workaround. Check this asnwer for the detailed description: "new" inside concrete type projection is only called once
It probably has something to do with weird IQueryable implementation of your provider.
Aducci's answer extracts data from database with AsEnumerable() call and performs the query on that set, which is different from performing it through IQueryable.
For example IQueryable builds the ExpressionTree which it later parses according to the concrete provider (i.e. executing shared code once for optimization), while IEnumerable accepts Func and performs it as you would expect.
You can read more here:
http://msdn.microsoft.com/en-us/vcsharp/ff963710
Have you tried using adding the SomeLinq2SqlEntity object with linq to objects?
var result = (from t in MyContext.MyItems
select new
{
FirstProperty = t
})
.AsEnumerable()
.Select(t => new MyViewModelClass()
{
FirstProperty = t.FirstProperty ,
SecondProperty = new SomeLinq2SqlEntity();
});
I have IQueryable<> object.
I want to Convert it into List<> with selected columns like new { ID = s.ID, Name = s.Name }.
Edited
Marc you are absolutely right!
but I have only access to FindByAll() Method (because of my architecture).
And it gives me whole object in IQueryable<>
And I have strict requirement( for creating json object for select tag) to have only list<> type with two fields.
Then just Select:
var list = source.Select(s=>new { ID = s.ID, Name = s.Name }).ToList();
(edit) Actually - the names could be inferred in this case, so you could use:
var list = source.Select(s=>new { s.ID, s.Name }).ToList();
which saves a few electrons...
Add the following:
using System.Linq
...and call ToList() on the IQueryable<>.
The List class's constructor can convert an IQueryable for you:
public static List<TResult> ToList<TResult>(this IQueryable source)
{
return new List<TResult>(source);
}
or you can just convert it without the extension method, of course:
var list = new List<T>(queryable);
System.Linq has ToList() on IQueryable<> and IEnumerable<>. It will cause a full pass through the data to put it into a list, though. You loose your deferred invoke when you do this. Not a big deal if it is the consumer of the data.
Here's a couple of extension methods I've jury-rigged together to convert IQueryables and IEnumerables from one type to another (i.e. DTO). It's mainly used to convert from a larger type (i.e. the type of the row in the database that has unneeded fields) to a smaller one.
The positive sides of this approach are:
it requires almost no code to use - a simple call to .Transform<DtoType>() is all you need
it works just like .Select(s=>new{...}) i.e. when used with IQueryable it produces the optimal SQL code, excluding Type1 fields that DtoType doesn't have.
LinqHelper.cs:
public static IQueryable<TResult> Transform<TResult>(this IQueryable source)
{
var resultType = typeof(TResult);
var resultProperties = resultType.GetProperties().Where(p => p.CanWrite);
ParameterExpression s = Expression.Parameter(source.ElementType, "s");
var memberBindings =
resultProperties.Select(p =>
Expression.Bind(typeof(TResult).GetMember(p.Name)[0], Expression.Property(s, p.Name))).OfType<MemberBinding>();
Expression memberInit = Expression.MemberInit(
Expression.New(typeof(TResult)),
memberBindings
);
var memberInitLambda = Expression.Lambda(memberInit, s);
var typeArgs = new[]
{
source.ElementType,
memberInit.Type
};
var mc = Expression.Call(typeof(Queryable), "Select", typeArgs, source.Expression, memberInitLambda);
var query = source.Provider.CreateQuery<TResult>(mc);
return query;
}
public static IEnumerable<TResult> Transform<TResult>(this IEnumerable source)
{
return source.AsQueryable().Transform<TResult>();
}
Ok, I have managed to get the following working
public IQueryable getTicketInformation(int ticketID)
{
var ticketDetails = from tickets in _context.tickets
join file in _context.file_objects on tickets.ticket_id equals file.source_id
where tickets.ticket_id == ticketID
select new { tickets.ticket_id, tickets.title, tickets.care_of_email, file.filename };
return ticketDetails.AsQueryable();
}
I went ahead and created my own class (myObject) containing the primitives
ticket_id, title, care_of_email and filename. Which are the items I am returning in my linq statement.
I modified my statement to be
public IQueryable<myObject> getTicketInformation(int ticketID)
{
var ticketDetails = from tickets in _context.tickets
join file in _context.file_objects on tickets.ticket_id equals file.source_id
where tickets.ticket_id == ticketID
select new { tickets.ticket_id, tickets.title, tickets.care_of_email, file.filename };
return ticketDetails.AsQueryable()<myObject>;
}
thinking that this would make it type safe with generics, but I get the error
"Cannot convert method group 'AsQueryable' to non-delegate type 'System.Linq.IQueryable'. Did you intend to invoke the method?"
Is what I am trying to do even possible?
Does the myObject class need to implement IEnumerable or IQueryable?
Or is it best to construct the object MyObject from the linq resultset and then just return from the function the object MyObject
public myObject getTicketInformation(int ticketID)
{
....linq statement....
myObject o = null;
foreach (obj in linqstatemt)
{
o = new myObject();
o.ticket_id = obj.ticket_id
.......
}
return o;
}
You mean:
select new MyObject { TicketId = tickets.ticket_id,
Title = tickets.title, ...};
(note I tweaked the names slightly to be more C#-idiomatic)
This is an "object initializer" that creates a new MyObject (per record) and assigns the properties from the source data. What you had was an "anonymous type" initializer, which isn't the same. Note that if you have a non-default constructor, you could also use something like:
select new MyObject(tickets.ticket_id, tickets.title);
which uses the specified constructor, passing in the supplied values from the source data.
This will then be IQueryable<MyObject>; you don't need to call .AsQueryable(). Note it would be better for your function to return the typed form (IQueryable<MyObject>) than the untyped IQueryable.
This line is syntactically incorrect:
return ticketDetails.AsQueryable()<myObject>;
and should read
return ticketDetails.AsQueryable<myObject>();
Also, you're creating anonymous objects with the select new {, but you want to create myObject instances. A correct implementation would look like this:
public IQueryable<myObject> getTicketInformation(int ticketID)
{
return from tickets in _context.tickets
join file in _context.file_objects on tickets.ticket_id equals file.source_id
where tickets.ticket_id == ticketID
select new myObject() {
ticket_id = tickets.ticket_id,
title = tickets.title,
care_of_email = tickets.care_of_email,
filename = file.filename
};
}
The new SomeClass() { Property = value, ... syntax creates a SomeClass instance and sets the properties to the given values. Alternatively you could implement a constructor on the myObject class and call it in the linq statement with select new myObject(...).
As Marc stated you're not constructing instances of myObject when your query is run. But additionally you don't need to cast it to an IQueryable<T>, a LINQ select statment will return an IQueryable<T> unless explicity cast to an IEnumerable<T>.
Also, be careful that your DataContext hasn't been disposed of before you try and access the data being returned. But I noticed your context is not constructed in the method, be careful that you're not maintaining a DataContext for too long, it's a unit-of-work object and not meant to be kept open for long periods of time.
Gents,
It all makes sense as long as you're only returning single table, but what if there's two or more to be returned???
RPDTDataContext smdt = new RPDTDataContext();
var projectedUsers = smdt.SM_Users.Join(
smdt.SM_CTSGroups, u => u.CtsGroupID, c => c.id,
(u, c) => new { CTSGroup = c.Name, UserName = u.Name, u.EmpID, u.Email });
return projectedUsers;
I have a field object and I create a list of fields:
class Field {
string objectName;
string objectType;
string fieldName;
string fieldValue;
//constructor...
}
List<Field> fieldList = new List<Field>();
Suppose I wanted to query this list to return a collection of distinct object names (to then be inserted into a checkedlistbox. How would I go about doing that?
I imagine some LINQ magic can manage this?
The expression should return a List of distinct object names from the list as defined. I converted it to a list since the docs for the CheckedListBox DataSource property indicated that it needs to implement IList or IListSource, not merely IEnumerable.
((ListControl)cbListBox).DataSource = fieldList.Select( f => f.objectName )
.Distinct()
.ToList() );
If accessing the checkedListBox as a ListControl doesn't give access to the DataSource (sometimes the docs lie), you could try:
cbListBox.Items.AddRange( fieldList.Select( f => f.objectName )
.Distinct()
.ToArray() );
Either of these work
Using var
1) var fieldNameCollection = from f in fieldList select f.FieldName;
2) Lambda syntax
var fieldNameCollection = fieldList.Select(f => f.FieldName);
Alternately, instead of using var, you can also use
IEnumerable fieldNameCollection = fieldList.Select(f => f.FieldName);
var q = from Field f in fileldList select f.objectName;
chkBoxList.DataSource = q.Distinct();