Linq query causes null reference exception when accessing linked tables - c#

So I have the following linq query for a test blog:
var random = new Random();
var random = context.Blog.OrderBy(x => random.Next())
.Where(x => x.DisplayStatus == "Approved")
.Select(x => new Blog
{
Title = x.Title,
Content = x.Content,
AuthorId = x.AuthorId,
Author = x.Authors.AuthorName //null pointer exception here
}).Take(5).ToList()
The trouble is, it throws a null pointer exception when it hits 'x.Authors.AuthorName'. I cannot figure out why. I know the author is there because it works fine when I use linq expressions (from x in context.Blog ...etc), and it works in LinqPad. I can't use the linq expression, though, because I don't know how to declare 'OrderBy(x => random.Next())' without using a lambda expression.
this is the version that works without the random
var working = (from x in context.Blog
//NO known code to select random
where x.DisplayStatus == "Approved"
select new Blog
{
Title = x.Title,
Content = x.Content,
AuthorId = x.AuthorId,
Author = x.Authors.AuthorName //NO null pointer exception
}).Take(5).ToList()
Blog is a POCO class with no database relations.
Authors is a database entity class with AuthorName being a simple string.
Any idea on what is going on here?

I am not allowed to add a comment, so I ask here. What type is Authors? From the name I guess it could be some type of Collection.
What I often do if one Property might be null is to add a ?:
Author = x.Authors?.AuthorName

You can get random sort using Guid.NewGuid():
var random = context.Blog.OrderBy(x => Guid.NewGuid())
.Where(x => x.DisplayStatus == "Approved")
.Select(x => new Blog
{
Title = x.Title,
Content = x.Content,
AuthorId = x.AuthorId,
Author = x.Authors.AuthorName
}).Take(5).ToList()
Or SqlFunctions.Rand:
var random = context.Blog.OrderBy(x => SqlFunctions.Rand())
.Where(x => x.DisplayStatus == "Approved")
.Select(x => new Blog
{
Title = x.Title,
Content = x.Content,
AuthorId = x.AuthorId,
Author = x.Authors.AuthorName
}).Take(5).ToList()

Change your query to this:
var working = (from x in context.Blog
where x.DisplayStaus == "Approved"
select new Blog
{
Title = x.Title,
Content = x.Content,
AuthorId = x.AuthorId,
Author = x.Authors.AuthorName
})
.AsEnumerable().OrderBy(x => random.Next())
.Take(5).ToList();
Edit:
I wasn't 100% sure what the error is and wanted to wait for the OP to check if my answer actually does what I believe it does.
I assumed that Entity Framework will not translate your query to SQL and therefor lose the ability to automatically load x.Authors, by moving the OrderBy the first "half" of the query, can be translated to SQL while the AsEnumerable().OrderBy() will run in memory.
So AsEnumerable() will force the query to be translated and exceuted in SQL, and the following OrderBy will run in memory.
Edit2:
About how to do it on the SQL Server and not loading the whole query to memory, I am afraid I can't help you with that, a google search brought this
Linq to Entities, random order

Related

The multi-part identifier "t.subjectId" could not be bound. Entity Framework Core

I am facing problem, I write a query in Entity Framework Core using C#, but I am unable to get my final result. It will be great favour if someone help me.
I want to show students result of entry test, if subject result is not posted then it should show 0, means need to add left outer join. I write the below query, but when I execute it returns the error.
var result = _db.palSundayTests
.Where(w => w.testId == testId)
.Select(s => new
{
testId = s.testId,
testDate = s.testDate,
students = _db.palSundayTestStudents
.Where(w => w.testGroupId == s.testGroupId)
.Select(st => new
{
students = st,
results = (from tp in _db.palSubjInfo
join rs in _db.palStudentTestResult.Where(r => r.studentId == st.studentId)
on tp.subjectId equals rs.subjectId
into myres
from myview in myres.DefaultIfEmpty()
select new
{
myview.obtainedMarks
}).ToList()
}).FirstOrDefault(),
subjects = s.sundayTestTopic
});
When I run in POSTMAN, I get this error
The multi-part identifier "t.subjectId" could not be bound
The multi-part identifier "t.obtainedMarks" could not be bound
The multi-part identifier "t.studentId" could not be bound
The multi-part identifier "t.testId" could not be bound
You are mixing implicit joins with explicit joins. That is allowed, but you need to be aware of how to do that properly. may it help https://stackoverflow.com/a/7314936/11143288

The nested query is not supported

