I have an list of objects which each have a list of attributes.
In the grid displaying the list of objects, I to show as the attributes as columns instead of showing them in a subgrid.
public class MyObject {
public int Id {get; set;}
public string Name {get; set;}
public IEnumerable<MyAttribute> Attributes {get; set;}
}
public class MyAttribute {
public int Id {get; set;}
public string Key {get; set;}
public string Value {get; set;}
}
So what I have is a IEnumerable<MyObject>. e.g.
var lst = new[]
{
new MyObject
{
Id = 1,
Name = "o1",
Attributes = new[]
{
new MyAttribute
{
Id = 1,
Key = "k",
Value = "v1"
},
new MyAttribute
{
Id = 2,
Key = "x",
Value = "v2"
},
}
},
new MyObject
{
Id = 2,
Name = "o2",
Attributes = new[]
{
new MyAttribute
{
Id = 3,
Key = "k",
Value = "v11"
},
new MyAttribute
{
Id = 4,
Key = "x",
Value = "v22"
},
}
}
};
And the grid I would like to end up with would then have 4 columns, Id, Name, k, x.
I have 2 problems...
The component I use to display the grid, uses ModelMetadataProviders.Current.GetMetadataForType((Func<object>) null, typeof (TModel)) to figure out which columns to show. How do I convince the ModelMetadataProviders to include these extra columns?
Similar, the code to get the values ModelMetadataProviders.Current.GetMetadataForProperties(this.Value, typeof (TModel)), how do I convince it to send those extra values?
If I can find a solution for the 2nd problem, I can probably find a workaround for the first one, since that is just a helper class.
Related
I have this query :
var query = (from tables ...
where ...
select new
{
ClientName = ClientName,
ClientNumber = ClientNumber,
ClientProduct = ClientProduct
}).Distinct();
which returns rows with 3 values.
ClientName and ClientNumber can be linked to multiple products.
So we can have :
NameA NumberA Product1
NameA NumberA Product2
NameA NumberA Product3
NameB NumberB Product4
NameC NumberC Product5
I would like to know if it is possible to store that in a List of a certain class which would be like :
class MyClass
{
string ClientName,
int ClientNumber,
List<int> ClientProducts
}
So there are no duplicate of ClientName and ClientNumber.
Thank you in advance.
With this class structure to represent your data:
class MyClass
{
public string ClientName { get; set; }
public int ClientNumber { get; set; }
public List<int> ClientProducts { get; set; }
}
class Procuct
{
public string ClientName { get; set; }
public int ClientNumber { get; set; }
public int ProductID { get; set; }
}
and this test data:
List<Procuct> Products = new List<Procuct>()
{
new Procuct() { ClientName = "A", ClientNumber = 1, ProductID = 1},
new Procuct() { ClientName = "A", ClientNumber = 1, ProductID = 2},
new Procuct() { ClientName = "A", ClientNumber = 1, ProductID = 3},
new Procuct() { ClientName = "B", ClientNumber = 2, ProductID = 4},
new Procuct() { ClientName = "C", ClientNumber = 2, ProductID = 5}
};
you can use the following linq query:
var q = from p in Products
group p by new
{
cName = p.ClientName,
cNumber = p.ClientNumber
} into pGroup
select new MyClass
{
ClientName = pGroup.Key.cName,
ClientNumber = pGroup.Key.cNumber,
ClientProducts = pGroup.Select(x => x.ProductID).ToList()
};
to get exactly what you want, i.e. a collection of MyClass objects.
The Grouping performed in the above linq query essentially guarantees that there will be no duplicates on (ClientName, ClientNumber).
Since you mention Linq-to-sql, most probably you Client entity already has the products linked. You might look for an overcomplicated solution.
It depends a bit on your foreign key stucture, but if your datamodel would be
Client has 1-many product and you have a Foreign key from product to client it is already present.
So you can just reference client.Products.
So in your case it would be
var query = (from Clients...
where ...
select new
{
ClientName = Client.ClientName,
ClientNumber = Client.ClientNumber,
ClientProduct = Client.Products.Select(s=>s.id).ToList()
});
But you might as well simply use your client entity with a eager load of the products.
It all depends on your datamodel + proper foreign key structure
if you have a many-many associations like Product-per-client between your client and product you can start from that entity. Have a look at this documentation - it provides a good starting point for Linq-2-sql.
http://weblogs.asp.net/scottgu/using-linq-to-sql-part-1
I solve same problem , I think it useful to you
Only check your Where Condition properly
Thank...
var query = (from tables ...
where ...
select new
{
ClientName = ClientName,
ClientNumber = ClientNumber,
ClientProduct = ClientProduct.ToList()
}).Distinct();
I am having a class Item.
class Item{
public int Id { get; set; }
public DateTime CreatedDate { get; set; }
public string Name { get; set; }
public string Description { get; set;}
}
I want to filter list of items based on dynamic column name.
Suppose I want list of Names then Column Name is "Name" and result will be list of names
If column name is Description, I need list of descriptions.
How to do this with LinQ?
Easy, just select the property you need from the list:
var items = new List<Item>();
//get names
var names = items.Select(x => x.Name);
//get descriptions
var descriptions = items.Select(x => x.Description);
Update:
You'll need a bit of reflection to do this:
var names = items.Select(x => x.GetType().GetProperty("Name").GetValue(x));
Throw this in a method for re-usability:
public IEnumerable<object> GetColumn(List<Item> items, string columnName)
{
var values = items.Select(x => x.GetType().GetProperty(columnName).GetValue(x));
return values;
}
Of course this doesn't validate wether the column exists in the object. So it will throw a NullReferenceException when it doesn't. It returns an IEnumerable<object>, so you'll have to call ToString() on each object afterwards to get the value or call the ToString() in the query right after GetValue(x):
public IEnumerable<string> GetColumn(List<Item> items, string columnName)
{
var values = items.Select(x => x.GetType().GetProperty(columnName).GetValue(x).ToString());
return values;
}
Usage:
var items = new List<Item>(); //fill it up
var result = GetColumn(items, "Name");
I'm going through a short Web Api + OData tutorial from asp.net: http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/getting-started-with-odata-in-web-api/create-a-read-only-odata-endpoint.
I downloaded the example project, and it works. But then I started playing around with the Product model that they use in the example. I added a new property to act as a key of type string instead of an integer key.
The new Product.cs:
public class Product
{
public string stringKey { get; set; }
public int ID { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
The modified controller:
public class ProductsController : EntitySetController<Product, string>
{
static List<Product> products = new List<Product>()
{
new Product() { stringKey = "one", ID = 1, Name = "Hat", Price = 15, Category = "Apparel" },
new Product() { stringKey = "two", ID = 2, Name = "Socks", Price = 5, Category = "Apparel" },
new Product() { stringKey = "three", ID = 3, Name = "Scarf", Price = 12, Category = "Apparel" },
new Product() { stringKey = "four", ID = 4, Name = "Yo-yo", Price = 4.95M, Category = "Toys" },
new Product() { stringKey = "five", ID = 5, Name = "Puzzle", Price = 8, Category = "Toys" },
};
[Queryable]
public override IQueryable<Product> Get()
{
return products.AsQueryable();
}
protected override Product GetEntityByKey(string key)
{
return products.FirstOrDefault(p => p.stringKey == key);
}
}
The trouble is that when I go to /odata/Products(one) the string "one" is not bound to the key argument in the GetEntityByKey(string key) action. However, when I browse to odata/Products(1) then "1" does get bound to the key argument.
How can I get a string with text values to bind correctly, instead of just binding strings with numerical values?
Update
I forgot to include the WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Product>("Products");
Microsoft.Data.Edm.IEdmModel model = modelBuilder.GetEdmModel();
config.Routes.MapODataRoute("ODataRoute", "odata", model);
}
}
I noticed that the path /odata/Products(0011-1100) would only bind "0011" as the string key.
After some playing around with it, I've found that the following path works as I had hoped:
/odata/Products('one')
It appears the single quotes are required to read the entire string within the parentheses.
I create this class for a test.
I want to compare to List of the class and get different class between ListA and ListB. In my example the result get only class of ListB.
I do the same thin with list of string and work it
Class Example
public class FileNode
{
public string Source { get; set; }
public int Id { get; set; }
}
List<FileNode> ListA = new List<FileNode>
{
new FileNode{ Id = 1, Source="a" },
new FileNode{ Id = 2, Source="b" },
};
List<FileNode> ListB = new List<FileNode>
{
new FileNode{ Id = 1, Source="a" },
new FileNode{ Id = 2, Source="b" },
new FileNode{ Id = 3, Source="c" },
};
List<FileNode> ListAB = ListB.Where(m => !ListA.Contains(m)).ToList();
String example, it's works
List<string> a = new List<string> {"a","b","c","d","e" };
List<string> b = new List<string> {"a","b","c","d" };
List<string> ab = a.Where(m => !b.Contains(m)).ToList();
Well Contains is going to call Equals on the elements - and may also use GetHashCode (I doubt it, but you should override it consistently anyway). So you need to override Equals(object) and GetHashCode() in FileNode. (By default, you'll get reference equality.)
Note that as soon as you start trying to use Contains in a query which will execute in the database, it could behave completely different - it wouldn't be looking at your Equals/GetHashCode methods at that point.
class Category
{
public string Name { get; set; }
public int Count { get; set;}
}
Name Count
AA 2
BB 3
AA 4
I have an IEnumerable<Category>
and would like to get a list of Categories with unique names and the sum of multiple entries
Output
Name Count
AA 6
BB 3
Update
class Category
{
public string Name { get; set; }
public int CountA { get; set;}
public int CountB { get; set;}
public string Phone { get; set;}
}
How would I sum two columns. and the phone column can be the last row or any row
Your updated question isn't entirely clear in terms of the phone number, but I suspect you want something like:
var query = from category in list
group category by category.Name into grouped
select new { Name = grouped.Key,
SumA = grouped.Sum(x => x.CountA),
SumB = grouped.Sum(x => x.CountB),
Phone = grouped.Last().Phone };
Changing grouped.Last() to grouped.First() would be more efficient, by the way.
Evaluating multiple aggregates in this way isn't terribly efficient in general. The Push LINQ project developed by myself and Marc Gravell makes it a lot more efficient at the cost of not being quite as easy to use. You might want to look into it if you need to deal with a lot of data.
var foo = new List<Category>() {
new Category() { Name = "AA", Count = 2},
new Category() { Name = "BB", Count = 3},
new Category() { Name = "AA", Count = 4}
};
var bar = foo.GroupBy(c => c.Name).Select(g => new Category(){ Name = g.Key, Count = g.Sum(c => c.Count) });