Contains matches on pairs of items - c#

I have a mapping table in the following form:
Id ReferenceId ReferenceType LinkId
To retrieve a set of combinations, I could run each query separately:
var pairs = new List<Pair>
{
Pair.Create(1000, "Car"),
Pair.Create(2000, "Truck"),
};
var maps = new List<Mapping>();
foreach (var pair in pairs)
{
maps.AddRange(context.Mappings.Where(x => x.ReferenceId = pair.Id && x.ReferenceType == pair.Type).ToList());
}
However, I want to combine these into a single statement to reduce my hits on the db. Is there some form of Contains statement that can work with pairs of objects? Or is it possible to append an OR clause onto an IQueryable within a loop? Any other solutions?

Not sure if it works for your LINQ provider but you could try to join with an anonymous type:
var mapQuery = from p in pairs
join m in context.Mappings
on new { p.Id, p.Type } equals new { m.ReferenceId, m.ReferenceType}
select m;
List<Mapping> maps = mapQuery.ToList();

You could union your queries together.
Something like this:
var pairs = new List<Pair>
{
Pair.Create(1000, "Car"),
Pair.Create(2000, "Truck"),
};
List<Mapping> result =
pairs
.Select(pair =>
context.Mappings.Where(
x => x.ReferenceId == pair.Id
&& x.ReferenceType == pair.Type))
.Aggregate(Queryable.Union)
.ToList();

Related

SQL to LINQ expres

I'm trying to convert a SQL expression to Linq but I can't make it work, does anyone help?
SELECT
COUNT(descricaoFamiliaNovo) as quantidades
FROM VeiculoComSeminovo
group by descricaoFamiliaNovo
I try this:
ViewBag.familiasCount = db.VeiculoComSeminovo.GroupBy(a => a.descricaoFamiliaNovo).Count();
I need to know how many times each value repeats, but this way it shows me how many distinct values ​​there are in the column.
You can try:
var list = from a in db.VeiculoComSeminovo
group a by a.descricaoFamiliaNovo into g
select new ViewBag{
familiasCount=g.Count()
};
or
var list = db.VeiculoComSeminovo.GroupBy(a => a.descricaoFamiliaNovo)
.Select (g => new ViewBag
{
familiasCount=g.Count()
});
If you need column value:
new ViewBag{
FieldName=g.Key,
familiasCount=g.Count()
};
You don't need the GROUP BY unless there are fields other than the one in COUNT. Try
SELECT
COUNT(descricaoFamiliaNovo) as quantidades
FROM VeiculoComSeminovo
UPDATE, from your comment:
SELECT
COUNT(descricaoFamiliaNovo) as quantidades,
descricaoFamiliaNovo
FROM VeiculoComSeminovo
GROUP BY descricaoFamiliaNovo
That's it as SQL. In LINQ it is something like:
var reponse = db.VeiculoComSeminovo.GroupBy(a => a.descricaoFamiliaNovo)
.Select ( n => new
{Name = n.key,
Count = n.Count()
}
)
Not tested.
Ty all for the help.
I solved the problem using this lines:
// get the objects on db
var list = db.VeiculoComSeminovo.ToList();
// lists to recive data
List<int> totaisFamilia = new List<int>();
List<int> totaisFamiliaComSN = new List<int>();
// loop to cycle through objects and add the values ​​I need to their lists
foreach (var item in ViewBag.familias)
{
totaisFamilia.Add(list.Count(a => a.descricaoFamiliaNovo == item && a.valorSeminovo == null));
totaisFamiliaComSN.Add(list.Count(a => a.descricaoFamiliaNovo == item && a.valorSeminovo != null));
}
The query was a little slow than I expected, but I got the data

Joining Collections and Assigning values

I have two collections and i want to join them based on key attribute and assign one collection's values to other. I am doing it in following way
var joinedData = from collection_one in Office.Employees
join collection_two in NewOffice.Employees
on collection_1.OfficeId equals collection_two.OfficeId
select new { collection_one, collection_two};
// Declare a new Collection
ICollection<Office.Employees> updatedCollection = New List<Office.Employees>();
// Assign New Collection_Two Values to Collection_One
foreach (var item in joinedData.ToList())
{
item.collection_one.Deleted = item.collection_two.Deleted;
updatedCollection .Add(item.obp);
}
this is not producing the right result. My Join is producing more records than it should for an inner join. Can anyone spot an issue ?
Try left join using DefaultIfEmpty() :
var updatedCollection =
(from collection_one in Office.Employees
from collection_two in NewOffice.Employees.Where(x => x.OfficeId == collection_one.OfficeId).DefaultIfEmpty()
where collection_two == null
select new {
collection_one,
collection_two
}).Select(x => {
x.collection_one.Deleted = x.collection_two.Deleted;
return x.obp;
});
But what is x.obp? Maybe result should be x.collection_one?

