Cannot access members of a class in the select linq method - c#

static void Main(){
List<Foo> t = new List<Foo>{
new Foo(){Id=1,Name="A",Value=1},
new Foo(){Id=2,Name="B",Value=1},
new Foo(){Id=3,Name="C",Value=1},
new Foo(){Id=3,Name="D",Value=1}};
var x = t.GroupBy(gp => gp.Id).Select(sel => new Foo { Id = ,Name=,Value= });
}
public class Foo{
public string Name { get; set; }
public int Id { get; set; }
public int Value { get; set; }
}
In the var x I want to group all the Foo objects by their ID and get the SUM in the Value field.
The problem is that it seems I cannot access the members/fields of the class in the select method.
Thanks

After GroupBy you don't select an IEnumerable<Foo> but groups of them. You probably want:
var x = t.GroupBy(f => f.Id)
.Select(grp => new Foo {
Id = grp.Key,
Name = String.Join(",", grp.Select(f => f.Name)),
Value = grp.Sum(f => f.Value)
});
I'm using String.Join to concenate all names of each ID-group, the values are summed.

Try this way
var x = t.GroupBy(gp => gp.Name).OrderBy(group => group.Key).Select(group => Tuple.Create(group.Key, group.Count()));

Related

How to have a changing name as Key and it's value as Count in select

So I'm not sure if this is possible, but I'm doing a final select query, and I've noticed that it would be easier for my data if I could do something like:
var finalQuery = selectQuery
.GroupBy(x => x.ProductName)
.Select(c => new
{
c.Key = c.Count()
}).ToList();
Therefore, I want the returned data to be something like:
[{
"Clothes" : 5,
"Shoes" : 7,
"Laptop" : 10
}]
My current query is:
var finalQuery = selectQuery
.GroupBy(x => x.ProductName)
.Select(c => new
{
ProductName = c.Key
ProductCount = c.Count()
}).ToList();
I thought something like c.Key.ToString() would do the trick, but it doesn't work. I'm guessing the "Key" must be a set value to work, and it can't necessarily be dynamically changed?
You can try this way
var finalQuery = selectQuery.GroupBy(p => p.ProductName).ToDictionary(g => g.Key, g => g.Count());
You should customize the string using your own code, or you can use an external library as NewtonSoft.Json.
Below is a sample code that does the trick for you.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public class Test
{
public static void Main()
{
var products = new List<Product>()
{
new Product("a", "Clothes"),
new Product("b", "Shoes"),
new Product("c", "Clothes"),
new Product("d", "Clothes"),
new Product("e", "Shoes"),
new Product("f", "Shoes"),
new Product("g", "Laptop"),
new Product("h", "Laptop"),
new Product("h", "Shoes"),
};
var result = products
.GroupBy(p => p.Type)
.Select(
group => new
ProductCount(group.Key, group.Count())
// You can use also an object as the commented code below
//{
// Type = group.Key,
// Count = group.Count()
//}
)
.ToList();
Console.WriteLine(ProductCount.ToFormatedString(result));
}
class Product
{
public string Name { get; set; }
public string Type { get; set; }
public Product(string name, string type)
{
this.Name = name;
this.Type = type;
}
}
class ProductCount
{
public string Type { get; set; }
public int Count { get; set; }
public ProductCount(string type, int count)
{
this.Type = type;
this.Count = count;
}
public override string ToString()
{
return $"\"{this.Type}\" : {this.Count}";
}
public static string ToFormatedString(IEnumerable<ProductCount> products) // if you need a more generic method, u can make this an extension method and use Reflection
// Or u can use a nuget package that formats objects to json (e.g: Newtonsoft is a good library)
{
var sb = new StringBuilder();
sb.AppendLine("[{");
foreach (var item in products)
sb.AppendLine(item.ToString());
sb.AppendLine("}]");
return sb.ToString();
}
}
}

how to fix error:"The specified type member is not supported in LINQ to Entities ..." on not mapped columns

