I have a collection IEnumerable. In a LINQ query, preferably, I would like to select only the properties in this collection from type T, into an anonymous type, where T is a POCO business object.
Example:
My IEnumerable contains properties "Name", "Age".
My POCO is:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string Address { get; set; }
public string Phone { get; set; }
}
I want to achieve the same effect as below, but without hard-coding the members of the anonymous type, and instead using my PropertyInfo collection.
IEnumerable<Person> peeps = GetPeople();
var names = from p in peeps
select new {Name = p.Name, Age = p.Age};
If I was using Entity Framework, I could use Entity SQL with a dynamically constructed string where clause, but then although not strictly hard-code, I'm still using string names of the properties.
Could I not perhaps dynamically construct an expression for the .Select projection method that determines which properties are included in the result object?
You can't do that. The compiler needs to know statically the type of the items in the enumeration, even if it's an anonymous type (the var keyword denotes implicit typing, not dynamic typing)
Why do you need to do that ? If you explain what your requirement is, we can probably suggest another way to do it
Related
Lets suppose that I have a Cosmos Db collection that owns the documents explained by these 2 classes
public class DocumentBase {
public string DocumentType {get; set;}
}
public class DocumentA : DocumentBase {
public string PropertyA {get; set;}
}
public class DocumentB : DocumentBase {
public string[] PropertyA {get; set;}
}
How can I query this collection in a way that for DocumentA the clause is PropertyA = value and for DocumentB the clause is PropertyA contains Value?
The Query needs to be generated automatically depending on input values.
EDIT: Updated question content.
You can use the query below. If propA is undefined or not the appropriate type that part of the expression results in a false and not throw any error. So you can safely query over both types.
SELECT * FROM c
WHERE ARRAY_CONTAINS(c.propA, "myValue") OR c.propA = "myValue"
You can logically separate your documents in Cosmos to ensure you are querying the correct ones. I've used two approaches:
Use a type property to distinguish between types
Use the type as (the/part of the) partition key
In either case, you can add the type as part of the query, either as a WHERE clause like WHERE c.type = 'DocumentA' AND ... <other expressions>, or a just with the partition key in the SDK operation when partitioned by type.
With this setup, there's no problem with querying against a property of the same name using different expression types. Of course, it might be less confusing to use different names.
I have been tasked with writing a routine against an existing database. This database has several tables that have identical structures, but different names (I did not design this, please do not suggest database design change). I am writing this in EF, and the models were created database-first.
The best option I can think of for this situation is to create a class that has the exact same properties, and create a routine that can accept generic types to copy data from the EF model to the generic model.
My models:
// Sample EF database-first created model
namespace SampleDataModel.Models
{
using System;
using System.Collections.Generic;
public partial class SampleClassFlavorOne
{
public int Id {get; set;}
public string PropertyOne {get; set;}
public string Property2 {get; set;}
public DateTime Property3 {get; set;}
}
}
// Sample of generic class I created
public class GenericSampleClass{
public int Id {get; set;}
public string PropertyOne {get; set;}
public string Property2 {get; set;}
public DateTime Property3 {get; set;}
}
My routine:
private static void CopyFlavorToGenericList<T1, T2>(List<T1> fromList, List<T2> toList){
foreach (var t in fromList)
{
//(As you can see, I have tried entering the foreach loop a both ways
//foreach (var p in typeof(T1).GetProperties())
foreach (var p in typeof(T2).GetProperties())
{
if (p != null && p.CanWrite)
{
dynamic newObject = null;
p.SetValue((T2)newObject, p.GetValue(t, null), null);
}
}
toList.Add(toObject);
}
}
Implementing the routine:
switch (flavor){
case "FlavorOne":
List<SampleClassFlavorOne> _baseFlavor = db.SampleClassFlavorOne.ToList();
List<GenericSampleClass> _genericFlavor = new List<GenericSampleClass>();
CopyFlavorToGenericList<SampleClassFlavorOne, GenericSampleClass>(_baseFlavor, _genericFlavor);
break;
}
No matter what I try, I always get:
An exception of type 'System.Reflection.TargetException' occurred in mscorlib.dll but was not handled in user code. Additional information: Object does not match target type.
I cannot figure out what I am missing.
What may I be missing?
Am I going about this the wrong way? If so, what is the correct way to do what I want to do given these conditions?
Any help appreciated, thanks!
Your call to GetProperties() gets an array of PropertyInfo objects that apply to that specific type. Thus, when you call GetValue(), you are trying to get the value from an object of the wrong type.
I.e. T2, the type used to get the PropertyInfo object, is GenericSampleClass, but the type of the object you pass to the GetValue() method is SampleClassFlavorOne. In your alternative, getting the properties from T1, you have the same problem, but with the SetValue() method, passing (in theory…but not really, see "Note:" below) an object of type GenericSampleClass, when the PropertyInfo object came from the SampleClassFlavorOne type.
To do this correctly, you need to get the PropertyInfo objects from both classes, and use them with the objects of the appropriate type. For example:
private static void CopyFlavorToGenericList<T1, T2>(List<T1> fromList, List<T2> toList) where T2 : new()
{
var map = from p1 in typeof(T1).GetProperties()
join p2 in typeof(T2).GetProperties()
on p1.Name equals p2.Name
select new { From = p1, To = p2 };
foreach (var t in fromList)
{
T2 toObject = new T2();
foreach (var copyItem in map)
{
if (copyItem.To.CanWrite)
{
copyItem.To.SetValue(toObject, copyItem.From.GetValue(t));
}
}
toList.Add(toObject);
}
}
Note: you also had an issue with how you were creating the new object. I don't even know what you meant, using dynamic like that, but it wouldn't have worked. You were just passing null as the value of the destination object, which doesn't do anything useful.
You need to be able to create instances of the destination object as needed, and the way to do that in the generic method is to add the new() constraint to the generic type parameter to require the destination type has a parameterless construction, so that you can in fact use the expression new T2() to create a new instance of the object.
I am new to Interfaces.
I have a lot of objects that I pass as DTOs through my layers to the UI. Some of them are quite complex (Quite a few properties), but I only want to use them, in certain circumstances, in DropDown lists. These DTOs all have an int Id, and a string Description.
I would like to create a static function that takes a List<> of one of these objects, and returns a List<SelectListItem>
So, I am trying to use Interfaces for the first time.
I created an Interface:
public interface IListableItem
{
int Id { get; set; }
string Description { get; set; }
}
And then, I assigned that interface to one of my DTO objects I am trying to convert:
public class CategoryDto : BaseDto , IListableItem
{
public int PortfolioId { get; set; }
public string Description { get; set; }
public List<ExtendedSubCategoryDto> SubCategories { get; set; }
public bool IsExpenseCategory { get; set; }
public CategoryDto()
{
SubCategories = new List<ExtendedSubCategoryDto>();
}
}
Then, I created my generic method that takes a list of the category dtos, and will hopefully return a list
public static List<SelectListItem> TranslateToSelectList(List<IListableItem> source)
{
var reply = source.Select(item => new SelectListItem
{
Value = item.Id.ToString(CultureInfo.InvariantCulture), Text = item.Description
}).ToList();
return reply;
}
But, when I attempt to use this method, passing it a List, it fails.
model.Categories =
Translator.TranslateToSelectList(MyService.GetCategoriesByPortfolioId());
GetCategoriesByPortfolioId returns a List.
It's failing with the error:
CategoryDto is not assignable to IListableItem
It's probably a basic Interface understanding issue on my part, but what am I doing wrong, and how can I fix it?
If your method expects List<IListableItem>, you can't pass List<CategoryDTO>.
If that would be possible, you could Add different instances of elements that implement the interface IListableItem into a collection that is holding CategoryDTO elements, and read them.
Ultimately, that wouldn't make sense.
You can fix it, if you use IEnumerable interface. That allows covariance(going from higher type to lower type in generic type parameter).
The reason it works, is that IEnumerable is a "read-only view" of collection, thus you can't really add anything to it - plus what's important, the type parameter is marked as covariant.
public static List<SelectListItem> TranslateToSelectList(
IEnumerable<IListableItem> source)
{..}
Hi I need to find a way to declare an anonymous type for a method.This is my code:
public List<var> ListOfProducts(string subcategory)
{
var products = (from p in dataContext.Products
join s in dataContext.SubCategories.Where(x => x.SubCatName == subcategory)
on p.SubcatId equals s.SubCatId
join b in dataContext.Brands on p.BrandId equals b.BrandId
select new
{
Subcategory = s.SubCatName,
Brand = b.BrandName,
p.ProductName,
p.ProductPrice
});
return products;
}
I don't know what type should I set the List for the method.What should I do in this case?
You can't return an Anonymous Type from a method.
Just create a class for your type and return that.
public class Product
{
string Subcategory { get; set; }
string Brand { get; set; }
string ProductName { get; set; }
decimal ProductPrice { get; set; }
}
Then return as such:
var products = (from p in dataContext.Products
join s in dataContext.SubCategories.Where(x => x.SubCatName == subcategory) on p.SubcatId
equals s.SubCatId
join b in dataContext.Brands on p.BrandId equals b.BrandId
select new Product
{
Subcategory = s.SubCatName,
Brand = b.BrandName,
p.ProductName,
p.ProductPrice
});
return products;
EDIT: To clarify my first statement, as #JamesMichaelHare points out, technically it is possible to return an anonymous type from a method by returning object or dynamic, but it's probably more hassle than it's worth since you'd have to use Reflection or some other way of accessing the properties of your object.
As per MSDN, The dynamic type enables the operations in which it occurs to bypass compile-time type checking. Instead, these operations are resolved at run time.
So Try this instead :
public IEnumerable<dynamic> ListOfProducts(string subcategory)
What I would say, you should define another model for this, if you use returned result for Presentation layer, you should define ViewModel, or if you use for distribution layer, you can define as Dto object
public class ProductDto
{
public string Subcategory {get; set; }
public string Brand { get; set; }
public string ProductName{ get; set; }
public decimal ProductPrice{ get; set; }
}
Just a note: anonymous types are designed to be used inside the scope of a method(or a function) not outside of it.
But there is a way to do that by some extension methods and some additional castings (I do not like this):
(In your code you should add .ToList() to your LINQ expression too.)
Extension methods are:
static List<T> InitList<T>(this T o) { return new List<T>(); }
static T CastToThis<T>(this T target, object o) { return (T)o; }
You can initialize a list of anonymous type by:
var list = new { Name = "Name", Age = 40 }.InitList();
Now cast the returned object of your method to type of this list by using:
list = list.CastToThis(returnedValueOfYourMethod);
And another thing: anonymous types are valid just inside an assembly and can not be passed across assembly boundaries. From here:
The C# specification guarantees you that when you use "the same" anonymous type in two places in one assembly the types unify into one type. In order to be "the same", the two anonymous types have to have the exact same member names and the exact same member types, in the exact same order.
All in all I do not understand why you need to do that because declaring a new type is far more practical and if you really need this you should look into dynamic type in C# and if you are going to do some more magical things you should employ reflection.
fromI have this linq query to build a json (of string and bool?) from an IQueryable :
var ret = from c in results select new { country = c.EN, schengen = c.Schengen };
I would like to append new items to it manually : (pseudo code)
ret = ret /* add this -> */ { country = "some new country", schengen = true }
I've tried to do that :
//End up with cannot convert from AnonymousType#2 to AnonymousType#1
var listRet = ret.ToList();
listRet.Add(new { country ="", schengen = true });
As it is an anonymous type that I build I cannot find a way to add it. I always end up with conversion error
Anonymous types are still statically typed and C# compiler generates a separate class for each anonymous type definition occurrence. You need a named class in this case. It's a good practice for data transfer objects anyway. I use JSON DTOs like this, using data annotations and DataContractJsonSerializer.
[DataContract]
public class CountryInfo
{
[DataMember(Name = "country")]
public string Country { get; set; }
[DataMember(Name = "schengen", EmitDefaultValue = false)
public bool? Schengen { get; set; }
}
This way I have a well-documented JSON "protocol", which also uses C# naming conventions before serialization, but JS naming conventions after.
Well it's obvious. Generics are invariant. So another type won't fit. You can use List<object> or a named class to store the values.
If you want to change the result manually, you need to cast it into a static type instead of a dynamic one. Create a class that has a string country and bool schengen(You can even create the class within your current class to hide it from the rest of the application, if that's applicable), cast your linq results into that and then just add to it.