Performing a union in LINQ

I'm trying to get the union of these two queries but keep getting the following error:
'System.Linq.IQueryable<AnonymousType#1>' does not contain a definition for 'Union' and the best extension method overload 'System.Linq.ParallelEnumerable.Union<TSource>(System.Linq.ParallelQuery<TSource>, System.Collections.Generic.IEnumerable<TSource>)' has some invalid arguments
The linq queries look like this:
var g = from p in context.APP_PROD_COMP_tbl
where p.FAM_MFG == fam_mfg
group p by new
{
a_B_G = p.B_G,
a_MFG = p.MFG,
a_PRODUCT_FAM = p.PRODUCT_FAM,
};
var q = from p in context.APP_COMP_tbl
where p.FAM_MFG == fam_mfg
group p by new
{
a_B_G = p.a_B_G,
a_MFG = p.a_MFG,
a_PRODUCT_FAM = p.a_PRODUCT_FAM,
};
var data = q.Union(g);
I've tried using IEnumerable around the queries, but it still didn't work. Not really sure where I'm going wrong at this point, although admittedly LINQ isn't something I've had a ton of exposure to.
Update:
So I've gone in a slightly different direction from what I posted earlier. After doing more research, the group by statements were from old code and no longer needed for the intended purpose. I changed those to select new statements and had no further issue with the union.
I think that your problem here is type mismatch: g is of type IGrouping<AnonymousType#1, APP_PROD_COMP_tbl> and q is of type IGrouping<AnonymousType#1, APP_COMP_tbl>; this is why Union gives you the error.
I am not really sure what you are trying to Union (keys of the groups or groups of data themselves) but the solution would be:
If you want to union group keys, select the keys of your groups
var data = g.Select(x => x.Key).Union(q.Select(x => x.Key));
If you want to union the groups themselves then you need to project each element from both sequences into a common type, perform the grouping and then union the groups
var g = context.APP_PROD_COMP_tbl
.Where(p => p.FAM_MFG == fam_mfg)
.Select(ToCommonType)
.GroupBy(p => new
{
a_B_G = p.B_G,
a_MFG = p.MFG,
a_PRODUCT_FAM = p.PRODUCT_FAM,
});
var q = context.APP_COMP_tbl
.Where(p => p.FAM_MFG == fam_mfg)
.Select(ToCommonType)
.GroupBy(p => new
{
a_B_G = p.a_B_G,
a_MFG = p.a_MFG,
a_PRODUCT_FAM = p.a_PRODUCT_FAM,
});
var data = g.Union(q);
private CommonClass ToCommonType(APP_PROD_COMP_tbl item)
{
return new CommonClass
{
};
}
private CommonClass ToCommonType(APP_COMP_tbl item)
{
return new CommonClass
{
};
}
The problem is your Anonymouse types don't match:
var a = Enumerable.Range(1, 10).Select(x => new {a = x}).AsQueryable();
var b = Enumerable.Range(1, 10).Select(x => new {b = x}).AsQueryable();
var c = a.Union(b);
This won't work because typeof a is not same as typeof b
var a = Enumerable.Range(1, 10).Select(x => new {a = x}).AsQueryable();
var b = Enumerable.Range(1, 10).Select(x => new {a = x}).AsQueryable();
var c = a.Union(b);
But this will work, because Anonymouse types are the same.
You can try selecting same anonymouse types from your collection in q and g. Read more about Union for IQueryable
Union on IQueryAble<TSource>() accepts IQueryAble<TSource> as a parameter, so collection has to be the same type.

How do I return data presorted after grabbing them inside a foreach loop?