Table 1:
class TRD
{
[Column("ID")]
[Key]
[Required]
public int ID { set; get; }
[Column("Name", TypeName = "nvarchar")]
public string Name { set; get; } // <-- Mapped
[Column("LastName", TypeName = "nvarchar")]
public string LastName { set; get; } // <-- Mapped
public string DisplayFullName // <-- Not Mapped
{
get
{
return string.Format("{0} - {1}", Name, LastName);
}
}
[Column("gate_id", TypeName = "int")]
public int gate_id { get; set; } // <-- Mapped - foreign key
}
Table 2:
class Gate
{
[Column("ID")]
[Key]
[Required]
public int ID { set; get; }
public string name { get; set; }
}
I want to have TRD left join Gate:
var search = OdataBaseContex.TRD.GroupJoin(
OdataBaseContex.Gates,
f => f.gate_id,
p => p.ID,
(x, y) => new { TRD = x, gate = y })
.SelectMany(
x => x.gate.DefaultIfEmpty(),
(x, y) => new {
LastName = x.TRD.LastName,
Name = x.TRD.Name,
DisplayFullName = x.TRD.DisplayFullName, //<-- Error
gate_id = y.name
}).ToList();
Error:
The specified type member 'DisplayFullName' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
Try this:
var search = OdataBaseContex.TRD.GroupJoin(
OdataBaseContex.Gates,
f => f.gate_id,
p => p.ID,
(x, y) => new { TRD = x, gate = y })
.SelectMany(
x => x.gate.DefaultIfEmpty(),
(x, y) => new {
LastName = x.TRD.LastName,
Name = x.TRD.Name,
DisplayFullName = x.TRD.Name+"-"+x.TRD.LastName,
gate_id = y.name
}).ToList();
or make that property don't use string.Format like x.TRD.Name+"-"+x.TRD.LastName :
public string DisplayFullName
{
get
{
return Name + " - " + LastName;
}
}
UPDATE:
as my comment below, when you want use a method in Linq-To-Entities query it makes the transition of query to be executable query for database, so when you call your method in C# going to get translated into some SQL query, so if all C# actually get executed as SQL, there is no problem otherwise you can't call method() in main block of query, BTW the workaround and easy solution is to split that query to separate parts like below:
var search = OdataBaseContex.TRD.GroupJoin(
OdataBaseContex.Gates,
f => f.gate_id,
p => p.ID,
(x, y) => new { TRD = x, gate = y })
.SelectMany(
x => x.gate.DefaultIfEmpty(),
(x, y) => new
{
LastName = x.TRD.LastName,
Name = x.TRD.Name,
gate_id = y.name
}).ToList().Select(mn => new
{
LastName = mn.LastName,
Name = mn.Name,
DisplayFullName = GetFullName(mn.Name,mn.LastName)
gate_id = mn.gate_id
});
by calling ToList() you query will execute on DB and then by the last Select() you will get something you want.
Or suppose you have a DTO class like below :
public class DtoClass
{
public string FullName { get; set; }
public int GateId { get; set; }
public string ShamsiDate { get; set; }
}
then you can run your query in two steps:
var search = OdataBaseContex.TRD.GroupJoin(
OdataBaseContex.Gates,
f => f.gate_id,
p => p.ID,
(x, y) => new { TRD = x, gate = y })
.SelectMany(
x => x.gate.DefaultIfEmpty(),
(x, y) => new
{
LastName = x.TRD.LastName,
Name = x.TRD.Name,
gate_id = y.name
}).ToList();//query will be executed on DB because of ToList()
List<DtoClass> results = search.Select(mn =>
{
//Manipulating mn data
//call some custom method to cast Date
string newDate = CastMiladi2Other(date);
DtoClass ret = new DtoClass()
{
FullName = GetFullName(mn.Name, mn.LastName),
ShamsiDate = newDate,
GateId = mn.gate_id
};
return ret;
}).ToList();
in Select() of results you can do and call any methods you want, you can also use AsEnumerable() instead of ToList() , the rest of the query will then be evaluated as an in memory collection using Linq-to-Objects.
Your query wont work as this query is getting converted to SQL query and that time it is not understanding what you have used in setter in .Net class. So, try using expressions instead else the other work around are already listed above i guess which will work for you.

LINQ GroupBy return dictionary with given objects as value

