I have a flat structure of data which I have retrieved with a QueryExpression and LinkEntity (InnerJoin). I have aliased the child with "element"
So my data looks like this:
parentIdPK parentStatus element.ChildIdPK element.parentIdFK
1 100 10 1
1 100 11 1
1 100 12 1
2 100 13 2
2 100 14 2
3 100 15 3
3 100 16 3
3 100 17 3
So bascially I have a Parent/Child structure and I want to push this data into my own classes:
public class ExistingProposal
{
public Guid parentIdPK { get; set; }
public int parentStatus { get; set; }
public List<ExistingElement> Elements { get; } = new List<ExistingElement>();
}
public class ExistingElement
{
public Guid ChildIdPK { get; set; }
public Guid parentIdFK { get; set; }
}
So in general this would lead to have one ExistingProposal with N ExistingGRProposalElement's
Ho can I achieve this in the best way? I have tried with linq but I'm struggling pretty much with this.
What I am trying actually is to group the data with linq:
var groups = from a in result.Entities
orderby a.Attributes["parentId"]
group a by a.Attributes["parentId"] into g
select new { g };
The problem I have actually is I dont know exactly from where to start to create the needed class structure.
Maybe somebody can point me to the right direction?
Any hint is highly appreciated.
Your question isn't very clear, but, if I understood well the following expression will do the trick for you :
var groups = from a in result.Entities
group a by a.Attributes["parentId"] into g
select new ExistingProposal {
parentIdPK = a.Key,
parentStatus = (int)a.FirstOrDefault().Attributes["parentStatus"],
Elements = (from y in g
select new ExistingElement {
ChildIdPK = y.Attributes["element.ChildIdPK"],
parentIdFK = a.Key
}).ToList()
};
You'll need to add a setter to your Elements property in ExistingProposal
You don't need to order before grouping
You should rename intermediate vars (y, g, a, etc.) to more meaningful ones
Related
I am trying to query data from database using EF Core, but the scenery is a bit complicated for me. I will try to be clear and synthesize what I want to accomplish.
There are three tables involved:
Table WORK_TO_DO - Columns: ID, DESCRIPTION
Table PARAM_DEFINITIONS_FOR_WORK - Columns: ID, NAME
Table PARAM_VALUES_FOR_WORK - Columns: WORK_TO_DO_ID, PARAM_DEFINITION_ID, VALUE
Let's say these tables have their classes as below.
public class WorkToDo
{
public int Id { get; set; }
public string Description { get; set; }
}
public class ParamDefinition
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ParamValue
{
public int WorkToDoId { get; set; }
public int ParamDefinitionId { get; set; }
public string Value { get; set; }
}
I have a list of ParamValue items with ParamDefinitionId and Value populated, but without WorkToDoId.
I want to query all WorkToDo items that match the ParamValue items, considering all the ParamValue items and not just any of them.
Let me explain with example records on each table:
WORK_TO_DO
ID
DESCRIPTION
1
Work Example A
2
Work Example B
PARAM_DEFINITIONS_FOR_WORK
ID
NAME
101
Param Definition X
102
Param Definition Y
103
Param Definition W
104
Param Definition Z
105
Param Definition +
PARAM_VALUES_FOR_WORK
WORK_TO_DO_ID
PARAM_DEFINITION_ID
VALUE
1
101
Param Value J
1
102
Param Value K
2
103
Param Value L
2
104
Param Value M
2
105
Param Value N
So, let's say my list of ParamValues has two items: ParamDefinitionId = 101, Value = "Param Value J" and ParamDefinitionId = 102, Value = "Param Value K". I would like to retrieve the WorkToDo of Id = 1.
If my list of ParamValues had, instead, three items:
ParamDefinitionId = 103, Value = "Param Value L"
ParamDefinitionId = 104, Value = "Param Value M"
ParamDefinitionId = 105, Value = "Param Value N"
Then I would like my query to retrieve the WorkToDo of Id = 2.
Note that the size of ParamValues list is variable!
I'd like to say that I have tried a solution, but the truth is I don't even know how to begin. I've searched on the web but had no luck.
I only have an idea of how I would do this using SQL:
SELECT DISTINCT WORK_TO_DO.ID, WORK_TO_DO.DESCRIPTION
FROM WORK_TO_DO
INNER JOIN PARAM_VALUES_FOR_WORK PV1 ON PV1.WORK_TO_DO_ID = WORK_TO_DO.ID
INNER JOIN PARAM_VALUES_FOR_WORK PV2 ON PV2.WORK_TO_DO_ID = WORK_TO_DO.ID
(... Adding as many INNER JOINs as needed based on list of ParamValues)
INNER JOIN PARAM_VALUES_FOR_WORK PVX ON PVX.WORK_TO_DO_ID = WORK_TO_DO.ID
WHERE PV1.PARAM_DEFINITION_ID = :ParamValues[0].ParamDefinitionId
AND PV1.VALUE = :ParamValues[0].Value
AND PV2.PARAM_DEFINITION_ID = :ParamValues[1].ParamDefinitionId
AND PV2.VALUE = :ParamValues[1].Value
(... Adding as many conditions as needed based on list of ParamValues)
AND PVX.PARAM_DEFINITION_ID = :ParamValues[X].ParamDefinitionId
AND PVX.VALUE = :ParamValues[X].Value
Basically I want to add JOINs and filters to the query based on my list of ParamValues. How can I do this?
Use FilterByItems extension and you can generate desired query:
var requiredCount = ParamValues.Count();
var query = context.WorkToDo
.Where(w => context.ParamValue
.Where(pv = pv.WorkToDoId == w.Id)
.FilterByItems(ParamValues, (pv, v) => pv.ParamDefinitionId == v.ParamDefinitionId && pv.Value == v.Name, true)
.Count() >= requiredCount
);
I have two tables like this
Table 1 : animal
Country Lion Tiger State
india 4 5 Madhya Pradesh
india 10 2 Utrakhand
Russia 0 10 Primorsky krai
Russia 1 20 Khabarovsk Krai
and Table 2: Project_Tiger
Country No
India 10
Russia 5
I have created inumerable class like this
public animal animal {get;set;};
public project_tiger project_tiger {get;set;};
now I want to show result something like this in view
Country NO lion tiger
india 10 14 7
Russia 5 1 30
here lion and tiger is sum of both the record in table 1
10+4= 15, 5+2 =7, for india and same for russia
now I am lacking of knowledge how to query these data as sum from database using linq and how to show this in razor view
I can write query in sql like this but can't translate it to linq
select animal.country, No, sum(lion), sum(tiger) from animal
inner join project_tiger ON animal.country equals project_tiger.country
Any help regarding this will be appreciated.
You basically need to join both the tables and group the results by the country name and generate the result out of that.
var groupd = (from a in dbContext.Animals
join b in dbContext.ProjectTigers on a.Country equals b.Country
select new { Country = a.Country,
No = b.No,
Lion = a.Lion,
Tiger = a.Tiger }
) // We have the join results. Let's group by now
.GroupBy(f => f.Country, d => d,
(key, val) => new { Country = key,
No = val.First().No,
Lion = val.Sum(s => s.Lion),
Tiger = val.Sum(g => g.Tiger) });
This will give you a collection of anonymous objects. If you have a view model/dto to represent your expected data items, you may use that in the projection part of this linq expression.
Also, like others mentioned in the comments, you might want to look into a better way of building your db schema.
You can still acheive it using EntityFramework, and still use the power of the SQL server to preform the lifting for you.
By using directly with the generic method SqlQuery<> this can be done pretty easily.
Create a class that will fit your need
public class AnimalsCount
{
public int No { get; set; }
public int Lion { get; set; }
public int Tiger { get; set; }
}
Now use the Generic method of SqlQuery<AnimalsCount>
var animalsCount = ctx.Database
.SqlQuery<AnimalsCount>("SELECT Country,(SELECT [No] FROM ProjectTiger WHERE Country = a.Country) as [No], sum(lion) as Lion, sum(tiger) as Tiger FROM [Animal] as a GROUP BY Country")
.ToList();
I have two lists of Level, and I want to merge them into a single list with only unique
indexes, and having the higher highscore of the two.
The Level object.
public class Level
{
public int Index { get; set; }
public string User { get; set; }
public int Highscore { get; set; }
public Level(int index,string user, int highscore)
{
Highscore = highscore;
User = user;
Index = index;
}
}
I have this test code
List<Level> Levels = new List<Level>();
List<Level> otherlevels = new List<Level>();
Levels.Add(new Level(1, "Test", 1));
Levels.Add(new Level(2, "Test", 2));
Levels.Add(new Level(3, "Test", 4));
Levels.Add(new Level(4, "Test", 1));
otherlevels.Add(new Level(1, "Test", 4));
otherlevels.Add(new Level(2, "Test", 4));
otherlevels.Add(new Level(3, "Test", 1));
//some linq code here
What I want from the linq code is a list that will have these 4 items.
Level(1,"Test",4)
Level(2,"Test",4)
Level(3,"Test",4)
Level(4,"Test",1)
I managed to group by Index and select the first, but I dont know how to select the one with the max Highscore.
It sounds like you got 90% of the way there by grouping the levels. Now you just need to order them to grab the largest scoring result:
List<Level> newLevels = from x in Enumerable.Concat(Levels, otherLevels)
group x by x.Index into grp
select grp.OrderByDescending(x => x.HighScore).First()
In addition to being more concise than Thomas's solution, this does not have either of his limitations: it will work with lists containing any number of duplicates, and for instances where a level is present in one list but not the other.
You can do an outer join:
from level in levels
join other in otherLevels on level.Index equals other.Index into tmp
from other in tmp.DefaultIfEmpty()
select other == null
? level
: level.HighScore >= other.HighScore ? level : other;
This approach assumes two things:
each list contains only distinct ids
each item in otherLevels has a corresponding item in levels (the reverse doesn't need to be true)
I am using a nested set model and want to be able to select 10 parent items, as well as 10 child items for each parent, which are ordered.
I am trying to write a nested comment system, whereby a reply to a comment will be signified by having left and right values within its 'parent' comment.
I am having a real headache reducing the load times of large lists of items being retrieved and believe if I could do the above in a single Linq statement then I might be able to save a lot more time than making repeated calls to the db to get 10 children for each parent.
I use a this statement to get the parent(or root items) from the datacontext.
var query = context.Items
.Where(x => !context.Items.Any(y => y.LeftPos < x.LeftPos
&& y.RightPos > x.RightPos))
.OrderBy(x => x.Id)
.Take(pageSize)
.ToList();
The above code is getting the outermost (parent) items in the nested set model by checking there are not any other items with left and right values outside of theirs.
So, rather than looping through the parentItems with a foreach (which I am doing currently) and making 10 or (pageSize) calls to the db on each iteration, how can I take 10 children of each parent node with a Linq statement?
EDIT:
I am using the nested set model. My items have left and right positions (leftPos and rightPos). So a child is an object with left and right values within the left and right values of another object, which in turn would be the parent.
1 a 4
2 b 3
so if I have a lot of items
1 a 4
2 b 3
5 c 10
6 d 7
8 e 9
11 f 14
12 g 13
....
Is there a way I can select x amount of children from each parent using Linq?
Any help appreciated
class Program
{
static void Main(string[] args)
{
List<A> lst = new List<A>();
for (int j = 1; j < 4; j++)
{
var tmp = new A() { Value = j * 1000 };
for (int i = 0; i < 150; i++)
{
tmp.SubItems.Add(new B { Value = i + 1, Parent = tmp });
}
lst.Add(tmp);
}
List<B> result = lst.SelectMany(x => x.SubItems.Take(10)).ToList();
}
}
public class A
{
public A()
{
SubItems = new List<B>();
}
public int Value { get; set; }
public List<B> SubItems { get; set; }
}
public class B
{
public int Value { get; set; }
public A Parent { get; set; }
}
not sure if this is what you want. this way you get a collection of subitems. 10 subitems of each parent. you can access the parents with the .Parent property of each subitem...
We have a site that contains streaming video and we want to display three reports of most watched videos in the last week, month and year (a rolling window).
We store a document in ravendb each time a video is watched:
public class ViewedContent
{
public string Id { get; set; }
public int ProductId { get; set; }
public DateTime DateViewed { get; set; }
}
We're having trouble figuring out how to define the indexes / mapreduces that would best support generating those three reports.
We have tried the following map / reduce.
public class ViewedContentResult
{
public int ProductId { get; set; }
public DateTime DateViewed { get; set; }
public int Count { get; set; }
}
public class ViewedContentIndex :
AbstractIndexCreationTask<ViewedContent, ViewedContentResult>
{
public ViewedContentIndex()
{
Map = docs => from doc in docs
select new
{
doc.ProductId,
DateViewed = doc.DateViewed.Date,
Count = 1
};
Reduce = results => from result in results
group result by result.DateViewed
into agg
select new
{
ProductId = agg.Key,
Count = agg.Sum(x => x.Count)
};
}
}
But, this query throws an error:
var lastSevenDays = session.Query<ViewedContent, ViewedContentIndex>()
.Where( x => x.DateViewed > DateTime.UtcNow.Date.AddDays(-7) );
Error: "DateViewed is not indexed"
Ultimately, we want to query something like:
var lastSevenDays = session.Query<ViewedContent, ViewedContentIndex>()
.Where( x => x.DateViewed > DateTime.UtcNow.Date.AddDays(-7) )
.GroupBy( x => x.ProductId )
.OrderBy( x => x.Count )
This doesn't actually compile, because the OrderBy is wrong; Count is not a valid property here.
Any help here would be appreciated.
Each report is a different GROUP BY if you're in SQL land, that tells you that you need three indexes - one with just the month, one with entries by week, one by month, and one by year (or maybe slightly different depending on how you're actually going to do the query.
Now, you have a DateTime there - that presents some problems - what you actually want to do is index the Year component of the DateTime, the Month component of the date time and Day component of that date time. (Or just one or two of these depending on which report you want to generate.
I'm only para-quoting your code here so obviously it won't compile, but:
public class ViewedContentIndex :
AbstractIndexCreationTask<ViewedContent, ViewedContentResult>
{
public ViewedContentIndex()
{
Map = docs => from doc in docs
select new
{
doc.ProductId,
Day = doc.DateViewed.Day,
Month = doc.DateViewed.Month,
Year = doc.DateViewed.Year
Count = 1
};
Reduce = results => from result in results
group result by new {
doc.ProductId,
doc.DateViewed.Day,
doc.DateViewed.Month,
doc.DateViewed.Year
}
into agg
select new
{
ProductId = agg.Key.ProductId,
Day = agg.Key.Day,
Month = agg.Key.Month,
Year = agg.Key.Year
Count = agg.Sum(x => x.Count)
};
}
}
Hopefully you can see what I'm trying to achieve by this - you want ALL the components in your group by, as they are what make your grouping unique.
I can't remember if RavenDB lets you do this with DateTimes and I haven't got it on this computer so can't verify this, but the theory remains the same.
So, to re-iterate
You want an index for your report by week + product id
You want an index for your report by month + product id
You want an index for your report by year + product id
I hope this helps, sorry I can't give you a compilable example, lack of raven makes it a bit difficult :-)