I have two sequence of objects; namely Messages for Message object and Newsflashes for Newsflash object.
Both are derived through Entity Framework ADO.NET Entity Model. The diagram of the model is as below:
As you can see, the Newsflash inherits from Message. However, the generated Index in the controller somehow error and I need to do manual query to pass the right sequence to the View.
The Newsflash table only has one column, which is Id and at the same time a foreign key to the Message Id.
I want to query like this in LINQ SELECT * FROM MESSAGE WHERE ID IN (SELECT ID FROM NEWSFLASH)
So far I have tried something like this:
var message = Messages.Where(x => x.Id == Newsflash.Any(y=>y.Id))
But I am getting error that cannot convert int to bool. What did I do wrong? How do nested select especially from a list is handled in LINQ? How can I access element in the sequence; in this case Newsflash so that I could get the Id individually?
Any returns a bool, not a list of values. If you want a list of Newsflash ID's, you would use Newsflash.Select(x => x.Id)
To get your list of messages that have a newsflash, you should use:
var messages = (from m in Messages
join n in Newsflash on m.Id equals n.Id
select m).ToList();
This will join messages to your newsflash based on the Id for each, and then select the Message object that matches.
alternative lamba syntax:
var messages = Messages.Join(Newsflash, x => x.Id, y => y.Id, (x, y) => new { Message = x }).ToList();
If newsflash is just a list of Ids Try this.
var message = Messages.Where(x => Newsflash.Contains(x.Id));
or
var message = Messages.Where(x => Newsflash.Select(y => y).Contains(x.Id));
simple example.
var listOfInts = new List<int>{1,2,3,4,5,6,7,8,9,10};
var listOfInts2 = new List<int>{1,2,3,4,5};
listOfInts.Where(x => listOfInts2.Contains(x));
Related
I am struggling with converting SQL to NHibernate HQL.
SQL Query
SELECT Posts.id, Count(Comments.id) FROM Posts LEFT JOIN Comments ON Posts.id=Comments.fk_post GROUP BY Posts.id
LINQ
Session.Query<Post>().Fetch(x => x.Comments).GroupBy(x => x.id, x => x.Comments)
.Select(x => new { PostId = x.Key, CommentCount = x.Single().Count }).ToList();
This is still failing with exception:
Parameter 'inputInfo' has type 'Remotion.Linq.Clauses.StreamedData.StreamedSingleValueInfo' when type 'Remotion.Linq.Clauses.StreamedData.StreamedSequenceInfo' was expected.
What is wrong with my query?
So you have tables of Posts and Comments. There is a one-to-many relation between Posts and Comments: every Post has zero or more Comments, every Comment belongs to exactly one Post, namely the Post that the foreign key Comments.fk_post refers to.
You want to fetche the Id of every Post, together with the number of Comments for this Post.
Whenever you need to select "items with their zero or more sub-items", like Schools with their Students, Customers with their Orders, or in your case Posts with their Comments, consider to use one of the overloads of Queryable.GroupJoin.
You can also see that a GroupJoin is the most obvious solution, if you see a SQL Left Outer Join followed by a GroupBy.
Whenever you see a SQL left outer join followed by a GroupBy, it is almost certain that you need a GroupJoin.
If you want something else than juse "items with their sub-items", use the overload that has a parameter resultSelector.
I don't know nHibernate, I assume that Session, Query, Fetch are used to get the IQueryables. As this is not part of the question, I leave it up to you to get the IQueryables:
IQueryable<Post> posts = ...
IQueryable<Comment> comments = ...
// GroupJoin Posts with Comments
var postIdsWithCommentsCount = posts.GroupJoin(comments,
post => post.Id, // from every Post take the primary key
comment => comment.fk_post, // from every Comment take the foreign key to Post
// parameter resultSelector: from every Post, with all its zero or more Comments,
// make one new
(post, commentsOfThisPost) => new
{
Id = post.Id,
Count = commentsOfThisPost.Count(),
});
Try this query:
var query =
from p in Session.Query<Post>()
from c in p.Comments.DefaultIfEmpty()
group c by p.Id into g
select new
{
PostId = g.Key,
CommentCount = g.Sum(x => (int?)c.Id == null ? 0 : 1)
}
var result = query.ToList();;
I want to change code below to be sql translateable because now i get exception.
Basicallly i want list of customers from certain localisation and there could be more than one customer with the same CustomerNumber so i want to take the one that was most recently added.
In other words - distinct list of customers from localisation where "distinct algorithm" works by taking the most recently added customer if there is conflict.
The code below works only if it is client side. I could move Group By and Select after ToListAsync but i want to avoid taking unnecessary data from database (there is include which includes list that is pretty big for every customer).
var someData = await DbContext.Set<Customer>()
.Where(o => o.Metadata.Localisation == localisation)
.Include(nameof(Customer.SomeLongList))
.GroupBy(x => x.CustomerNumber)
.Select(gr => gr.OrderByDescending(x => x.Metadata.DateAdded).FirstOrDefault())
.ToListAsync();
Short answer:
No way. GroupBy has limitation: after grouping only Key and Aggregation result can be selected. And you are trying to select SomeLongList and full entity Customer.
Best answer:
It can be done by the SQL and ROW_NUMBER Window function but without SomeLongList
Workaround:
It is because it is not effective
var groupingQuery =
from c in DbContext.Set<Customer>()
group c by new { c.CustomerNumber } into g
select new
{
g.Key.CustomerNumber,
DateAdded = g.Max(x => x.DateAdded)
};
var query =
from c in DbContext.Set<Customer>().Include(x => x.SomeLongList)
join g in groupingQuery on new { c.CustomerNumber, c.DateAdded } equals
new { g.CustomerNumber, g.DateAdded }
select c;
var result = await query.ToListAsync();
Suppose query is like so:
var query = from x in db.Customers
join y in db.Orders
on x.Id Equals y.CustomerId
where ...
select new {Customer = x, Order = y}
and I want the results grouped by customer:
var resultsByCustomer = query.GroupBy(x => x.Customer.Id)
This works fine, except that now the grouping Key is the customer id, but I want it to be the whole Customer object, because I want to access other properties of that customer (but not properties I want to group by).
I could do this instead:
var resultsByCustomer = query.GroupBy(x => x.Customer)
but this doesn't work, because it seems to group by object equality on the Customer class, not the customer id.
Is it possible to group by Customer Id, but have the whole customer object returned as the Key?
Go ahead and group them by the customer ID (with the ID as they Key), and then extract the customer from the first item in the group.
var resultsByCustomer =
from g in query.GroupBy(x => x.Customer.Id)
let customer = g.Select(x => x.Customer).FirstOrDefault()
select ...;
I am trying to get a list of a database table called oracleTimeCards whose employee id equals to the employeeID in employees list. Here is what I wrote:
LandornetSQLEntities db = new LandornetSQLEntities();
List<OracleEmployee> employees = db.OracleEmployees.Where(e => e.Office.Contains(officeName) && e.IsActive == true).Distinct().ToList();
var oracleTimeCards = db.OracleTimecards.Where(c => employees.Any(e => c.PersonID == e.PersonID)).ToList();
Anyone has any idea?
I'm going to assume you're using Entity Framework here. You can't embed calls to arbitrary LINQ extension methods inside your predicate, since EF might not know how to translate these to SQL.
Assuming you want to find all the timecards for the employees you found in your first query, you have two options. The simplest is to have a navigation property on your Employee class, named let's say TimeCards, that points to a collection of time card records for the given employee. Here's how that would work:
var oracleTimeCards = employees
.SelectMany(e => e.TimeCards)
.ToList();
If you don't want to do this for whatever reason, you can create an array of employee IDs by evaluating your first query, and use this to filter the second:
var empIDs = employees
.Select(e => e.PersonID)
.ToArray();
var oracleTimeCards = db.OracleTimecards
.Where(tc => empIDs.Contains(tc.PersonID))
.ToList();
Suppose I have three tables:
Person(pid, ...)
PersonAddress(pid, aid,...)
Address(aid, ...)
Then I want to get the person address like sql:
select a.* from address a join PersonAddress pa on a.addressID=pa.addressID
where pa.personID = myPersonID
Use Entity Framework to create Entity model, then want to write a linq equivalent as above sql.
I tried it in following way:
var addresses = this.GetAddress();
var personaddresses = this.GetPersonAddress();
var query = from ad in addresses
from pa in personaddresses
where ((ad.AddressID == pa.AddressID)&&(pa.PersonID==person.personID))
select ad;
but I got error. Or I try to start from:
var result = this.Context.Address;
var result = result.Join .... //how to write linq in this way?
How to write the linq?
This is untested but if you have all of your relationships setup and you create the model (I have used Model as the name for this) from this you should be able to use the following:
var values = this.Model.Address.Select(a => a.PersonAddress.Where(pa => pa.Id == myPersonID));
You almost never use join in LINQ to Entities.
Try:
var q = from p in Context.People
where p.PersonId == personId
from a in p.Addresses // presumes p.Addresses is 1..*
select a;
Assuming you have three entities: Person, PersonAddress and Address, here is a query that should meet your needs (this example assumes an Entity Framework context named context):
var values = context.PersonAddress.Where(pa => pa.Person.PersonId == myPersonId).Select(pa => pa.Address);
However, if the PersonAddress table exists as a pure many-to-many relationship table (i.e. contains only keys), you'd be better off setting up your Entity Framework model in such a way that the intermediate table isn't necessary, which would leave you with the much simpler:
var values = context.Person.Where(p => p.PersonId == myPersonId).Addresses;
Based on the additional feedback
Because you need to include the country table, you should originate your query from the Address table. In that case:
var values = context.Address.Where(a => a.PersonAddress.Where(pa => pa.Product.Id == myProductId).Count() > 0)
To include the Country table in the result:
var values = context.Address.Include("Country").Where(a => a.PersonAddress.Where(pa => pa.Product.Id == myProductId).Count() > 0)