I have an IEnumerable<ValueObj> object with multiple valid ValueObj objects in it. I would like to group those objects by Id and receive Dictionary<Guid, IEnumerable<ValueObj>> where the Key is Id from ValueObj, and Value is just unchanged ValueObj.
public class ValueObj
{
public Guid Id { get; set; }
public string Name { get; set; }
public DateTime Time { get; set; }
public double Result { get; set; }
}
I've tried to mess with Linq GroupBy but with no success
IEnumerable<ValueObj> persons = ...;
var results = persons.GroupBy(
p => p.Id,
p => p,
(key, g) => new { PersonId = key, Cars = g.ToList() });
Try this:
IEnumerable<ValueObj> col = ...;
var dict = col.GroupBy(x => x.Id).ToDictionary(x => x.Key, x => x.ToList());

Add GroupBy to Select

I have this query
[HttpGet]
public List<AttachedPhotosModel> GetReportAttachedPhotos(int reportId)
{
var photos = new ReportsRepository().GetInjuryPhotos(reportId);
return photos.Select(x => new AttachedPhotosModel()
{
Id = x.Id,
Type = x.InjuryType,
Photos = photos.Where(y => y.InjuryType == x.InjuryType).Select(z => z.ServicePhotoUrl).ToList()
}).ToList();
}
I need to GroupBy InjuryType, how to do this?
I added return photos.GroupBy(k => k.InjuryType).Select(x => new AttachedPhotosModel() but how to select model, x have new value key and I don't know how to select my data
This code should work. Assuming photos is collection of objects with InjuryType property and PhotoUrl property and AttachedPhotosModel has an InjuryType and Photos properties like this.
public class AttachedPhotosModel
{
public string InjuryType { set; get; }
public List<string> Photos { set; get; }
}
Code for grouping by InjurType.
var grouped = photos
.GroupBy(s => s.InjuryType,d => d.PhotoUrl, (k, g) => new
AttachedPhotosModel
{
InjuryType = k,
Photos = g.ToList()
}).ToList();

Getting properties from a child

I have the following entities:
public class Parent
{
int Id { get; set; }
string ParentName { get; set; }
List<Child> Children { get; set; }
}
public class Child
{
int Id { get; set; }
string ChildName { get; set; }
}
and the following dto:
public class ParentDTO
{
int Id { get; set; }
List<string> ChildrenNames { get; set; }
}
using QueryOver code below I can get the Parent values
ParentDTO result = null;
Parent parentAlias = null;
Child childAlias = null;
var query = session.QueryOver(() => parentAlias)
.JoinAlias(() => parentAlias.Children, () => childAlias, JoinType.LeftOuterJoin)
.SelectList(list => list.Select(c => c.Id).WithAlias(() => result.Id)
.Select(c => c.ParentName).WithAlias(() => result.Name)
//this part does not work
.Select(c => c.Children .Select(v => v.ChildName)).WithAlias(() => result.ChildrenNames)
//
)
.TransformUsing(Transformers.AliasToBean<ParentDTO>());
return query.List<ParentDTO>();
However I cant seem to be able to project the list of childName values into my ChildrenNames collection.
Any ideas?
As some guys said in comments, you need to do two queries. Using linq, you could try something like this:
// get the parent Ids
var parentIds = session.Query<Parent>().Select(c => c.Id).ToList();
// get the childNames
var childNames = session.Query<Child>()
.Where(x => parentIds.Contains(x.ParentId)) // get on the child from parents query
.Select(x => new {x.Name, x.ParentId}) // get only the properties you need
.ToList(); // list of anon objects
// loop in memory between parentIds filling the corresponding childNames
var result = parentIds.Select(parentId => new ParentDTO()
{
Id = parentId,
ChildrenNames = childNames.Where(x => x.ParentId == parentId).ToList()
}).ToList();
I am not sure if it works, but you could try this in a single query:
var query = from p in session.Query<Parent>()
let names = p.Children.Select(c => c.ChildName).ToList()
select new ParentDTO()
{
Id = o.Id,
ChildrenNames = names
};
return query.Tolist();
Obs: I did not test it.

Categories

Resources