Entity Framework LINQ Get all items part of another collection - c#

Get all the NWatchRelation records from the DBContext that overlap those in the relationsCollection.
The same Id, RelatedNodeId, and RelationType (enum: int) should be what's considered a match.
public class NWatchRelation : INWatchRelation
{
public int Id { get; set; }
public int NodeId { get; set; }
public NWatchNode Node { get; set; }
public int RelatedNodeId { get; set; }
public NWatchNode RelatedNode { get; set; }
public NWatch.NWatchRelationType RelationType { get; set; }
}
INWatchRelation[] relationsCollection = GetRelations();

You can do a LINQ join between these 2 collections.
var result = from a in db.NWatchRelations.AsEnumerable()
join b in relationsCollection on a.RelatedNodeId equals b.RelatedNodeId
&& a.Id equals b.Id
&& a.RelationType equals b.RelationType
select a;

The only way you can do that fully in LINQ to Entities is to manually compose UNION ALL query by using Queryable.Concat like this:
IQueryable<NWatchRelation> query = null;
foreach (var relation in relationsCollection)
{
var m = relation;
var subQuery = db.NWatchRelations
.Where(r => r.Id == m.Id
&& r.RelatedNodeId == m.RelatedNodeId
&& r.RelationType == m.RelationType);
query = query == null ? subQuery : query.Concat(subQuery);
}
But please note that it's a limited approach and will not work if the relationsCollection is big.

