Constraint on Linq to Entities Include - asp.net

I have a message table that self joins to itself where Message.ID == Message.Parent. So I have 1 Message with several ChildMessages. This is set up nicely with a Navigation property.
The code is currently:
var message = from m in soe.Messages.Include("ChildMessages")
where m.ID == id
&& m.IsActive
select m;
return message.FirstOrDefault();
What I really want is to only return 1 Message (that is equal to a particular ID and isActive) with all of its Active ChildMessages and I want to do it in one call to the database.

I have a solution for 4.0 (I'm not sure if it will work on 3.5, i hadn't check).
First, set your entities model Lazy Loading Enabled property to false. Note that as a result you will lose the references auto-loading.
Next, try this code:
var message = soe.Messages
.Where(m => (m.ID == id || m.ParentId == id) && m.IsActive)
.AsEnumerable()
.FirstOrDefault(e => e.Id == id);
The EF will resolve the parent-child references, and will not load other child references unless an explicit request will be made (like Include or Load). Notice that you must iterate over the Where results using AsEnumerable or otherwise they won't load.

I've concluded this is not possible in Entity framework.
A work around would be to return an anonymous type that satisfies the constraint. E.g.
var message = from m in soe.Messages
where m.ID == id
&& m.IsActive
select
new
{
Message = m,
ChildMessages = m.ChildMessages.Where(c => c.IsActive)
};
return message.FirstOrDefault()

Related

Entity Framework Core - This may indicate either a bug or a limitation in EF Core

I want to load from a user the newest private chat messages.
My code to load the newest messages
public async Task<List<UserChatMessage>> GetUserPrivateChatMessagesAsync(string userId, string userChatPartnerId, int limit, int skip, CancellationToken cancellationToken) {
cancellationToken.ThrowIfCancellationRequested();
if (userId == null) throw new ArgumentNullException(nameof(userId));
if (userChatPartnerId == null) throw new ArgumentNullException(nameof(userChatPartnerId));
return await this.Context.Messages
.OrderBy(d => d.CreatedDate)
.AsNoTracking()
.Include(p => p.UserChatPartner)
.Where(u => u.UserId == userId && u.UserChatPartnerId == userChatPartnerId || u.UserChatPartnerId == userId && u.UserId == userChatPartnerId)
.TakeLast(limit)
.Skip(skip)
.ToListAsync(cancellationToken);
}
The following code throw this error:
System.InvalidOperationException: Processing of the LINQ expression 'DbSet<UserChatMessage>
.OrderBy(d => d.CreatedDate)
.Include(p => p.UserChatPartner)
.Where(u => u.UserId == __userId_0 && u.UserChatPartnerId == __userChatPartnerId_1 || u.UserChatPartnerId == __userId_0 && u.UserId == __userChatPartnerId_1)
.TakeLast(__p_2)' by 'NavigationExpandingExpressionVisitor' failed. This may indicate either a bug or a limitation in EF Core. See https://go.microsoft.com/fwlink/?linkid=2101433 for more detailed information.
I changed .Take to .TakeLast and now I get the described error.
Database structure of UserChatMessage
I also tried to change ToListAsync to AsEnumerable without success.
I hope somebody understand the error and can help me.
Thanks in advance.
Regard Timo
The error means you are hitting current EF Core bug or limitation.
I changed .Take to .TakeLast and now I get the described error.
So you know what caused the issue. In general avoid LINQ methods having Last in name (like Last, LastOrDefault, TakeLast) - these have no direct equivalent in SQL world, hence have bigger chance to hit bugs/limitations (or just not supported) by query translators.
Instead, invert the ordering and use the corresponding First method.
Applying it to your case means replacing
.OrderBy(d => d.CreatedDate)
with
.OrderByDescending(d => d.CreatedDate)
and
.TakeLast(limit)
with
.Take(limit)

Linq Query Distinct Function do not work with DropdownList