I have a query that is similar to the following (my actual query has three sections like this and then Concats them together and applies some additional filters and sorting).
var articles = from p in Repository.Query<Product>()
let article = p.Article
let office = p.TariffCategory.Office
where p.IsDeleted == false
select new
{
OfficeId = office.Id,
Office = office.Name,
Category = p.TariffCategory.Description,
ArticleId = article.Id,
Article = article.Title,
Destinations = p.ProductDestinations.Select(d => new { Id = d.DestinationId, Name = d.Destination.Description }),
GlobalDestinations = p.AllDestinationsInOffice,
article.LastReviewedDate,
article.CreatedDate,
article.CreatedByEmployee
};
Everythings seems right except my assignment to Destinations. That line produces the following error.
The nested query is not supported. Operation1='UnionAll' Operation2='MultiStreamNest'
If I remove that line, everything works as expected. Is there any way to perform a query like this?
I had a bit of a think, and rather than doing a join as I suggested, it may make sense to start the query at ProductDestination. What we're interested in is a row for each product+destination combination, much like you'd see via regular SQL queries. Once we've got that, we can apply grouping to the result so that we're closer to the representation you had
var data = Repository.Query<ProductDestination>()
.Where(pd => !pd.Product.IsDeleted)
.Select(pd =>
new {
Product = pd.Product,
Destination = pd.Destination,
})
.GroupBy(pd => pd.Product)
//I'm not in a position to test if EF will successfully run the below, so .ToList()
//May not be required. However, the bulk of the work is done in the database, anyway.
//.ToList()
.Select(g => new {
OfficeId = g.Key.TariffCategory.Office.Id,
Office = g.Key.TariffCategory.Office.Name,
Category = g.Key.TariffCategory.Description,
ArticleId = g.Key.Article.Id,
Article = g.Key.Article.Title,
Destinations = g.Select(gg => new { Id = gg.Destination.DestinationId, Name = gg.Destination.Description }),
GlobalDestinations = g.Key.AllDestinationsInOffice,
g.Key.Article.LastReviewedDate,
g.Key.Article.CreatedDate,
g.Key.Article.CreatedByEmployee
});
I'm pretty sure the above should work without the ToList(), but I'm not confident to say it 100% will work. However, as noted, the bulk of the work is done in the database, the final projection shouldn't be too intensive, even if it's done in memory. However, should the ToList() be required, we would need to modify the GroupBy to return all fields we select via Key, otherwise we're going to have issues with lazy loading and N+1 queries.

IQueryable where clause

Was difficult for me to find a fitting title for this post. But I have the following:
IArticleRepository articleRepo = unitOfWork.ArticleRepository;
List<Article> articles = new List<Article>(
articleRepo.GetAll()
.Where(a => a.Title == searchTerm)
//.Where(a => a.Categories.Contains(Category.))
.OrderByDescending(a => a.CreatedDate));
So some explanation: An article has , among other things, a Title and a CreateDate, and filtering through those is easy. But an article also has categories associated with it. So an article has an array property of type Category. Type Category has a property called CategoryId of type int.
So in my code where it's commented out, I'm trying to select an article, which has a category associated with it, who's CategoryId is equal to.. say 4.
But I'm finding it quite difficult to express this in my C# syntax. I'm also new to C# so that's not helping either.
You don't need to write two Where clauses; just add another condition to your first Where. The second condition should use Any function to search for the categories you're looking for.
IArticleRepository articleRepo = unitOfWork.ArticleRepository;
List<Article> articles = new List<Article>(
articleRepo.GetAll()
.Where(a => a.Title == searchTerm &&
a.Categories.Any(c => c.CategoryID == 4))
.OrderByDescending(a => a.CreatedDate));
For multiple categories, suppose you have your CategoryIDs in an int[] or List<int> named MyCatIDsList. They you can change the categories clause in the above query to this:
a.Categories.Any(c => MyCatIDsList.Contains(c.CategoryID))
There is an alternative syntax when using LINQ queries, which is more like SQL. The code above is correct, but you may find this version more concise:
int categoryId = 4
IArticleRepository articleRepo = unitOfWork.ArticleRepository;
var articlesQuery = from article in articleRepo.GetAll()
from category in article.Categories
where category.CategoryId == categoryId
where article.Title == searchTerm
orderby article.CreatedDate descending
select article
List<Article> articles = articlesQuery.ToList();
Or its more common to do these all together in one step:
int categoryId = 4
List<Article> articles = (
from article in articleRepo.GetAll()
from category in article.Categories
where category.CategoryId == categoryId
where article.Title == searchTerm
orderby article.CreatedDate descending
select article
).ToList()
You don't need to create a new list and you can use several where expressions in one Where clause. Can you try the following code:
List<Article> articles = articleRepo.GetAll()
.Where(a => a.Title == searchTerm && a.Categories.Contains(Category)).OrderByDescending(a => a.CreatedDate)).ToList();

LINQ to Entities does not recognise custom method

