My select returns joined data from db where for every object there are several attributes. For every attribute of the object I have a new row.
I use Entity Framework to retrieve the data.
var products = _ctx.ExecuteFunction<GetProducts_Result>("GetProducts");
GetProducts is a stored proc that returns following result.
Code | Name | Term | Rate
--------+------+------+-----
111 | Foo | 12 | 10
111 | Foo | 24 | 12
111 | Foo | 36 | 16
222 | Boo | 12 | 8
222 | Boo | 24 | 9
How I can efficiently query data with linq and map to a class like
class Product
{
int Code
string Name
List<Term> terms;
}
Where Term is a class with parameters (Term, Rate)
var products = from p in _ctx.ExecuteFunction<GetProducts_Result>("GetProducts")
group p by new {p.Code, p.Name} into g
select new Product
{
Code = g.Key.Code,
Name = g.Key.Name,
terms = g.Select(x => new Terms {
Term = x.Term,
Rate = x.Rate }).ToList()
};
Related
I have this Linq to Entities (EF Core) query which looks like below
var query = (from p in db.Samples
join q in db.Items on p.Id equals q.SampleId
Where p.active = IsActive and p.Id = GivenId
group new
{
p.Name,
p.Address,
p.Marks,
p.LocationId,
q.EmailId,
q.Grade
}
by new
{ q.Grade }
into data
select new DataSummary()
{
UserName = data.Name,
Grade = data.Min(x => x.Grade),
Email = data.Min(x => x.Email,
Total = data.Sum(x => x.Marks)
}.ToList()
Now I have a constant List of Hashset of Grades and Location that looks like this:
public List<(HashSet<string> Grades, HashSet<Location> Loctions)> LocationGrades => new()
{
(new() { "A", "B" }, new()), // Includes all location
(new() { "C"}, new(){
Location.Boston, //Location is Enum
Location.Maine
}
}
I want to get the data where if the student has grade A or B include all location and if the student has grade C only include Boston and Maine.
Is it possible to integrate this within the LINQ to Entities query?
Sample Table
| ID | Name | Address | Marks | LocationId |
|-----|-------|---------|-------|-------------|
| 234 | Test | 123 St | 240 | 3 (Maine) |
| 122 | Test1 | 234 St | 300 | 5 (Texas) |
| 142 | Test1 | 234 St | 390 | 1 (Boston) |
Items Table
| ID | SampelId | Grade | Email |
|----|----------|-------|-------|
| 12 | 234 | A | a.com |
| 13 | 122 | C | b.com |
| 14 | 142 | C | c.com |
So, In the table above I shouldn't get Texas row but get Boston row as they both have Grade C but Texas does not exist in the HashSet combo.
Okay, now I got it. You have to add dynamic ORed constraints to the query based on a given list of elements. This is a little tricky, because AND can be done with using multiple .Where() statements, but OR not. I did something similar recently against CosmosDB by using LinqKit and the same should also work against EF.
In your case you probably of to do something like this:
...
into data
.WhereAny(grades, (item, grade) => item.Grade == grade)
select new DataSummary()
...
I think the given example doesn't match your exact case, but it allows you to define multiple ORed constraints from a given list and I think this is the missing part you're searching. Take care to use within the lambda method only definitions which are also supported by EF core. The given inner enumeration (in this example grades) will be iterated on the client side and can be dynamically build with everything available in C#.
Please be kind to a LINQ dummy...
Say I have 8 rows data like this:
+-------------------+
| ID WORKTYPEDESC|
+-------------------+
| 1 plumber |
| 1 carpenter |
| 1 electrician |
| 2 juggler |
| 2 mime |
| 3 writer |
| 3 actor |
+-------------------+
As an 8-item IList<Worktype> collection where Worktype looks like
public class Worktype
{
public int Id { get; set; }
public string WorktypeDesc { get; set; }
}
And what I want is to aggregate by Id to get a list with 3 Worktypes, each with a WorktypeDesc being a sorted, semicolon-delimited list, like this:
+-------------------------------------+
| ID WORKTYPEDESC |
+-------------------------------------+
| 1 carpenter;electrician;plumber |
| 2 juggler;mime |
| 3 actor;writer |
+-------------------------------------+
??
Just group by Id, use string.Join to aggregate within each group.
IEnumerable<Worktype> source = ...;
var query = source.GroupBy(
x => x.Id,
(id, g) => new Worktype
{
Id = id,
WorktypeDesc = string.Join(";", g.Select(x => x.WorktypeDesc).OrderBy(x => x))
});
I have two DataTables:
DataTable dtCatalogFromMySql;
DataTable dtDataForExistingProducts;
dtCatalogFromMySql
Id | productname | barcode | pricesell| type
---+--------------+----------+----------+------
1 | Abz | 123 | 2.01 | RS // different product name
2 | Abd | 122 | 8.90 | RS // different price
3 | Abx | 125 | 21.00 | WS // both different
4 | Abf | 124 | 2.11 | RS
5 | Abg | 126 | 8.01 | WS
6 | Abh | 127 | 60.23 | RS
7 | Abi | 128 | 9.10 | RS
dtDataForExistingProducts
Id | productname | barcode | pricesell| type
---+--------------+----------+----------+------
1 | Abc | 123 | 2.01 | RS
2 | Abd | 122 | 3.90 | RS
3 | Abe | 125 | 23.00 | WS
4 | Abf | 124 | 2.11 | RS
5 | Abg | 126 | 8.01 | WS
6 | Abh | 127 | 60.23 | RS
7 | Abi | 128 | 9.10 | RS
I need return only rows which are different as in first table
I need select all data where Prod_No equals to baracode and Descript not equals to productname and Retail_PRC also not equals to pricesell.
I am not getting results with this code
List<DataRow> matchingRows = dtCatalogFromMySql.AsEnumerable()
.Where(a => dtDataForExistingProducts.AsEnumerable()
.Select(b => b.Field<string>("Prod_No"))
.Contains(a.Field<string>("barcode")))
.Where(a => !dtDataForExistingProducts.AsEnumerable()
.Select(b => b.Field<string>("Descript"))
.Equals(a.Field<string>("productname")))
.Where(a => !dtDataForExistingProducts.AsEnumerable()
.Select(b => b.Field<decimal>("Retail_PRC"))
.Equals(Convert.ToDecimal(a.Field<double>("pricesell"))))
.ToList();
I suppose, Contains() will also fetch the data if barcode = 123456 and Prod_No = 1234, it is right? If I am right what is right way to compare string exactly same
You may want to consider a clearer syntax such as:
var results = from t1 in dtCatalogFromMySql.AsEnumerable()
join t2 in dtDataForExistingProducts.AsEnumerable() on
(string)t1["barcode"] equals (string)t2["Prod_No"]
where (string)t1["productname"] != (string)t2["descript"] &&
Convert.ToDecimal((double)t1["pricesell"]) !=
(decimal)t2["Retail_PRC"]
select t2;
The Join is definitely the way to go. You can modify the select according to your required result set.
trighati makes a good point about using OR instead of AND. This is assuming that you want all of the data where at least one of your values changed where Prod_no and barcode are equal. This would change the query to be:
var results = from t1 in dtCatalogFromMySql.AsEnumerable()
join t2 in dtDataForExistingProducts.AsEnumerable() on
(string)t1["barcode"] equals (string)t2["Prod_No"]
where (string)t1["productname"] != (string)t2["descript"] ||
Convert.ToDecimal((double)t1["pricesell"]) !=
(decimal)t2["Retail_PRC"]
select t2;
Use Join to combine them into one result set, then filter the result set:
var combined = dtDataForExistingProducts.AsEnumerable()
.Join(dtCatalogFromMySql.AsEnumerable(),
ep => ep.Field<string>("Prod_No")
ce => ce.Field<string>("barcode"),
(ep, ce) => new {ExistingProduct = ep, CatalogEntry = ce})
.Where(m => !m.ExistingProduct.Field("Descript")
.Equals(m.CatalogEntry.Field("productname")))
.Where(m => decimal.Parse(m.ExistingProduct.Field("Retail_PRC").ToString())
!= decimal.Parse(m.CatalogEntry.Field("pricesell").ToString()))
.ToList()
;
I have datatable looks like this:
| date | value |
| 1/1/2013 10:28 | 5 |
| 1/1/2013 10:29 | 6 |
| 2/1/2013 01:54 | 6.5 |
| 2/1/2013 02:24 | 6.7 |
| 2/1/2013 03:14 | 8 |
I want to group the table into days.
then to calculate the avarage value of every group.
then to make avarage of all the values I calculated in step 2.
is there any good way to do it through linq ?
Thanks
of course you can use linq for this purpose:
var results = from p in list
group p by p.date.Date into g
select new { date = g.Key, value = g.Average(p=> p.value) };
var endAverage = results.Average(x => x.value);
I have 5 tables:
course_id | course_name (course)
------------------------------
1 | Basic1
2 | Basic2
3 | Basic3
4 | Basic4
5 | Basic5
course_id | trainer_id (course_trainer)
-----------------------------
1 | 1
1 | 2
2 | 2
3 | 2
4 | 3
4 | 2
5 | 3
course_id | topic_id (course_topic)
-----------------------------
1 | 1
1 | 2
2 | 2
3 | 2
4 | 3
4 | 2
5 | 3
trainer_id| trainer_name (trainer)
-----------------------------
1 | Tom
2 | Thomas
3 | Sue
tropic_id | topic_name (topic)
-----------------------------
1 | Skill 1
2 | Skill 2
3 | Skill 3
How can I use LINQ to select with result as below
Course_name | Trainer_name | Topic_name
----------------------------------------------
Basic 1 | Tom, Thomas | Skill 1, Skill 2
Basic 2 | Thomas | Skill 2
Basic 3 | Thomas | Skill 2
Basic 4 | Sue, Thomas | Skill 3, Skill 2
Basic 5 | Sue | Skill 3
That is my code in C#, but the result isn't correct. Please help me, many thanks !
public class course_datatable
{
public string course_name {get; set;}
public string trainer_name {get; set;}
public string topic_name {get; set;}
}
IQueryable<course_datatable> coursequery =
from c in db.course
join ct in db.course_trainer on c.course_id equals ct.course_id
join t in db.trainers on ct.trainer_id equals t.trainer_id
join ctopic in db.course_topic on c.course_id equals ctopic.course_id
join topic in db.topic on ctopic.topic_id equals topic.topic_id
select new course_datatable()
{
course_name = c.course_name,
trainer = t.trainer_name,
topic = topic.topic_name
};
Get your data from your database:
var result = context.Courses.Select(c =>
new { Course = c, Trainers = c.Trainers, Skills = c.Skills }).ToList();
and then flatten the Trainers and Skills objects using String.Join:
result.Select(r => new
{
Course = r.Course.Course_Name,
Trainer = String.Join(",", r.Trainers.Select(t => t.TrainerName).ToArray()),
Skill = String.Join(",", r.Skills.Select(S => S.SkillName).ToArray())
});
edit
Using your schema, I'll rename so that it should work.
var result = db.course.Select(c => new
{
Course = c,
Trainers = c.course_trainer.trainers,
Skills = c.course_topic.topic
}).ToList();
result.Select(r => new
{
Course = r.Course.course_Name,
Trainer = String.Join(",", r.Trainers.Select(t => t.trainer_name).ToArray()),
Skill = String.Join(",", r.Skills.Select(S => S.topic_name).ToArray())
});
You can do this all in one statement but I've structured it this way so that it's hopefully clearer for you.
Because you seem unable to use my initial answer (which is preferred because doesn't require redundant join conditions), I'll work with your existing code and show you how to group and project.
Starting with this:
var coursequery =
from c in db.course
join ct in db.course_trainer on c.course_id equals ct.course_id
join t in db.trainers on ct.trainer_id equals t.trainer_id
join ctopic in db.course_topic on c.course_id equals ctopic.course_id
join topic in db.topic on ctopic.topic_id equals topic.topic_id
select new course_datatable()
{
course_name = c.course_name,
trainer = t.trainer_name,
topic = topic.topic_name
};
You then want to GroupBy the course_name
var groups = coursequery.GroupBy(item => item.course_name);
and then each group needs to project into your new result type
var result = groups.Select(group =>
new course_datatable
{
course_name = group.Key,
trainer_name = String.Join(",", group.Select(i=> i.trainer_name).ToArray()),
topic_name = String.Join(",", group.Select(i => i.topic_name).ToArray()),
}).ToList();
or if you want to try something else (for fun) use LINQs Aggregate method, rarely used:
var result = groups.Select(group =>
group.Aggregate((initial, next) =>
{
initial.topic_name += String.Format(", {0}", next.topic_name);
initial.trainer_name += String.Format(", {0}", next.trainer_name);
return initial;
})).ToList();