I have a IQueryable<SomePOCO> (a LINQ-Entities query, if that matters):
public class SomePOCO
{
public string ParentName { get; set; }
public string Name { get; set; }
public string Url { get; set; }
}
And i'm trying to project to a single object (anonymous type would be best, since i only need method scope) which has 2 properties:
public string ParentName { get; set; }
public ICollection<SimplePoco> { get; set;
SimplePOCO is as follows:
public class SimplePOCO
{
public string Name { get; set; }
public string Url { get; set; }
}
The reason i'm doing this is that all of the "SomePOCO"s im retrieving have the same ParentName, so i just want that once, as opposed to bringing over the wire the same value N amount of times and doing a .First().
Hope that makes sense.
The end result is i should be able to do this:
var parentName = result.ParentName; // string
var pocos = result.SimplePOCOs; // ICollection<SimplePOCO>
I think i either need some kind of aggregation, like with GroupBy or SelectMany.
Any ideas?
I think you just need to do a group by Parent Name
var result = collection.GroupBy(i => i.ParentName)
.Select(g => new {
ParentName = g.Key,
SimplePocos = g.Select(i => new SimplePoco
{
Name = i.Name,
Url = i.Url
})
});
This is the first step.
var groups = myQ.GroupBy(p => p.ParentName);
You will need to have your middle data structure. I'll call it Grouping.
var myList = new List<Grouping>();
foreach (var group in groups) {
var newGrouping = new Grouping();
new Grouping.ParentName = group.Key;
newGrouping.SimplePocos = group.Select(p => new SimplePoco(p)).ToArray();
}
You should have a constructor of SimplePoco that will convert it for you.
Related
I am trying to group a column and form the the rest of the columns as child, hierarchical data:
I am trying to group by Code and form the parent and child relationship from a flat list, below is the hierarchical data I am trying to form:
source list:
public class ItemAssignmentFlatList
{
public int Code { get; set; }
public string Type { get; set; }
public string Description { get; set; }
public int ItemCode{ get; set; }
public DateTime EffectiveDate{ get; set; }
public string Area{ get; set; }
public string TaxCode{ get; set; }
public string LocationId { get; set; }
}
Need to convert above flat list into below List of hierarchical data:
public class ItemInfo
{
public int Code { get; set; }
public string Type { get; set; }
public string Description { get; set; }
public List<TaxInfo> TaxPlan { get; set; }
}
public class TaxPlan
{
public int ItemCode{ get; set; }
public DateTime EffectiveDate{ get; set; }
public string Area{ get; set; }
public string TaxCode{ get; set; }
public string LocationId { get; set; }
}
Need hierarchical list with above flat data list with C# extension methods.
I have below code, but looking for clean code to reduce number of lines:
var items= results.GroupBy(x => new { x.Code, x.Type });
List<ItemInfo> result = new List<ItemInfo>();
foreach (var group in items)
{
var taxPlans = group.
Select(y => new TaxPlan
{
TaxArea = y.TaxArea,
ItemCode = y.ItemCode
});
var itemInfo= new ItemInfo
{
Code = group.FirstOrDefault().Code,
Type = group.FirstOrDefault().Type,
Description = group.FirstOrDefault().Description,
TaxPlan = taxPlans.ToList()
};
result.Add(itemInfo);
}
Something like this?:
var input = new List<ItemAssignmentFlatList>(){
new ItemAssignmentFlatList{
Code = 1,
Area = "a"
},
new ItemAssignmentFlatList{
Code = 1,
Area = "b"
},
new ItemAssignmentFlatList{
Code = 2,
Area = "c"
}
};
input
.GroupBy(
x => x.Code,
(int code, IEnumerable<ItemAssignmentFlatList> items) =>
{
var first = items.FirstOrDefault();
var key = new ItemInfo
{
Code = first.Code
//, ...
};
var plan = items.
Select(y => new TaxPlan
{
Area = y.Area
//, ...
});
return new
{
key = key,
items = plan
};
}
).Dump();
Whenever you have a sequence of similar object, and you want to make "Items with their SubItems", based on common properties in your source sequence, consider to use one of the overloads of Enumerable.GroupBy
Because you don't just want "Groups of source items" but you want to specify your output, consider to use the overload that has a parameter resultSelector.
parameter keySelector: what should all elements in a group have in common
parameter resultSelector: use the common thing, and all elements that have this common thing to make one output element.
.
IEnumerable<ItemAssignmentFlatList> flatItemAssignments = ...
IEnumerable<ItemInfo> items = flatItemAssignments
// make groups with same {Code, Type, Description}
.GroupBy(flatItemAssignment => new {Code, Type, Description},
// parameter resultSelector: take the common CodeTypeDescription,
// and all flatItemAssignments that have this common value
// to make one new ItemInfo
(codeTypeDescription, flatItemAssignmentsWithThisCodeTypeDescription) => new ItemInfo
{
Code = codeTypeDescription.Code,
Type = codeTypeDescription.Type,
Description = codeTypeDescription.Description,
TaxPlans = flatItemAssignmentsWithThisCodeTypeDescription
.Select(flatItemAssignment => new TaxPlan
{
ItemCode = flatItemAssignment.ItemCode,
EffectiveDate = flatItemAssignment.EffectiveDate,
Area = flatItemAssignment.Area,
...
})
.ToList(),
});
So I've got a nested data structure like this:
public class ContractTerm
{
public int ContractId { get; set; }
public string SectionId { get; set; }
public string SubsectionId { get; set; }
public string TermId { get; set; }
public int TermOrder { get; set; }
public TermItem TermNavigation { get; set; }
}
public class TermItem
{
public string SectionId { get; set; }
public string SubsectionId { get; set; }
public string TermId { get; set; }
public string Text { get; set; }
public ICollection<ContractTerm> ContractNavigation { get; set; }
}
I've also got a class to map the section/subsection pairings in a more EF-friendly way (IRL this is an enum with attribute values and a helper, but this class abstracts away some work not necessary to reproduce the issue):
public class Section
{
public string Name { get; set; }
public string SectionId { get; set; }
public string SubsectionId { get; set; }
}
Both ContractTerm and TermItem have their own collections in a DbContext, and I'm trying to get a collection of all text entries assigned to specific Sections for a given ContractId. I have the following class to contain it:
public class TextsBySection
{
public string SectionName { get; set; }
public IEnumerable<string> Texts { get; set; }
}
I want to select a collection of TextsBySection, and have something like this:
public class ContractManager
{
//insert constructor initializing MyContext here
private MyContext Context { get; }
public IEnumerable<MyOutputClass> GetTerms(int contractId, IEnumerable<Section> sections)
{
Func<string, string, IEnumerable<string>> getBySection =
(section, subsection) => context.ContractTerms.Include(x => x.TermNavigation)
.Where(x => x.ContractId == contractId
&& x.SectionId == section
&& x.SubsectionId == subsection)
.Select(x => x.TermNavigation.Text);
var result = sections.Select(x => new MyOutputClass
{
SectionName = x.Name,
Texts = getBySection(x.SectionId, x.SubsectionId)
}).ToList();
return result;
}
}
This works fine and dandy, but it hits the database for every Section. I feel like there's got to be a way to use Join and/or GroupBy to make it only query once, but I can't quite see it. Something like this, perhaps:
var result = context.ContractTerms.Include(x => x.TermNavigation)
.Where(x => x.ContractId == contractId)
.Join(sections,
term => //something
section => //something
(term, section) => /*something*/)
If all this were in SQL, selecting the necessary data would be easy:
SELECT sections.name,
term_items.text
FROM contract_terms
JOIN term_items
ON term_items.section_id = contract_terms.section_id
AND term_items.subsection_id = contract_terms.subsection_id
AND term_items.term_id = contract_terms.term_id
JOIN sections --not a real table; just corresponds to sections argument in method
ON sections.section_id = contract_terms.section_id
AND sections.subsection_id = contract_terms.subsection_id
...and then I could group the results in .NET. But I don't understand how to make a single LINQ query that would do the same thing.
I changed my answer, well I would do something like this... maybe this may help you.
public static void Main(string[] args)
{
List<Section> sections = new List<Section>();
List<ContractTerm> contractTerms = new List<ContractTerm>();
List<TermItem> termItens = new List<TermItem>();
//considering lists have records
List<TextsBySection> result = (from contractTerm in contractTerms
join termItem in termItens
on new
{
contractTerm.SectionId,
contractTerm.SubsectionId,
contractTerm.TermId
}
equals new
{
termItem.SectionId,
termItem.SubsectionId,
termItem.TermId
}
join section in sections
on new
{
contractTerm.SectionId,
contractTerm.SubsectionId
} equals new
{
section.SectionId,
section.SubsectionId
}
select
new
{
sectionName = section.Name,
termItemText = termItem.Text
}).GroupBy(x => x.sectionName).Select(x => new TextsBySection()
{
SectionName = x.Key,
Texts = x.Select(i=> i.termItemText)
}).ToList();
}
Please have patience with me I'm new to linq and have a question about a couple of errors that I am not understanding any help would be greatly appreciated.
the error I am receiving in the title of my question.
my childproduct variable returns every single character indiviuallly I would like them to be a string version of the productId and Childtext parameters.
Code:
public class AOAPlusChildModel
{
public List<string> LongName { get; set; }
public List<string> Text { get; set; }
public List<string> ProductId { get; set; }
public static List<AOAPlusChildModel> GetChildProducts()
{
List<AOAPlusChildModel> cp = new List<AOAPlusChildModel>();
List<AoaUserDefinedVWGetAOAPlusProducts> ChildProductsLists = AoaSvcClient.Client.Context.AoaUserDefinedVWGetAOAPlusProductss.Where(a => a.MasterProductFlag == false && a.Affiliate == "VA").ToList();
var childProducts = ChildProductsLists.SelectMany(p => p.LongName, (id, childtext) =>
new { ProductId = id.ProductId, Text = childtext }).ToList();
cp = childProducts.ToList();
return cp;
}
}
Your variable cp is a List<AOAPlusChildModel> but the linq query is projecting an anonymous type. Instead of creating a new anonymous type create a new AOAPlusChildModel
return ChildProductsLists.SelectMany(p => p.LongName,
(id, childtext) =>
new AOAPlusChildModel {
ProductId = id.ProductId,
Text = childtext }).ToList();
Reason for following errors are that you perform ChildProductsLists.SelectMany(p => p.LongName) which basically now returns a collection of strings - this collection of strings you are trying to assign as a new AOAPlusChildModel object which does not hold string properties but List<string> properties.
I think your model should look like:
public string LongName { get; set; }
public string Text { get; set; }
public string ProductId { get; set; }
Basic idea is similar to Merging Expression Trees to Reuse in Linq Queries.
In my situation, I have two models and DTOs:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public Extra Extra { get; set; }
}
public class Extra
{
public int Id { get; set; }
public string Text { get; set; }
}
public class UserDto
{
public int Id { get; set; }
public string Name { get; set; }
public ExtraDto Extra { get; set; }
}
public class ExtraDto
{
public int Id { get; set; }
public string Text { get; set; }
}
and expressions:
Expression<Func<Extra, ExtraDto>> extraSelector = o => new ExtraDto
{
Id = o.Id,
Text = o.Text
};
Expression<Func<User, UserDto>> userSelector = o => new UserDto
{
Id = o.Id,
Name = o.Name
};
Now, I'd like to 'append' extraSelector into userSelector. The pseudo code is like:
var selectorExpression = userSelector.Append(user => user.Extra, extraSelector);
Context.Users.Select(selectorExpression).ToList();
The final expression would be like this:
Expression<Func<User, UserDto>> userSelector = o => new UserDto
{
Id = o.Id,
Name = o.Name,
Extra = new ExtraDto
{
Id = o.Extra.Id,
Text = o.Extra.Text
}
};
I've tried using ExpressionVisitor, but no luck.
Apart from the "merge" of the two selectors, you have to insert the "path" o => o.Extra into the extraSelector and create a new "bind expression" for the property Extra of UserDto.
In fact, i'm playing around with such scenarios within this project, where i've tried to abstract this kind of expression plumbing. Your "merge" would then look like that:
userSelector = extraSelector.Translate()
.Cross<User>(o => o.Extra)
.Apply(o => o.Extra, userSelector);
The Translate extension method is just a little helper to make use of type inference, Cross inserts o => o.Extra into the extraSelector, Apply creates the "bind expression" for the property Extra of UserDto and finally merges the result with userSelector.
I have a class that I made and I want to use it for a list but when I try adding data to it and loop through the list all I'm getting is the address of the class. I have no idea why its added that as the values.
My Class is
public class Regions
{
public int dataID { get; set; }
public int documentID { get; set; }
public string region_ID { get; set; }
public string region_Name { get; set; }
public string sortID { get; set; }
}
This is how I am trying to add the values. The query has the right data in it, but the list isn't getting the data, just where the class resides.
List<Regions> lst = new List<Regions>();
var q = dt.AsEnumerable()
.Select(region => new {
dataID = region.Field<int>("dataID"),
documentID = region.Field<int>("documentID"),
region_ID = region.Field<string>("Region_ID"),
region_Name = region.Field<string>("Region_Name"),
sortID = region.Field<string>("SortID").ToString()
});
foreach (var d in q)
lst.Add(new Regions() { dataID = d.dataID,
documentID = d.documentID,
region_ID = d.region_ID,
region_Name = d.region_Name,
sortID = d.sortID
});
EDIT
Here is a link that I found that is similar what I am trying to do, but it doesn't seem he had the same errors as I did. I tried that answer, but wasn't working for me.
Storing data into list with class
Fixed
When I was looping through the list, I wasn't reading it properly.
Try adding this in Regions
public override string ToString()
{
return region_Name;
}
actually you can create the list in one step
List<Regions> lst = dt.Rows.OfType<DataRow>().Select(region => new Regions{
dataID = region.Field<int>("dataID"),
documentID = region.Field<int>("documentID"),
region_ID = region.Field<string>("Region_ID"),
region_Name = region.Field<string>("Region_Name"),
sortID = region.Field<string>("SortID")
}).ToList();