I am creating a list like following:
var result = data.Select(p => new
{
p.FirstName,
p.LastName,
Relationship = p.RelationshipType,
p.TierType,
Gender = p.GenderType,
p.AnnualSalary
});
However, I need to add more properties into each of the array item of result like following
foreach(var property in ListOfAdditionalProperties)
{
// Add property logic
}
Is this possible?
I tried ExpandoObject but was not able to come up with the final result list that I get with the Lambda mentioned on top.
No, it's not possible.
You cannot add more members to a dynamic type after the type has been created.
Think of it as a class declaration. Once the class is declared and compiled it cannot be modified.
You need to use another approach, just like the ones you mention. But yes, that has it's drawbacks in regards to syntax.
Related
I am trying to do following assert
Assert.AreEqual<string>(A.Drivers[i].FirstName, Response);
Drivers is IEnumerable collection and it has other properties like last name , middle name etc. I'd like to dynamically select the properties of Drivers so it can be done in one method rather than writing different methods for each property
You could do it using reflexion:
get string properties of the Drivers class
var driverProperties = typeof(Drivers).GetProperties().Where(i => i.PropertyType.Equals(typeof(string)));
and then iterate through the properties
foreach (var property in driverProperties)
{
Assert.AreEqual<string>(property.GetValue(A.Drivers[i]), Response);
}
slight change to Dano suggestion. To get one specific property
var prop = typeof(Drivers).GetProperty("propName");
var val = (string)prop.GetValue(A.Drivers[i]);
Recently I asked a more general question about getting properties of model through foreign key.
Now I moved a bit further but still have no idea how transform objects on the fly.
What I've got is an IEnumerable collection which I get through repository
regionRaw = unitOfWork.RegionRepository.Get(
keyOrder: q => q.OrderBy(d => d.RegionID),
filter: p => p.FullName.Contains(lastname) || p.ShortName.Contains(lastname),
orderBy: jtSorting,
includeProperties: "District, ISO31662, GOST767Region");
Further I am going to export data from this collection to Excel. So I need a select statement that gets all the fields I need.
dt = regionRaw
.Select(x => new
{
ISO = x.ISO31662.GOSTName,
DistrictName = x.District.ShortName
})
I do not want to enumerate all the fields I need like on the top.
I am able to make a method that recognizes which of the fields have simple values and which have objects referenced through foreign key. And then that method will return a list of properties.
Now I need some way to write something like a foreach inside select. I see something like this:
dt = regionRaw
.Select(x => new
{
foreach (prop in propList)
{
prop.PropertyName = x.GetType()
.GetProperty(prop.TableName)
.GetValue(x, null).GetType()
.GetProperty(prop.PropertyName)
.GetValue(
x.GetType().GetProperty(prop.TableName).GetValue(x, null),
null);
}
}
Where propList is a collection of properties that I get before.
I do totally realize that upper code is more a pseudo-code but I have no idea how to realize this in .NET.
So if you can suggest some solution for this task I will be very grateful. Or maybe you could explain that all this is a bad idea and shouldn't be realized.
You wont be able to create an anonymous type with dynamic properties as anon types are created during compile and your properties are created during execution.
But why do you need strongly typed properties if you're not going to code against them, as you wont know them until someone executes the query?
Expando object may be of use to you?http://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject.aspx
[update]
I'm sorry, i should tag this question
as MVC-2, I pass result of query into
view's model, so i must specify type
of my model in View's header
defintion. I declare it like this:
Inherits="System.Web.Mvc.ViewPage<IQueryable<dynamic>>"
how ever nothing changed and none of
answers doesn't work for me :(.
finally i used an ModelView class as
helper to put my query result in it.
:(
[/update]
I have a query like this:
IQueryable<dynamic> result = from d in KiaNetRepository.KiaNetEntities.Discounts
where d.AgentTypeID == agentTypeId
select new { d.Category, d.DiscountValue, d.PriceConfige };
then i retrive value in my view like this:
foreach(var item in result){
Category cat = item.Category; // throws exception 'object' does not contain a definition for 'Category'
//...
}
note that type of query as IQueryable is anonymouse class...
Try to declare names explicitly:
select new { Category = d.Category, DiscountValue = d.DiscountValue, PriceConfige = d.PriceConfige }
If you are not forcing result to be of IQueryeable<dynamic> for any specific reason, I would recommend using var result = .... This will let the compiler make result of type IQueryable<T> with T the type of the anonymous class that you create using new { ... } in the select. There is no necessity for using dynamic from the code that you show here.
If you replace the inappropriate declaration IQueryable<dynamic> by var, sure it works, I've just also tested it.
Your problem is that your foreach loop being in the view page gets compiled into a separate assembly. Since anonymous types are internal the dynamic doesn't see it because of the permissions don't allow it.
Simplest fix is to call ToList() on your query statement and then select each anonymous type and copy parameters to a declared class or expandoobject.
When I do a query that returns an anonymous type
var assets =
from Product p in Session.CreateLinq<Product>()
where bundles.Contains(p.ProductBundle)
select new {p.Asset, p.Asset.PropertyTbl};
Can I type the return to anything other than var?
You cannot* return an anonymous type because the caller would not know what type it is and wouldn't be able to use it.
If you want to return the results, you can create objects of a non-anonymous type:
IEnumerable<Foo> assets =
from Product p in Session.CreateLinq<Product>()
where bundles.Contains(p.ProductBundle)
select new Foo { Bar = p.Asset, Baz = p.Asset.PropertyTbl};
You can also use the Tuple type in .NET 4 if you don't want to create a custom class for your values.
* This is not strictly true - it is possible but you should avoid doing it. Here is a link anyway if you really want to.
You can use object or dynamic (in .NET 4.0) instead of var but don't expect to find a name to an anonymous type. In your case using var is better as it will preserve the strong typing at least until you leave the scope of the current method.
You could define a new class:
public class AssetProp
{
public virtual string Asset {get;set;}
public virtual string PropertyTbl {get;set;}
}
And then you can return it as that class:
IEnumerable<AssetProp> assets =
from Product p in Session.CreateLinq<Product>()
where bundles.Contains(p.ProductBundle)
select new AssetProp {p.Asset, p.Asset.PropertyTbl};
Not really, since the new {p.Asset, p.Asset.PropertyTbl} code creates an anonymous type. Even using object doesn't really gain you much since you can't cast it to anything useful later on, so you would have to use reflection to access the properties.
Not really. If you cast to object you wont be able to access the properties of your anonymous class.
The var keyword was specifically introduced for dealing with anonymous classes - why would you want to avoid it? If you need to return the data you should name the class.
You can if you use lambda expressions, otherwise you can do a cast but do some good exception handling.
you can also do this (it does relate much to your problem though, because you just move "var" somewhere else, but it's interesting that it recognize those types as same)
var element = new { id = 7 };
List<object> collection = new List<object>();
element = collection.Select(item => new { id = 0 }).First();
Is there a way to add a property to the objects of a Linq query result other than the following?
var query = from x in db.Courses
select new
{
x.OldProperty1,
x.OldProperty2,
x.OldProperty3,
NewProperty = true
};
I want to do this without listing out all of the current properties of my object. There are many properties, and I don't want to have to update this code whenever I may change my class.
I am still learning with LINQ and I appreciate your suggestions.
Add it with partial classes:
public partial class Courses
{
public String NewProperty { get; set; }
}
Then you can assign it after you've created the object.
I suppose you could return a new object composed of the new property and the selected object, like this:
var query = from x in db.Courses
select new
{
Course = x,
NewProperty = true
};
eking's answer will be the most straightforward approach.
If that doesn't work for you (because you need to pass the results around or whatever), and assuming the class you're dealing with already defines the property you want to set, you could create a copy constructor or factory method that takes an existing instance plus the value of the property you want to set:
var query = from x in db.Courses
select new Course(x, valueOfNewProperty);
Alternatively, if Course doesn't define the property, you could subclass it and use the same approach:
var query = from x in db.Courses
select new CourseWithExtraProperty(x, valueOfNewProperty);
(obviously, pick a better name for your subclass)
Again, though, unless you really need to do this, stick with eking's solution.
ServiceStack has a built-in way to handle this with the PopulateWith method.
Here's a code example.
foreach (var item in results)
{
var test1 = new ItemDto().PopulateWith(item);
test1.extraField1 = "extra";
response.Add(test1);
}`
And if you're not using ServiceStack, you can always use AutoMapper.
CreateMap<Foo, Bar>().ForMember(x => x.ExtraBarProperty, opt => opt.Ignore());
If you are looking to dynamically add a property to an object this could be a solution.
This is what has worked for me, I also had a concern and it was what happened with those domain objects that had many properties, the maintainability for any changes in the object was absurd, I managed to build an implementation with LINQ - ExpandObject - Reflection, which helped to keep my object dynamic and only add the additional properties that my view logic required.
var expandedModel = db.Courses.Select(x =>
{
dynamic expandObject = new ExpandoObject();
expandObject.NewProperty= $"PropertyValue";
foreach (var property in x.GetType().GetProperties())
{
((IDictionary<string, object>)expandObject).Add(property.Name, property.GetValue(x));
}
return expandObject;
}).ToList();