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();
Related
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
I have two lists. One list is of type Cascade (named, cascadeList) & the other list if of type PriceDetails (named priceList), both classes are shown below. I have also given a simple example of what I'm trying to achieve below the classes.
So the priceList contains a list of PriceDetail objects where they can be multiple (up to three) PriceDetail objects with the same ISIN. When there are multiple PriceDetails with the same ISIN I want to select just one based on the Source field.
This is where the cascadeList comes in. So if there were 3 PriceDetails with the same ISIN I would want to select the one where the source has the highest rank in the cascade list (1 is the highest). Hopefully the example below helps.
Reason for the question
I do have some code that is doing this for me however its not very efficient due to my lack of skill.
In a nutshell it first creates a unique list of ISIN's from the priceList. It then loops through this list for each unique ISIN to get a list of the PriceDetails with the same ISIN then uses some if statements to determine which object I want. So hoping and pretty sure there is a better way to do this.
My Classes
class Cascade
{
int Rank;
string Source;
}
class PriceDetails
{
string ISIN;
string Sedol;
double Price;
string Source;
}
Example
PriceList Cascade
ISIN Source Price Source Rank
BN1 XYZ 100 ABC 1
MGH PLJ 102 XYZ 2
BN1 PLJ 99.5 PLJ 3
BN1 ABC 98
MGH XYZ 102
Result I'm looking for
PriceList
ISIN Source Price
BN1 ABC 98
MGH XYZ 102
For getting the desired result we must do these steps:
Join two lists based on Source property.
Group the last result by ISIN property.
After grouping we must get the minimum rank for
each ISIN.
Then we will use the minRank variable to compare it
against the rank of an elements with the same ISIN and then select
the first element.
We can write this query either with query or method syntax.
With query syntax:
var result = from pr in pricesList
join cas in cascadesList on pr.Source equals cas.Source
select new { pr, cas } into s
group s by new { s.pr.ISIN } into prcd
let minRank = prcd.Min(x => x.cas.Rank)
select prcd.First(y => y.cas.Rank == minRank).pr;
With method syntax:
var result = pricesList.Join(cascadesList,
pr => pr.Source,
cas => cas.Source,
(pr, cas) => new { pr, cas })
.GroupBy(j => j.pr.ISIN)
.Select(g => new { g, MinRank = g.Min(x => x.cas.Rank) })
.Select(r => r.g.First(x => x.cas.Rank == r.MinRank).pr);
Result will be same with both ways:
PriceList
ISIN Source Price
BN1 ABC 98
MGH XYZ 102
P.S: I have assumed that list's name is as following: pricesList and cascadesList
from pr in priceList
join c in cascadeList on pr.Source = c.Source
order by c.Rank
select new {Isin = pr.Isin, Source = pr.Source, Price = pr.Price}
See if this works for you
priceList.GroupBy(p => p.ISIN).OrderByDescending(p =>
cascadeList.FirstOrDefault(c => c.Source == p.Source).Rank).First();
I have a LINQ class of this format in C#:
class Vehicle
{
int _VehicleID
int _ModelID
EntetySet<Cars> _AllCars
EntetySet<Bus> _AllBus
EntetyRef<Driver> _Person
}
Cars table
CarsID | Manufacturer | Type
1000 | Honda | Diesel
1001 | Mitsubishi | Petrol
1002 | Maruti | Diesel
Bus table
BusID | Manufacturer | Type
2000 | Volvo | Diesel
2001 | TATA | Petrol
2002 | layland | Petrol
From the UI, I will get a Vehicle ID as a parameter. Based on that, all the cars, Bus and its associated tables will get copied to a variable. That is:
Vehicle v1 = new Vehicle();
v1 = dc.vehicle.where(v => v.vehicleID == param.vehicleID).findfirst();
My v1 has all the table and its contents which satisfies the above condition.
Now, I want to filter some more based on the table content present in table cars and bus; i.e based on type of vehicle petrol or diesel.
If I want to copy all the petrol cars and Buses using a query statement in a single line, please tell me the way.
Thanks in advance.
** Edit based upon comments **
The logic of your question somehow slipped by me, because I was taking a logical Human approach on vehicle. Whenever I look at this Vehicle class I instantly see cars and busses as vehicles to.
This indicates more that the naming is poorly chosen.
But no more judging from me.
I understand you want one vehicle by id and you want the result to only contain cars and busses riding on let's say Petrol, in a single line.
If it is translated to SQL than I don't know how far the tree can go within a select, but if it is in memory than you can go a long way inside a subselect.
You could use a 'Model' class to achieve that or you could go with an anonymous type, but I will give an example for a 'Model' class:
Example:
public class VehicleModel
{
public int VehicleID { get; set; }
public int ModelID { get; set; }
public List<Cars> Cars { get; set; }
public List<Bus> Buses { get; set; }
}
var vehicleID = 1; // just for the example of course.
var fuelType = "Petrol";
var vehicle = (from v in dc.vehicle
where v._VehicleID == vehicleID
select new VehicleModel
{
VehicleID = v._VehicleID,
ModelID = v._ModelID,
Cars = v._AllCars.Where(car => car.Type == fuelType).ToList(),
Buses = v._AllBus.Where(bus => bus.Type == fuelType).ToList()
}).SingleOrDefault();
// use FirstOrDefault() when _VehicleID is NOT unique to get TOP 1
Do not modify the EntitySets within the Entity itself, always use a model for these things, because if you would do that and call save changes on your EntityContainer all sorts of things could go wrong.
If you want to know about anonymous types take a look here:
http://msdn.microsoft.com/en-us/library/bb397696(v=vs.100).aspx
Also take a look here for Linq examples:
http://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b
- before edit
Do you mean this:
var query = dc.vehicle.Where(v =>
v._AllCars.Any(c => c.Manufacturer == "Honda") ||
v._AllBuss.Any(b => b.Manufacturer == "Volvo"));
This will give you all vehicles where the cars are Honda Or the Busses are Volvo.
Which will be:
1000 | Honda | Diesel
2000 | Volvo | Diesel
the result is an IEnumerable containing all or no items satisfying the criteria.
If you only want the first hit you can do:
//if it must exist, otherwise this throws an exception
var firstVehicle = query.First();
//if it may exist, otherwise null
var firstVehicle = query.FirstOrDefault();
It seems that you want something like this:
var v1 = dc.vehicle.Where(v => v.vehicleID == param.vehicleID
&& v.Cars.All(c => c.Type == "Petrol")
&& v.Buses.All(c => c.Type == "Petrol")).First();
)
to get a "Vehicle" of which all cars and buses have petrol. (Note that I use "Cars" and not the unconventional name "_AllCars", etc.).
Edit
Or:
var v1 = dc.vehicle.Where(v => v.vehicleID == param.vehicleID)
.Select(v => new {
Id = v.vehicleID,
Cars = v.Cars.Where(c => c.Type == "Petrol"),
Buses = v.Buses.Where(c => c.Type == "Petrol")
}).First();
to get an anonymous type with filtered collections.
I need some help with Linq self-join.
I have the following classs:
public class Owner
{
public string OwnerId {get;set;}
public string Name {get;set;}
public string Area {get;set;}
public string City {get;set;}
public string Sex {get;set;}
public List<Dog> dog {get;set;}
}
And table....
ID OwnerId OwnerName TypeId TypeName TypeValue TypeCodeId
1 1 John 1 Area United States 440
2 1 John 2 City Los-Angeles 221
3 1 John 3 Sex Female 122
4 2 Mary 1 Area Mexico 321
4 2 Mary 2 City Cancun 345
............................................................
I need to parse results of table1 into list of owners the fastest way possible.
Note: Type can be null, but I still need to show that owner (So, I assume left join should work).
Here's What I do. (owners is a webservice class that contains table1 results)
public IEnumerable<Owner> GetOwners() {
return (from owner in owners
join area in owners into owner_area
from oa in owner_area.DefaultIfEmpty()
join City in owners into owner_city
from oc in owner_city.DefaultIfEmpty()
join sex in owners into owner_sex
from os in owner_sex.DefaultIfEmpty()
where oa.TypeId == 1 && oc.TypeId ==2 && os.TypeId ==3
select new Owner() {OwnerId = owner.OwnerId,
Name = owner.Name,
Area = oa.TypeValue,
City = oc.TypeValue,
Sex = os.TypeValue}).Distinct();
}
This query has several issues:
It returns multiple results and distinct does not work as expected
I've tried to use GroupBy but it says that cannot implicitly convert Owner into IEnumerable <int, Owner>
It is super slow
How can I get distinct record with self join and improve performance?
Thanks
UPDATE:
Thank you guys for your ansewers, testing now, but I figured out that I forgot to supply one more thing. I've added a new column called TypeCodeId to the table layout(see above)
User can filter values based on their selection. So, I have TypeCodeId + TypeValue dictionaries for Area, City and Sex. All of those parameters are optional (If the user didn't select any, I just show them all records.
So, Assume that the user has selected filter Area: Unites States and filter City: Los Angeles
them my query would look like this:
Select Projects where Area equals United States(440) and City equals Los Angeles(221)
If Only Area:Mexico was selected then my query would read something like this:
Select Projects where Area equals Mexico(321)
I'm not sure how to do optional where clauses with what you've provided in the examples.
Since the table isn't normalized we need to get the distict users from the objects/table. This can be accomplished with:
owners.Select(o => new { o.OwnerId, o.OwnerName }).Distinct()
And then we need to join the "types" with two matching values, one for the ownerId and another for the specific type.
var ownerQuery =
from o in owners.Select(o => new { o.OwnerId, o.OwnerName }).Distinct()
join area in owners on new { o.OwnerId, TypeId = 1 } equals new { area.OwnerId, area.TypeId } into areas
from area in areas.DefaultIfEmpty()
join city in owners on new { o.OwnerId, TypeId = 2 } equals new { city.OwnerId, city.TypeId } into cities
from city in cities.DefaultIfEmpty()
join sex in owners on new { o.OwnerId, TypeId = 3 } equals new { sex.OwnerId, sex.TypeId } into sexes
from sex in sexes.DefaultIfEmpty()
select new
{
owner = o,
Area = (area != null) ? area.TypeValue : null,
City = (city != null) ? city.TypeValue : null,
Sex = (sex != null) ? sex.TypeValue : null,
};
You may need to change the projection in the above example.
For best performance if think this is the way to do it.
public IEnumerable<Owner> GetOwners(IEnumerable<Tuple<int, int>> filter)
{
var q = (from o in owners
join f in filter on
new {o.TypeId, o.TypeCodeId} equals
new {TypeId = f.Item1, TypeCodeId = f.Item2}
select o).ToList();
var dic = q.ToDictionary (o => new {o.OwnerId, o.TypeId}, o => o.TypeValue);
foreach (var o in q.Select(o => new { o.OwnerId, o.OwnerName }).Distinct())
{
var owner = new Owner()
{
OwnerId = o.OwnerId,
Name = o.OwnerName
};
string lookup;
if(dic.TryGetValue(new {o.OwnerId, TypeId = 1}, out lookup))
owner.Area = lookup;
if(dic.TryGetValue(new {o.OwnerId, TypeId = 2}, out lookup))
owner.City = lookup;
if(dic.TryGetValue(new {o.OwnerId, TypeId = 3}, out lookup))
owner.Sex = lookup;
yield return owner;
}
}
To get even a little more performance you can write an IEqualityComparer class that only compares int OwnerId and send it into the Distinct function
How to merge two lists using LINQ like the following:
class Person
{
public int ID { get; set;}
public string Name { get; set;}
public Person Merge( Person p)
{
return new Person { ID = this.ID, Name = this.Name + " " + p.Name };
}
}
I have two List of person:
list1:
1, A
2, B
list2:
2, C
3, D
I want the result like the following
result:
1, A
2, B C
3, D
Any help!
I would strongly recommend against using string-concatenation to represent this information; you will need to perform unnecessary string-manipulation if you want to get the original data back later from the merged list. Additionally, the merged version (as it stands) will become lossy if you ever decide to add additional properties to the class.
Preferably, get rid of the Merge method and use an appropriate data-structure such as a multimap that can each map a collection of keys to one or more values. The Lookup<TKey, TElement> class can serve this purpose:
var personsById = list1.Concat(list2)
.ToLookup(person => person.ID);
Anyway, to answer the question as asked, you can concatenate the two sequences, then group persons by their ID and then aggregate each group into a single person with the provided Merge method:
var mergedList = list1.Concat(list2)
.GroupBy(person => person.ID)
.Select(group => group.Aggregate(
(merged, next) => merged.Merge(next)))
.ToList();
EDIT: Upon re-reading, just realized that a concatenation is required since there are two lists.