You could create a kind of unique key using the three values:
//To create a unique key (an string, which is a primitive type) combining the three values
var keys=relationsCollection.Select(e=>e.Id+"-"+e.RelatedNodeId+"-"+ ((int)e.RelationType)).Distinct();
var query=db.NWatchRelations.Where(r=>keys.Any(k=>k == (SqlFunctions.StringConvert((double)r.Id)+"-"+
SqlFunctions.StringConvert((double)r.RelatedNodeId )+"-"+
SqlFunctions.StringConvert((double)((int)r.RelationType)) ));
If your NWatchRelations table doesn't have many rows or relationsCollection is a small collection, please, use one of the alternatives that were proposed earlier at your convinience.

Also you can have the directly linked like this
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public NWatchRelation()
{
this.INWatchRelation = new HashSet<INWatchRelation>();
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<INWatchRelation> INWatchRelation { get; set; }
But the entiry relation must be liked like this in order to work properly
Then you could select/list it like this
db.NWatchRelation.INWatchRelation.ToList();

Related

How to select needed records in joining table using LINQ with tables which have many to many relationship?

I have many to many relationship between entities user and group, I also have joining table GroupParticipants.
public class User
{
public string Id {get; set;}
public ICollection<GroupParticipant> Group { get; set;}
}
public class Group
{
public int Id { get; set; }
public ICollection<GroupParticipant> Participants { get; set; }
}
public class GroupParticipant
{
public int GroupId { get; set; }
public string ParticipantId { get; set; }
public User Participant { get; set; }
public Group Group { get; set; }
}
I need to select groups which user specified user did not join. I want to do something like:
string userId = 5;
var groupsAvailableToJoin = await _context.Groups
.Where(group => group.Participants.Id != userId);
Thanks!
A query like:
_context.Groups.Where(g =>
!_context.GroupParticipants.Any(gp => gp.UserId == userId && gp.GroupId == g.I'd
);
Should translate to:
SELECT * FROM Groups g
WHERE NOT EXISTS(SELECT null FROM groupParticipants gp WHERE gp.UserId = 5 AND gp.GroupId = g.Id)
Which should be a reasonably performant way of getting you what you're looking for.. I'm sure that the GroupParticipants columns are indexed..
There are various ways to write this - if you find a two step approach easier to understand, it's effectively the same as:
var joined = _context.GroupParticipants.Where(gp => gp.UserId == 5).Select(gp => gp.GroupId).ToList();
var notJoined = _context.Groups.Where(g => !joined.Contains(g.Id));
This one translates as a NOT IN (list,of,groups,they,are,in) for a similar effect

Simple Linq query that joins two tables and returns a collection

I have two tables LookUpCodes and LookUpValues they are defined as below:
public partial class LookUpCodes
{
public int Id { get; set; }
public int? CodeId { get; set; }
public string Code { get; set; }
public string Description { get; set; }
}
public partial class LookUpValues
{
public int Id { get; set; }
public int CodeId { get; set; }
public string CodeValue { get; set; }
public bool? IsActive { get; set; }
}
Each LookUpCode can have multiple Values associated with it. I want to pass in a code and get associated list of values back.
This is probably a common question as I have seen this everywhere, I am not looking for an answer per se, if someone can just explain how to build the proper query I would be obliged.
Here is what I have done so far:
public IEnumerable<LookUpValues> GetValuesByCode(string cd)
{
var query = from code in _context.LookUpCodes
join values in _context.LookUpValues on code.CodeId equals values.CodeId
where code.Code == cd
select new { LookUpValues = values };
return (IEnumerable<LookUpValues>) query.ToList();
}
You are very close to that you are looking for:
public IEnumerable<LookUpValues> GetValuesByCode(string cd)
{
var query = from code in _context.LookUpCodes
join values in _context.LookUpValues
on code.CodeId equals values.CodeId
where code.Code == cd
select values;
return query;
}
Since you have written the join, I assume that you have understood how it works. However let's revisit it:
from a in listA
join b in listB
on a.commonId equals b.commonId
In the above snippet we join the contents of listA with the contents of listB and we base their join on a commonId property existing in items of both lists. Apparently the pair of a and b that fulfill the join criterion it would form one of the possible many results.
Then the where clause is applied on the results of the join. The joined items that pass thewherefilter is the new result. Even at this point the results is still pairs ofaandb`.
Last you project, using the select keyword each pair of the results to a new object. In your case, for each pair of code and values that passed also the where filter, you return only the values.

Entity framework issue with join two objects

I have following objects:
public class City
{
public int CityId { get; set; }
public string Name { get; set; }
public virtual ICollection<CityTranslation> CityTranslations { get; set; }
}
public class CityTranslation
{
public int CityId { get; set; }
public string LanguageCode { get; set; }
public string Translation { get; set; }
public virtual City City { get; set; }
}
City table contain default language value in Name field other translations are in CityTranslation table.
I am having problem to get value by language from this.
I am trying to execute following:
public virtual IEnumerable<City> GetAllByLanguage(string language)
{
if (language != "en")
{
var data = from c in context.Cities
join t in context.CityTranslations
on c.CityId equals t.CityId
&& t.LanguageCode == language
select c;
}
else
{
return dbSet.ToList();
}
}
But I am gettings compile error.
Operator '&&' cannot be applied to operands of type 'int' and 'bool'
plus some casting errors.
What should I do to get value from translation table?
Others have spotted the issue, but you shouldn't even need the join - EF will handle it:
var data = from t in context.CityTranslations
where t.LanguageCode == language
select t.City;
The second predicate should not be part of the join:
var data = from c in context.Cities
join t in context.CityTranslations
on c.CityId equals t.CityId
where t.LanguageCode == language
For a join on multiple values you need to create composite key using anonymous types so your join statement needs to be something like
on new {t.CityId, t.languageCode} equals new {c.CityId, language}

Limit objects in a collection from being serialized based on User

I have a webapi that returns an object that contains some collections of objects which also contains a collection of objects. Each of these objects has a bool that represents if the object is 'published' See classes below for general idea.
public class A {
public int ID { get; set; }
public List<B> b { get; set;}
}
public class B {
public List<C> c { get; set; }
public List<D> d { get; set; }
public bool published { get; set; }
}
public class C {
public string Title { get; set; }
public bool published { get; set; }
}
public class D {
public string Title { get; set; }
public bool published { get; set; }
}
What is the best way to make it so when I serialize any of these objects that an unpublished child objects are not included if the user doesn't meet the requirements, IE not in a specific role. Can I add data attributes to my model in some way? I have had a look into using a custom IContractResolver but I'm not sure its the best way to handle the nested objects. Should I be handling this in the serializing stage or should I be removing the unpublished objects from the children after I get the object from the database.
As the comments (rightly) pointed out I was going about this in slightly the wrong way.
I solved my problems by having two database requests basic looks something like this.
A a = null;
if(User.IsInRole("Role")){
a = db.A.Find(Id);
} else {
a = (from a in db.A
join b in db.B on a.ID equals b.ID
join c in db.C on b.ID equals c.ID
join d in db.D on b.ID equals d.ID
where a.ID == id && b.Published && c.Published && d.Published
select a);
}
if(a == null)
return NotFound();
return Ok(a);
I was trying to avoid code like this but I'm not sure there is a better way to do it.
Actually I would propose for a certain users to load only needed data as this will increase the performance. The case if you want to load all data (both published and unpublished) is trivial. For the user who may view only published items I would make such query:
A model = context.ACollection.Where(a => a.ID == id).Select(a =>
new A {
ID = a.ID,
b = a.b.Where(i => i.published == true).Select(i =>
new B{
published = true,
c = i.c.Where(c_item => c_item.published == true),
d = i.d.Where(d_item => d_item.published == true)
})
}).Single();
This query should give you good performance.

Linq Query with join on subquery

Basically I'm trying to write a query where it joins on select top 1 from a second table so something like:
SELECT Sum(pinfo.quantity + p.itemcount),
i.owner
FROM invoice i
JOIN purchase_info pinfo
ON pinfo.invoice = i.invid
JOIN (SELECT DISTINCT sku,
productlineid,
itemcount
FROM products WHERE productlineid in (13, 14)) p
ON p.sku = pinfo.item
WHERE i.owner = 22623
GROUP BY i.owner
Here's my pathetic attempt in linq that has somewhat invalid syntax, any ideas would be much appreciated.
(from i in _invoiceRepository.Table
join pi in _purchaseInfoRepository.Table on i.InvoiceId equals pi.InvoiceId
join p in (from p2 in _productRepository.Table where p2.Sku == pi.Item select new { p2.Sku, p2.ItemCount }).Take(1)
on pi.Item equals p.Sku
where i.MemberId == memberId &&
(p.ProductLineId == (int)ProductLines.InkCartridges ||
p.ProductLineId == (int)ProductLines.TonerCartridges)
select pi.Quantity * p.ItemCount)
.DefaultIfEmpty(0)
.Sum();
Here is my first stab at this.
From the sql, it looks like you want to find how many Ink and Toner Cartridges a particular customer has ordered from you ever.
This should give you the same results as the sql (this is depending on the order of the Products table since we are taking the top 1 without some sort of ordering being done:
var count = from i in _invoiceRepository.Table
where i.OwnerId == memberId
select new
{
OwnerId = i.OwnerId,
TotalProductCount = i.Purchases.Sum(pro => pro.Products
.Where(p => p.ProductLineId == (int)ProductLines.InkCartridges ||
p.ProductLineId == (int)ProductLines.TonerCartridges)
.Take(1)
.Sum(p => p.ItemCount * pro.Quantity))
};
Since I did not know the the classes of the three objects (Invoice, PurchaseInfo, and Product) I made a guess at what they are:
Invoice Class: I assume it has a list/collection of PurchaseInfos
public class Invoice
{
public int Id { get; set; }
public int OwnerId { get; set; }
public List<PurchaseInfo> Purchases { get; set; }
}
PurchaseInfos: An invoice has multiple PurchaseInfos, each one links to (ideally) one product but since the SKU is not unique I assome that this has a list/collection of Products in it.
public class PurchaseInfo
{
public int Id { get; set; }
public int Quantity { get; set; }
public int InvoiceId { get; set; }
public Invoice Invoice { get; set; }
public int Item {get;set;}
public List<Product> Products { get; set; }
}
Product Class: I assome that there is an Id field (not shown) or a composite primary key somewhere
public class Product
{
public int Sku { get; set; }
public int ProductLineId { get; set; }
public int ItemCount { get; set; }
public List<PurchaseInfo> PurchaseInfos { get; set; }
}
Hopefully you can take this a get what you need. If this is way off, please update question with the class definitions (you can remove unneeded properities if you want) so a better answer can be produced.

Categories