I have a query with an inner join between two objects (BagDto and ItemDto) where ItemDto also has at least one parent with type of LocationDto. Here are the class definitions:
public class BagDto
{
public int Id { get; set; }
public string Color { get; set; }
public string Type { get; set; }
public DateTime CreatedDate { get; set; }
/* other properties for bag comming from oracle DB */
public IEnumerable<ItemDto> Items { get; set; }
}
public class ItemDto
{
public int Id { get; set; }
public int BagId { get; set; }
public int LocationId { get; set; }
public string Type { get; set; }
public DateTime AddDate { get; set; }
public BagDto Bag { get; set; }
public LocationDto Location { get; set; }
}
public class LocationDto
{
public int Id { get; set; }
public string Name { get; set; }
public double SquareMeters { get; set; }
public string CountyName { get; set; }
public virtual IEnumerable<ItemDto> Items { get; set; }
}
In my query I do not want any kind of Location data to get and only BagDto and ItemDto is really what I would like to query. The mapping of all three objects are okay and I do not want to change those. I also would not like to create inheritance to separate LocationDto out (ie.: ItemDto will not contains LocationDto but ItemWithLocationDto will).
Here is the query:
public IEnumerable<BagDto> GetBagsWithAvailableType()
{
ItemDto itemDtoAlias = null;
Session.QueryOver<BagDto>()
.Where(x.CreatedDate <= DateTime.UtcNow)
.JoinAlias(x => x.Items, () => itemDtoAlias, NHibernate.SqlCommand.JoinType.InnerJoin)
.Where(
() => itemDtoAlias.Type == "A")
.List();
}
Using nHibernate Profiler the following query is generated:
SELECT this_.ID,
this_.COLOR,
this_.TYPE,
this_.CREATEDDATE,
item1_.ID,
item1_.BAG_ID,
item1_.LOCATION_ID,
location2_.ID,
location2_.NAME,
location2_.SQUAREMETERS,
location2_.COUNTYNAME
FROM BAG this_
inner join ITEM item1_
on this_.ID = item1_.BAG_ID
left outer join LOCATION location2_
on item1_.LOCATION_ID = location2_.ID
WHERE (this_.CREATEDDATE <= TIMESTAMP '2021-04-07 16:23:54')
and (item1_.TYPE = 'A')
Anybody knows a way how to get rid of the left outer join related to the LOCATION? I would like to specify this in the query but google all the day for it still no solution. Thanks for the help!
P. S.: writting all this query in plain SQL and call it from the code is a bad practice. Our codeguides are not allowing that.
Without mappings it's not clear why Location is added to the query in a first place. Assuming it's added due to fetch="join" mapping for Location property you can skip it in a query with SelectMode.Skip (available since NHibernate 5.2):
ItemDto itemDtoAlias = null;
Session.QueryOver<BagDto>()
.Where(x.CreatedDate <= DateTime.UtcNow)
.JoinAlias(x => x.Items, () => itemDtoAlias, NHibernate.SqlCommand.JoinType.InnerJoin)
.Fetch(SelectMode.Skip, () => itemDtoAlias.Location)
...
See description of all SelectMode options here
Related
ASP CORE MVC application.
I have 2 tables:
public class StudentModel
{
public Guid Id { get; set; }
public string ChineseName { get; set; }
public string EnglishName { get; set; }
public DateTime BirthdayDate { get; set; }
public List<StudentParent> StudentParent { get; set; }
public List<StudentCourse> StudentCourse { get; set; }
public StudentModel()
{
StudentParent = new List<StudentParent>();
StudentCourse = new List<StudentCourse>();
}
}
public class ParentModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public string PhoneNumber { get; set; }
public List<StudentParent> StudentParent { get; set; }
public ParentModel()
{
StudentParent = new List<StudentParent>();
}
}
With relation many-to-many.
public class StudentParent
{
public Guid StudentId { get; set; }
public StudentModel Student { get; set; }
public Guid ParentId { get; set; }
public ParentModel Parent { get; set; }
}
The question is: How to get Id from ParentModel table if I know student Id?
In Microsoft SQL Server Management Studio I can execute this command and get what I want:
SELECT ParentModels.Id
FROM StudentModels
LEFT OUTER JOIN StudentParent
ON StudentModels.Id = StudentParent.StudentId
LEFT OUTER JOIN ParentModels
ON StudentParent.ParentId = ParentModels.Id
WHERE StudentModels.Id = 'A1F38C12-AE65-464C-C489-08D814F4CDDC'
How to write this code in Visual Studio with LINQ?
I tried this instead:
Guid parentID = context.StudentModels.FromSqlRaw("SELECT ParentModels.Id FROM StudentModels LEFT OUTER JOIN StudentParent ON StudentModels.Id = StudentParent.StudentId LEFT OUTER JOIN ParentModels ON StudentParent.ParentId = ParentModels.Id WHERE StudentModels.Id = '{0}'", guid).FirstOrDefault();
But it doesn't work.
With this being many-to-many, there could be more than 1 ParentModel.Id for each student.
This being said, you could find them like this:
IEnumerable<Guid> parentIds = context.ParentModels
.Where(p => p.StudentParent
.Any(sp => sp.Student.Id == new Guid("A1F38C12-AE65-464C-C489-08D814F4CDDC")))
.Select(p => p.Id);
In such complex query where you need to make a lot of joins most developers prefer query LINQ syntax. It seems to be easier to read and understand.
IEnumerable<Guid> parentIds =
(from sm in context.StudentModels
join sp in context.StudentParent on sm.Id equals sp.StudentId
join pm in context.ParantModels on sp.ParentId equals pm.Id
where sm.Id == new Guid("A1F38C12-AE65-464C-C489-08D814F4CDDC")
select pm.Id).ToList();
More info about query and method syntax you can find in Miscrosoft Docs
I can't seem to find out the query for a complicated model I'm using. The model looks like this:
public class Stash
{
public int StashId { get; set; }
public int LocationId { get; set; }
public Location Location { get; set; }
public int ProductId { get; set; }
public Product Product { get; set; }
public int Amount { get; set; }
}
A product can have several locations and now I want to get all the locations that are connected to a product.
var stashes = new List<Stash>();
int productID = [your id];
var result = stashes.Where(stash => stash.ProductId == productID).Select(stash => stash.Location);
If you need the result to be a List add .ToList() at the end.
you can use
Include to load reference of other entity
var result=context.entity.Include(c=>c.product)
.where(c=>c.product.productId==productid)
I'm novice in LINQ, so I need an initial help how to simplify writing of LINQ queries. Here my scenario: I have two tables C_Systemtype with 1:M relationship to CT_Systemtype, using database first approach.
Class C_System:
{
public string SystemtypeId { get; set; }
public bool Is_productive { get; set; }
public bool Is_systemown { get; set; }
public bool Is_active { get; set; }
public byte[] Icon { get; set; }
public virtual ICollection<CT_Systemtype> CT_Systemtype { get; set; }
public virtual ICollection<C_System> C_System { get; set; }
}
Class CT_Systemtype:
{
public string SystemtypeId { get; set; }
public string LanguageId { get; set; }
public string Title { get; set; }
public string Descript { get; set; }
public virtual C_Systemtype C_Systemtype { get; set; }
public virtual S_Language S_Language { get; set; }
}
I like to select all C_Systemtype but with CT_Systemtype restricted to a given LanguageId.
I believe the following LINQ query is working (p_langId is my parameter):
using (var db = new PaltrConnect())
{ var query = from s in db.C_Systemtype
join t in db.CT_Systemtype on s.SystemtypeId equals t.SystemtypeId
where t.LanguageId == p_langId
select new { s.Is_productive,
s.Is_systemown,
s.Is_active,
s.Icon,
s.CT_Systemtype }
}
The result is of type anonymous. My intention is something like C_Systemtype.Include(t => t.CT_Systemtype) but with additional restriction on CT_Systemtype.
How can I rewrite this query in such a way that I don't have to give each property in the select part and to finally map individual properties?
using (var db = new PaltrConnect())
{
var query = from s in db.C_Systemtype
join t in db.CT_Systemtype on s.SystemtypeId equals t.SystemtypeId
where t.LanguageId == p_langId
select s ;/*s is your C_Systemtype*/
}
Because you are joining two tables together you can't just return a single type. To prevent having to map each property in the select you can use something like AutoMapper.
I have the following class:
public class PingtreeTier
{
public BuyerType BuyerType { get; set; }
public int ID { get; set; }
public int MaxRequests { get; set; }
public IEnumerable<PingtreeNode> Nodes { get; set; }
public int Seq { get; set; }
public int Timeout { get; set; }
public bool Weighted { get; set; }
}
As you can see PingtreeTier contains an IEnumerable<PingtreeNode> class. This PingtreeNode class has a property named Status. Using Linq, I need to select only the Tiers/Nodes where PingtreeNode Status = 'Active'.
Anyone help as I'm struggling with the syntax for this.
How about using .Any or .All here:
var results = tiers.Where(t => t.Nodes.Any(n => n.Status == "Active"));
This will select any PingtreeTiers that contain at least one PingTreeNode with Status equal to "Active".
If you wanted to select only PingtreeTiers whose PingTreeNodes are all active, you could use the .All extension method instead:
var results = tiers.Where(t => t.Nodes.All(n => n.Status == "Active"));
I have List<Candidate> Candidates, List<Seat> Seats
The Model defined as shown below
public class Seat
{
public string CollegeId { get; set; }
public bool isFilled { get; set; }
public string SeatType { get; set; }
public string RollNumber { get; set; }
}
public class Candidate
{
public string RollNumber { get; set; }
public bool isAllotted { get; set; }
public string Quota { get; set; }
public int CandidateRank { get; set; }
public List<OptionPriority> SeatingPriorities { get; set; }
}
public class OptionPriority
{
public string CollegeId { get; set; }
public int PriorityRank { get; set; }
}
I need to filter List<Seat> from List<Seat> Seats where Seats.CollegeId IN List of CollegeID in SeatingPriorities.
// same as EXISTS
Seats.Where(s => SeatingPriorities.Any(sp => sp.CollegeId == s.CollegeId))
Also you can join seatings with seatings priorities:
// same as INNER JOIN
var prioritySeats = from s in Seats
join sp in SeatingPriorities
on s.CollegeId equals sp.CollegeId
select s;
NOTE: Both of queries above will not generate IN clause if you will execute them in LINQ to SQL or LINQ to Entities. IN is generated when you use Contains method of primitive types list:
var ids = SeatingPriorities.Select(sp => sp.CollegeId).ToList();
// same as IN
var prioritySeats = Seats.Where(s => ids.Contains(s.CollegeId));
var results = source.Where(x => SeatingPriorities.Contains(x.CollegeId)).ToList();
You can use Enumerable.Contains to find out the matches like you do with in
var result = lstSeats.Where(s=>SeatingPriorities.Contains(s.CollegeId));
Use Contains to achieve IN functionality in LINQ
You could use Any:
seats.Where(s => SeatingPriorities.Any(i => i.Id == s.CollegeId))
Since Contains only accepts an instance to compare (along with a possible IEqualityComparer<T>), which won't work if OptionPriority isn't an comparable to CollegeId (i.e. a string).