I want to assign Linq Query result to dropdownlist which contain a
Distinct function
My Code:-
var area = de.City_Area_View
.Select(m => new { m.Area_Id, m.Area_Name})
.Distinct()
.ToList();
drpfilter.DataTextField = "Area_Name";
drpfilter.DataValueField = "Area_Id";
drpfilter.DataSource = area;
drpfilter.DataBind();
Problem :- When I write this code then I get Below Error
Error:- The method 'Distinct' is not supported.
I get System.NotSupportedException.
I want to assign a Distinct name of area to the DropDownList
So please help me for this problem.
If your set is small enough (so you don't mind fetching all the values from the database), the simplest thing would be to force the distinct part to be performed locally:
var area = de.City_Area_View
.Select(m => new { m.Area_Id, m.Area_Name})
.AsEnumerable()
.Distinct()
.ToList();
AsEnumerable simply "changes" the expression type to IEnumerable<T> instead of IQueryable<T>, so that the compiler calls Enumerable.Distinct instead of Queryable.Distinct - and Enumerable.Distict will definitely work.

Entity framework select from two tables with different table relationship primary/foreign key scenarios

I have two sample tables:
SCENARIO 1
TABLE 1 - INGREDIENT
ingredientId(PK, int, not null)
userId(FK, int, not null)
timestamp(datetime, not null)
TABLE 2 - INGREDIENT ADDITIONAL INFORMATION
ingredientAdditionalInformationId(PK, int, not null)
ingredientId(FK, int, not null)
isApproved(bit, not null)
unitsConverted(bit, not null)
SELECT SENTENCE IN CODE BEHIND:
public IQueriable GetIngredientData(int ingredientId)
{
using (var context = new MyEntities())
{
var result = context.Ingredient
.Where(i => i.ingredientId == ingredientId)
.Select(i => new
{
i.ingredientId,
i.userId
i.IngredientAdditionalInformation.FirstOrDefault(iai => iai.ingredientId = i.ingredientId).isApproved
i.IngredientAdditionalInformation.FirstOrDefault(iai => iai.ingredientId = i.ingredientId).unitsConverted
});
return result.ToList().AsQueriable();
}
}
or select with join (I know that you can join with method syntax but I can write join with query method faster)
public IQueriable GetIngredientData(int ingredientId)
{
using (var context = new MyEntities())
{
var result = from i in context.Ingredient
join iai in context.IngredientAdditionalInformation on i.ingredientId equals iai.ingredientId
where i.ingredientId == 1
select new
{
i.ingredientId,
i.userId
iai.isApproved
iai.unitsConverted
};
return result.ToList().AsQueriable();
}
}
Which one is better/faster with join or FirstOrDefault() or I should write database table different like in example 2 below:
SCENARIO 2
TABLE 1 - INGREDIENT
ingredientId(PK, int, not null)
userId(FK, int, not null)
timestamp(datetime, not null)
TABLE 2 - INGREDIENT
ingredientId(PK, FK, int, not null) //WITHOUT PRIMARY (ingredientAdditionalInformationId) AUTO INCREMENT KEY)
isApproved(bit, not null)
unitsConverted(bit, not null)
Because I know that every ingredient have only one additional info...
SELECT SENTENCE IN CODE
using (var context = new MyEntities())
{
var result = context.Ingredient
.Where(i => i.ingredientId = 1)
.Select(i => new
{
i.ingredientId,
i.userId
i.IngredientAdditionalInformation.isApproved
i.IngredientAdditionalInformation.unitsConverted
});
}
I would like to know which table design is better (SCENARIO1 or SCENARIO2) for optimized select, if I really need auto increment key in ingredientAdditionalInformation if I know that there will be only one entry for every Ingredient and if this is the right way to use entity framework?
If you're maintaining a one-to-one relationship between the two tables, then your second design is better, because it will also ensure referential integrity in your database.
You can then make the property a single navigation property in your entity framework model, and simplify your EF query as follows. If you have lazy loading of navigational properties enabled in your model, you may be able to get away without using the include if you're
var result = from i in context.Ingredient.Include("IngredientAdditionalInformation") select i;
And then access the properties as follows:
i.IngredientAdditionalInformation.isApproved
However, do you really need an additional table? With only three properties on each I'd just combine them into one table and then you have all the properties available to you immediately.
Scenario 2 is better since you are saying there is a one-to-one relationship between the two tables.
Another option you should explore is using Table Per Type Inheritance. You will not need to specify eager loading using Include nor joins in this case.
Assuming your table1 = IngredientBase and table2 = Ingredients and in your context you have set up
public IQueryable<Ingredient> Ingredients {
get { return IngredientBases.OfType<Ingredient>(); } }
then you'll just need
using (var context = new MyEntities())
{
var result = context.Ingredients.Where(i => i.ingredientId = 1);
}
SQL wise, the 2nd select of scenario1 and scenario2 will produce nearly equivalent plans. But performance wise, scenario2 will be better, not to mention being the right design for a 1-1 relationship tables.

Linq Query with .Any condition returns nullreference exception

I have the following function that returns results from the database based on LINQ Expressions:
IQueryable<TEntity> FindAll<TEntity>(Expression<Func<TEntity, bool>> expression)
When I try pulling data from the function while using the .Any function from a list I get a null reference exception.
However when I pull the data without that specific condition and use the same .Any function in a for each loop everything works correctly.
Here is the call trying to use the .Any function which does not work:
var ppcReports = repository.FindAll<PPCReport>(
x => x.ClientId == clientId &&
(campaigns.Any(c=> c.Id == x.CampaignId))
).ToList();
And the way it does work properly:
var ppcReports = repository.FindAll<PPCReport>(
x => x.ClientId == clientId).ToList();
foreach (var item in ppcReports)
{
if (campaigns.Any(c => c.Id == item.CampaignId))
{
// do something
}
}
I was wondering why was this happening, am I doing something wrong is it just not possible to filter the results before the query finished?
By calling .ToList() before filtering the results it does work, so I suppose I cannot do such an operation on an IQueryable<T> implementation?
var ppcReports = repository.
FindAll<PPCReport>(x => x.ClientId == clientId).
ToList().
Where(w => campaigns.Any(c => c.Id == w.CampaignId)).
ToList();
Like those who commented, I'm surprised that you got a NullReferenceException rather than a complaint about not being able to compile that statement to SQL. However, the following code should let you do this in 1 query (and will do all filtering in SQL):
var campaignIds = (campaigns ?? Enumerable.Empty<Campaign>())
.Select(c => c.Id);
var ppcReports = repository
.FindAll<PPCReport>(pr => pr.ClientId == clientId
&& campaignIds.Contains(pr.CampaignId))
.ToList();
This should work in both EF and Linq-to-SQL.
Queryable.Any() returns an ArgumentNullException when the source is null, as documented here: https://msdn.microsoft.com/it-it/library/bb343630(v=vs.110).aspx

.net mvc3 entity deep compare on Where

My Code for a controller:
public ViewResult Index(int? ProjectID)
{
var user = HttpContext.User;
User profile = db.Users.Where(d => d.Email == user.Identity.Name).Single();
var contracts = db.Contracts.Include(c => c.Project);
if (!user.IsInRole("Admin"))
{
contracts = contracts.Where(p => p.Project.Client == profile.Client );
}
if (ProjectID != null)
{
contracts = contracts.Where(u => u.ID == ProjectID);
}
return View(contracts.ToList());
}
This is suppose to pull up all of the contracts whose parent project has the same client fk as the user currently signed in unless they are an admin. This isn't working.
I am getting the following error when the non-admin's look at the page:
Unable to create a constant value of type
'MembershipExt.Models.Client'. Only primitive types ('such as Int32,
String, and Guid') are supported in this context.
Do I need to use a second include or something?
What is the data type for p.Project.Client? I'm guessing that it is a complex type (it has properties off of it). Perhaps what you want is something like this...
contracts = contracts.Where(p => p.Project.Client.ClientID == profile.Client.ClientID );
Obviously I don't know what the object looks like, but perhaps this helps.
contracts = contracts.Where(p => p.Project.Client == profile.Client );
i am guessing the problem is in the above line the right hand side of lambda needs to be of simple type that is int32, string etc but you are having a complex type i.e. p.Project.Client
This looks wrong
db.Users.Where(d => d.Email == user.Identity.Name)
looks like you are comparing email with name

Resources