The code below produces the error:
LINQ to Entities does not recognize the method System.String GenerateSubscriptionButton(Int32) method, and this method cannot be translated into a store expression.
How do I create correct custom methods in LINQ to Entities?
var model = _serviceRepository.GetProducts().Select(p => new ProductModel
{
Id = p.Id,
Name = p.Name,
Credits = p.Credits,
Months = p.Months,
Price = p.Price,
PayPalButton = GenerateSubscriptionButton(p.Id)
});
private string GenerateSubscriptionButton(int id)
{
return new PaymentProcessor.PayPalProcessor().CreateSubscriptionButton(id);
}
You can't do that. How should the provider translate your method to SQL?
Remember: LINQ to Entities doesn't actually execute the C# code of your queries. Instead, it interpretes the expressions and translates them to SQL.
In your conrete case, the solution would probably look something like this:
var model = _serviceRepository.GetProducts()
.Select(p => new ProductModel
{
Id = p.Id,
Name = p.Name,
Credits = p.Credits,
Months = p.Months,
Price = p.Price
})
.ToList()
.Select(x =>
{
x.PayPalButton = GenerateSubscriptionButton(x.Id);
return x;
});
The call to ToList executes the query so far against the database and returns the result. From that point forward, the query actually is a LINQ to objects query where the code is not interpreted but executed.
You can't. The problem is, you cannot call GenerateSubscriptionButton from SQL.
You need to retrieve the entities, then once they are in memory, you can call GenerateSubscriptionButton. You can make this happen by adding a call to AsEnumerable before you project your entity onto your model.
var model = _serviceRepository.GetProducts()
.AsEnumerable()
.Select(p => new ProductModel
{
Id = p.Id,
Name = p.Name,
Credits = p.Credits,
Months = p.Months,
Price = p.Price,
PayPalButton = GenerateSubscriptionButton(p.Id)
});

using linq to order tickets by lowest date where ticket is not closed?

I have an object called Ticket with that contains a list of objects called TicketActions. The Ticket object has a field called Date_Closed and the Actions object has a field called Action_Date:
Ticket
Date_Closed
TicketActions
-Action_Date
What I'm trying to do is order a List of tickets (List) based on the latest date of each Action in ascending order where the Ticket does not have a value for Date_Closed. The goal is to load this list into a listview and show tickets in a way that displays tickets in order on the page, placing the ones that have gone the longest without an action at the top. Does that make sense?
Here is what I ended up with so far that isn't working:
protected List<FullTicket> BuildTickets(int ticketsToShow)
{
using (var db = new SupportLogDBDataContext())
{
var result =
(from ticket in db.Support_Tickets
join status in db.Ticket_Statuses on ticket.Status_ID equals status.ID
select new FullTicket
{
TicketID = ticket.ID,
DateOpened = (DateTime)ticket.Date_Opened,
DateClosed = (DateTime)ticket.Date_Closed,
Subject = ticket.Subject,
Status = new KeyPair { Key = status.Status, Value = status.ID },
CreatedBy = new GuidPair { Key = ticket.Reported_By, Value = (Guid)ticket.AD_GUID },
TicketActions =
(from a in db.Ticket_Actions
where a.Ticket_ID == ticket.ID
select a).ToList()
}).Take(ticketsToShow).ToList();
result.OrderBy(i => i.TicketActions.Where(i.DateClosed == null).Max()); //error on this line (invalid arguments)
return result;
}
}
People reply quick here!
Try this:
var result = (from ticket in tickets
where !ticket.DateClosed.HasValue
select ticket).OrderByDescending(t => (from a in t.TicketActions
select a.ActionDate).Max());
From here you can take as many as you need.
David B's analysis is slightly off. The line...
result.OrderBy(i => i.TicketActions.Where(i.DateClosed == null).Max());
... will not compile because the argument to the Where method is not a lambda expression or delegate.
I would suggest this solution (assuming that the relevant property of the TicketAction type is ActionDate):
return result.Where(i => i.DateClosed == null)
.OrderBy(i => i.TicketActions.Max(a => a.ActionDate));
Or, in query comprehension syntax:
return from i in result
where i.DateClosed == null
orderby i.TicketActions.Max(a => a.ActionDate)
select i;
Here is some simple code.
var sorted = tickets.Where(t => t.DateClosed == null)
.OrderBy(t => t.TicketActions.Max(ta => ta.Action_Date.Ticks));
Sorry, I prefer LINQ function syntax, but if you want it in query syntax, it shouldn't be too hard to convert.
result.OrderBy(i => i.TicketActions.Where(i.DateClosed == null).Max());
This line generates an error because TicketActions.Max() is not defined.
You need to project TicketAction into something that can be Max'd. For example:
result.OrderBy(i =>
i.TicketActions
.Where(ta => i.DateClosed == null)
.Select(ta => ta.Id)
.Max()
);
Also note:
OrderBy does not modify its source. OrderBy returns an ordered IEnumerable, which you didn't assign anywhere.
OrderBy's enumerable is deferred, and you want a List result instead, so you should call ToList.
You are accessing Ticket.TicketActions outside of the query. This will cause one database round trip per ticket to load that property.
Here is a modification to your query that avoids the problems mentioned above by ordering and using navigational properties within the query.
from ticket in db.Support_Tickets
where ticket.DateClosed == null
let lastDate = ticket.TicketActions
.Select(ta => ta.ActionDate)
.OrderByDescending(date => date)
.FirstOrDefault()
let ticketStatus = ticket.TicketStatus
order by lastDate
select new FullTicket
{
...
}

Categories

Resources