First, I'm grabbing ClientID. Then, I get all Invoices associated with that ClientID. I want to return data all ordered by InvoiceNumber, descending. Here's my code:
var rvInvoices =
(from i in db.QB_INVOICES_HEADER
where i.ClientID == cId
select i).ToList();
foreach (var itm in rvInvoices)
{
InvoiceModel cm = new InvoiceModel()
{
InvoiceNumber = itm.InvoiceNumber,
InvoiceSentDt = itm.InvoiceSentDt,
InvoiceDt = itm.InvoiceDt,
Amount = itm.Amount,
Term = itm.Term,
ClientName = itm.CI_CLIENTLIST.ClientName
};
listInvoices.Add(cm);
}
return listInvoices;
listInvoices.OrderByDescending(x => x.InvoiceNumber).ToList()
You should try something like this:
var rvInvoices =
(from i in db.QB_INVOICES_HEADER
where i.ClientID == cId
select i).OrderByDescending(x => x.InvoiceNumber);
And I don't see a reason you need to call .ToList().
You can do the order in three places.
In the initial query,
In the foreach, or
In the return
Option 1:
var rvInvoices =
(from i in db.QB_INVOICES_HEADER
where i.ClientID == cId
select i).OrderByDescending(i => i.InvoiceNumber).ToList();
Option 2:
foreach (var itm in rvInvoices.OrderByDescending(i => i.InvoiceNumber))
Option 3:
return listInvoices.OrderByDescending(i => i.InvoiceNumber).ToList();
I would suggest taking route 1 since it will run the order at the database level.
You should order them on the database instead of the client:
var rvInvoices = db.QB_INVOICES_HEADER
.Where(i => i.ClientID == cId)
.OrderByDescending(i => i.InvoiceNumber);
The method you currently have creates multiple lists, has an explicit foreach loop, and needs to have its output sorted. It can be done with just creating a single list, no explicit looping, and with the database doing the sorting for you:
return
(from i in db.QB_INVOICES_HEADER
where i.ClientID == cId
// have the database do the sorting
orderby i.InvoiceNumber descending
select i)
// break out of the DB query to make InvoiceModel
.ToEnumerable()
.Select(itm => new InvoiceModel()
{
InvoiceNumber = itm.InvoiceNumber,
InvoiceSentDt = itm.InvoiceSentDt,
InvoiceDt = itm.InvoiceDt,
Amount = itm.Amount,
Term = itm.Term,
ClientName = itm.CI_CLIENTLIST.ClientName
})
// only create one list as the last step
.ToList();

How to initialize var outside foreach

I want to initialize var outside the foreach loop.
Here is my code:
public List<Course> GetCourse()
{
IList<Semester> semesters = Semester.Get();
foreach (Semester sm in semesters)
{
IList<CourseInstance> courseInstances = CourseInstance.Get(sm[0].SemesterId);
var courseInfos = from c in courseInstances
select new Course { Code = c.Course.Code, Name = c.Course.Name };
}
return courseInfos.ToList();
}
How do I initialize courseInfos out side the foreach loop? I try to initialize with null give me error!
var infers the type from the value you are initialising with, so initialising with null will never work. Everything else will.
I believe the Linq statement you want is
var courses = semesters
.SelectMany( s => CourseInstance.Get(s.SemesterId)
.Select( c => new Course ( Code = c.Course.Code, Name = c.Course.Name ) )
.ToList();
EDIT:
If you want to map SemesterName to a list of courses, I would recommend a dictionary.
semesters.ToDictionary(semester => semester.Name, semester =>
semesters.SelectMany(sid =>
CourseInstance.Get(sid.SemesterId))
.Select(c => new Course
{Code = c.Course.Code, Name = c.Course.Name}).ToList())
This will create a Dictionary<string, List<Course> This is nearly identical to the code below, except that it maps the semester.Name as the key. This would, of course, mean you have to have unique semester names, otherwise the dictionary can't be created.
You are reinitializing courseInfos every time you loop in the foreach, so you will only get a list of the last semesterId.
You can write a linq query that does this all in one line for you.
return semesters.SelectMany(sid => CourseInstance.Get(sid.SemesterId))
.Select(c => new Course { Code = c.Course.Code,
Name = c.Course.Name }).ToList()
To break it down,
.SelectMany(sid => CourseInstance.Get(sid.SemesterId))
does the same thing as the foreach. It will return an IEnumerable<CourseInstance>.
After that, you are calling
.Select(c => new Course { Code = c.Course.Code, Name = c.Course.Name })
on the result that we got in the last section; it returns an IEnumerable<Course> that you turn into a list.
SelectMany works similar to Select except it will take each IEnumerable<Course> and flatten it into one sequence instead of IEnumerable<IEnumerable<Course>>
The answer is:
IEnumerable<Course> courseInfos = null;
foreach (Semester sm in semesters)
{
IList<CourseInstance> courseInstances = CourseInstance.Get(semesters[0].SemesterId);
courseInfos = from c in courseInstances
select new Course { Code = c.Course.Code, Name = c.Course.Name };
}
return courseInfos.ToList();
However, you are discarding everything but the final iteration of the foreach. Is this what you meant to do?
why not just initialize the first instance of courseInfo with the first semester and then iterate over Semesters, is there a reason you need to initialize courseInfo before the foreeach?
may be this help. Collecting course info in each iteration.
public List<Course> GetCourse()
{
IList<Semester> semesters = Semester.Get();
List<Course> courseInfos = new List<Course>();
foreach (Semester sm in semesters)
{
IList<CourseInstance> courseInstances = CourseInstance.Get(sm.SemesterId);
IEnumerable<Course> result = from c in courseInstances
select new Course { Code = c.Course.Code , Name = c.Course.Name };
courseInfos.AddRange(result);
}
return courseInfos;
}

Categories

Resources