In a left-join linq query such as:
var sales = await (
from order in conxtext.orders
join customerJoin in conxtext.customer on order.customerid equals customerJoin.customerid into
customerGroup
from customer in customerGroup.DefaultIfEmpty()
select new
{
OrderNumber = order.ordernumber,
CustomerName = customer == null ? "" : cumstomer.customername
}).ToListAsync().ConfigureAwait(false);
how can we check for null on a keyless table? I'm getting this error message using the above linq query:
System.InvalidOperationException: Cannot translate the '==' on an
expression of entity type 'customer' because it is a keyless entity.
Consider using entity properties instead.
With EF Core you do not have to check entity for null before accessing property. Instead of that correct result:
var sales = await (
from order in conxtext.orders
join customerJoin in conxtext.customer on order.customerid equals customerJoin.customerid into
customerGroup
from customer in customerGroup.DefaultIfEmpty()
select new
{
OrderNumber = order.ordernumber,
CustomerName = cumstomer.customername ?? ""
}).ToListAsync().ConfigureAwait(false);
Related
I am converting the C# linq query to SQL, LINQ never return the values.
But the same query I wrote in SQL returns some values. Can anyone help me find out what the issue is for my SQL query?
LINQ query:
var query = from l in _DbContext.Licenses
join lp in _DbContext.LicenseParts on l.PartNumber equals lp.PartNumber
join lpc in _DbContext.LicensePartConfigurations on lp.Id equals lpc.LicensePartId
join p in _DbContext.Products on lp.ProductId equals p.Id
join lsn in _DbContext.LicenseSerialNumbers on l.Id equals lsn.LicenseId
join lact in _DbContext.LicenseActivations on new { a = lsn.Id, b = lp.ProductId } equals new { a = lact.LicenseSerialNumberId, b = lact.ProductId }
where lact.AccountId == AccountId && JsonExtensions.JsonValue(lact.ActivationInfoJSON, "$.SubscriptionKey") !=
" " && (JsonExtensions.JsonValue(lact.ActivationInfoJSON, "$.IsConverted") == null || JsonExtensions.JsonValue(lact.ActivationInfoJSON, "$.IsConverted") == "0" || JsonExtensions.JsonValue(lact.ActivationInfoJSON, "$.IsConverted") == "false") && p.Name == "ClearPass Legacy"
select new SubscriptionKeys { SubscriptionKey = JsonExtensions.JsonValue(lact.ActivationInfoJSON, "$.SubscriptionKey"), CustomerMail = JsonExtensions.JsonValue(lact.ActivationInfoJSON, "$.CustomerMail"), CustomerName = JsonExtensions.JsonValue(lact.ActivationInfoJSON, "$.CustomerName") };
response.PageSize = pageSize;
response.PageNumber = pageNumber;
response.Model = await query.Distinct().ToListAsync();
response.ItemsCount = response.Model.Count();
SQL query:
SELECT
l.AccountId,CustomerMail,
JSON_VALUE(ActivationInfo, '$.SubscriptionKey')
FROM
Licenses l
JOIN
LicenseParts lp ON l.PartNumber = lp.PartNumber
JOIN
LicensePartConfigurations lpc ON lp.Id = lpc.LicensePartId
JOIN
Products p ON lp.ProductId = p.Id
JOIN
LicenseSerialNumbers lsn ON l.Id = lsn.LicenseId
JOIN
LicenseActivations lact ON lsn.Id = lact.LicenseSerialNumberId
AND lp.ProductId = lact.ProductId
WHERE
lact.AccountId = 'QWNjb3VudDoxNTMwNDAzMi00MWM2LTExZTktOWYzMy1kMzQxZjE5OWZlYjM='
AND JSON_VALUE(lact.ActivationInfo, '$.SubscriptionKey') != ' '
AND (JSON_VALUE(lact.ActivationInfo, '$.IsConverted') = NULL OR
JSON_VALUE(lact.ActivationInfo, '$.IsConverted') = 0 OR
JSON_VALUE(lact.ActivationInfo, '$.IsConverted') = 'false')
AND p.Name = 'ClearPass Legacy'
To start from a valid point, execute the code where it fires this linq query and use sql profiler to catch it up. That is a good way to find the exact equivalent sql statement it finally produces and executes. You need to set up a trace to sql profiler prior to execute the linq. Get the statement and then you can compare with the sql you already have.
this sql:
(JSON_VALUE(lact.ActivationInfo, '$.IsConverted') = NULL
is not equal to:
(JsonExtensions.JsonValue(lact.ActivationInfoJSON, "$.IsConverted") == null
as in first case you compare to database NULL value using '=' and this requires ansi_nulls off to work properly and it is not a good practice.
ORMs like EF Core are meant to Map Objects to Relational constructs. Instead of trying to write SQL in C# through LINQ, you should try to Map the attributes you need to entity properties.
In this case the SubscriptionKey and IsConverted fields should appear in the table itself, either as proper fields or computed columns. If that's not possible, you could use computed columns to map the SubscriptionKey and IsConverted attributes to entity properties so you can use them in queries.
In your LicenseActivation class add these properties:
public bool? IsConverted {get;set;}
public string? SubscriptionKey {get;set;}
public string? CustomerEmail {get;set;}
In your DbContext, you can specify computed columns with HasComputedColumnSql:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<LicenseActivation>()
.Property(b => b.SubscriptionKey)
.HasComputedColumnSql("JSON_VALUE(ActivationInfo, '$.SubscriptionKey')");
modelBuilder.Entity< LicenseActivations >()
.Property(b => b.IsConverted)
.HasComputedColumnSql("JSON_VALUE(ActivationInfo, '$.IsConverted')");
modelBuilder.Entity< LicenseActivations >()
.Property(b => b.CustomerEmail)
.HasComputedColumnSql("JSON_VALUE(ActivationInfo, '$.CustomerEmail')");
}
This will allow you to use the properties in LINQ queries.
You shouldn't have to use all those JOINs either. It's the ORM's job to generate JOINs from the relations between objects. If you add proper relations between the entities the query could be simplified to :
var binValue='QWNjb3VudDoxNTMwNDAzMi00MWM2LTExZTktOWYzMy1kMzQxZjE5OWZlYjM=';
var query=_dbContext.LicenseActivations
.Where(lact=>
lact.AccountId == binValue
&& (lact.IsConverted==null || !lact.IsConverted))
.Select(lact=>new {
lact.AccountId,
lact.SubscriptionKey,
lact.CustomerEmail});
or, if the AccountId fields don't hold the same data :
.Select(lact=>new {
AccountId =lact.LicensePart.License.AccountId,
lact.SubscriptionKey,
lact.CustomerEmail
});
EF Core will generate the appropriate SQL and JOIN clauses to get from LicenseActivation to License based on the relations between the entities
I am attempting to do a left outer join in Linq to Sql in order to include all roles for a given user:
from au
in Db.AspNetApplicationUsers
where au.ApplicationId == applicationId
join u in Db.AspNetUsers
on au.UserId equals u.Id
join r in Db.AspNetUserRoles
on au.UserId equals r.UserId
into ur
from userRole in ur.DefaultIfEmpty()
select new UserSummary
{
Active = au.Active,
Email = u.NormalizedEmail,
Name = u.FullName,
Id = u.Id,
LastLogin = u.LastLogin,
LastPasswordChange = u.PasswordLastChanged,
LockedOut = u.LockoutEnabled,
Roles = (from x in ur where userRole.RoleId != null select userRole.RoleId).ToArray()
}
but receive the following error:
System.InvalidOperationException: 'When called from 'VisitLambda', rewriting a node of type 'System.Linq.Expressions.ParameterExpression' must return a non-null value of the same type. Alternatively, override 'VisitLambda' and change it to not visit children of this type.'
I don't use outer joins in EF often, so I'm sure I'm just messing up the syntax.
Is it possible to return an array to a property in Linq to SQL?
If I add the where userRole.RoleId != null condition after from userRole in ur.DefaultIfEmpty() the code doesn't fail there anymore, but does during serialization. I've disabled lazy loading so that's interesting.
As per CamiloTerevinto's suggestion, the following worked just fine:
from au
in Db.AspNetApplicationUsers
where au.ApplicationId == applicationId
select new UserSummary
{
Active = au.Active,
Email = au.User.NormalizedEmail,
Name = au.User.FullName,
Id = au.User.Id,
LastLogin = au.User.LastLogin,
LastPasswordChange = au.User.PasswordLastChanged,
LockedOut = au.User.LockoutEnabled,
Roles = au.User.AspNetUserRoles.Where(r => r.RoleId != null).Select(r => r.RoleId).ToArray()
}
I have the following code. I'm getting error:
"The cast to value type 'Int32' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type."
when CreditHistory table has no records.
var creditsSum = (from u in context.User
join ch in context.CreditHistory on u.ID equals ch.UserID
where u.ID == userID
select ch.Amount).Sum();
How can I modify the query to accept null values?
A linq-to-sql query isn't executed as code, but rather translated into SQL. Sometimes this is a "leaky abstraction" that yields unexpected behaviour.
One such case is null handling, where there can be unexpected nulls in different places. ...DefaultIfEmpty(0).Sum(0) can help in this (quite simple) case, where there might be no elements and sql's SUM returns null whereas c# expect 0.
A more general approach is to use ?? which will be translated to COALESCE whenever there is a risk that the generated SQL returns an unexpected null:
var creditsSum = (from u in context.User
join ch in context.CreditHistory on u.ID equals ch.UserID
where u.ID == userID
select (int?)ch.Amount).Sum() ?? 0;
This first casts to int? to tell the C# compiler that this expression can indeed return null, even though Sum() returns an int. Then we use the normal ?? operator to handle the null case.
Based on this answer, I wrote a blog post with details for both LINQ to SQL and LINQ to Entities.
To allow a nullable Amount field, just use the null coalescing operator to convert nulls to 0.
var creditsSum = (from u in context.User
join ch in context.CreditHistory on u.ID equals ch.UserID
where u.ID == userID
select ch.Amount ?? 0).Sum();
Had this error message when I was trying to select from a view.
The problem was the view recently had gained some new null rows (in SubscriberId column), and it had not been updated in EDMX (EF database first).
The column had to be Nullable type for it to work.
var dealer = Context.Dealers.Where(x => x.dealerCode == dealerCode).FirstOrDefault();
Before view refresh:
public int SubscriberId { get; set; }
After view refresh:
public Nullable<int> SubscriberId { get; set; }
Deleting and adding the view back in EDMX worked.
Hope it helps someone.
I have used this code and it responds correctly, only the output value is nullable.
var packesCount = await botContext.Sales.Where(s => s.CustomerId == cust.CustomerId && s.Validated)
.SumAsync(s => (int?)s.PackesCount);
if(packesCount != null)
{
// your code
}
else
{
// your code
}
You are using aggregate function which not getting the items to perform action , you must verify linq query is giving some result as below:
var maxOrderLevel =sdv.Any()? sdv.Max(s => s.nOrderLevel):0
I see that this question is already answered. But if you want it to be split into two statements, following may be considered.
var credits = from u in context.User
join ch in context.CreditHistory
on u.ID equals ch.UserID
where u.ID == userID
select ch;
var creditSum= credits.Sum(x => (int?)x.Amount) ?? 0;
Got this error in Entity Framework 6 with this code at runtime:
var fileEventsSum = db.ImportInformations.Sum(x => x.FileEvents)
Update from LeandroSoares:
Use this for single execution:
var fileEventsSum = db.ImportInformations.Sum(x => (int?)x.FileEvents) ?? 0
Original:
Changed to this and then it worked:
var fileEventsSum = db.ImportInformations.Any() ? db.ImportInformations.Sum(x => x.FileEvents) : 0;
I was also facing the same problem and solved through making column as nullable using "?" operator.
Sequnce = db.mstquestionbanks.Where(x => x.IsDeleted == false && x.OrignalFormID == OriginalFormIDint).Select(x=><b>(int?)x.Sequence</b>).Max().ToString();
Sometimes null is returned.
Using EF 4.5, I want to convert a zero-count sub-query (relatedDrivers) to null in the following statement:
var query = from car in context.tblCar
let relatedDrivers = (from driver in context.tblDriver
where driver.CarId == car.CarId
select driver)
select new
{
CarId = car.CarId,
Drivers = relatedDrivers.Count() == 0 ? null : relatedDrivers
};
But I get the 'NotSupportedException' stating that it is impossible create a null constant value of type 'System.Linq.IQueryable`1' !
I wonder why this is so hard for Entity Framework to translate this query to T-SQL. Examining a sub-query and returning NULL if the result count is zero doesn't seem to be that much complicated.
Any solution and explanation is highly appreciated.
The solution is IQueriable.DefaultIfEmpty(). So the query will be changed to:
var query = from car in context.tblCar
let relatedDrivers = (from driver in context.tblDriver
where driver.CarId == car.CarId
select driver).DefaultIfEmpty()
select new
{
CarId = car.CarId,
Drivers = relatedDrivers
};
In my Linq, I am trying to make an inner join to a nullable field. Employee and Department have a relation, Department may have an EmployeeID or may have a null. So what would be my join, if i want only the records that satisifed the inner join (no result for null EmployeeIDs):
var result = from emp in employees
join dept in departments
on new { Source = emp.EmployeeID }
equals new { Source = dept.EmployeeID };
I am getting an exception:
The type of one of the expressions in the join clause is incorrect.
Type Inference failed in a call to 'join'.
Thanks
To compare Int? and Int, append .Value to the nullable property:
var result = from emp in employees
join dept in departments
on new { Source = emp.EmployeeID }
equals new { Source = dept.EmployeeID.Value };
What if you reverse your join and put a little where in there?
var result = from department in departments
where department.EmployeeID != null
join employee in employees
on department.EmployeeID.Value equals employee.EmployeeID
select new { employee, department };
Found a useful answer from another link at https://social.msdn.microsoft.com/Forums/en-US/bf98ec7a-cb80-4901-8eb2-3aa6636a4fde/linq-join-error-the-type-of-one-of-the-expressions-in-the-join-clause-is-incorrect-type-inference?forum=linqprojectgeneral
To join multi-valued keys you need to construct an anonymous typ on both sides of the 'equals' that is the same type. The anonymous type initializer expression infers both type and name of members from the expression you supply. In your case the names of the members are different so the types end up being different so C# cannot figure out the common type between the two.
on new { VC.Make, VC.Model } equals new { MD.MakeID, MD.RefNum }
should be
on new { VC.Make, CV.Model } equals new { Make = MD.MakeID, Model = MD.RefNum }
Using the name = value syntax in the initializer you can specify the name the compiler uses when creating the type. If all members types & names are the same then the anonymous types are the same type.
Check the type on emp.EmployeeID and dept.EmployeeID. You might be missing a cast if they are different.
something like:
on new { Source = emp.EmployeeID }
equals new { Source = **(int)**dept.EmployeeID };
Looks like emp.EmployeeID is of type int and dept.EmployeeID is of type nullable<int>.
I had the same issue, where my charge_codes.CompanyId was nullable but my order_items.CompanyId was NOT nullable.
So I had to get my charge codes into their own ananomous type and make it not be nullable.
var chargeCodes = from s in db.Charge_Codes
where s.CompanyID != null
select new { CompanyID = (int)s.CompanyID,
Charge_CodeID = s.Charge_CodeID,
Revenue_Code_Id = (int)s.Revenue_CodeID, };
//now my chargeCodes contains an anonymous with a non nullable CompanyID and
//a non nullable Revenue_CodeID
//use chargeCodes here
var query = from oi in db.Order_Items
join cc in chargeCodes on
new {oi.CompanyID, oi.Charge_CodeID} equals new {cc.CompanyID, cc.Charge_CodeID}
In my scenario I had this error on a join using multiple columns. The property names were different and one of them was also nullable. All I did was creating a name to those properties and adding the ".Value" on the nullable value so the LINQ Join could correctly associate those properties.
var query = from y in Context.Table1
join y in Context.Table2 on new { Field1 = x.Field1.Value, Field2 = x.Field2 }
equals new { Field1 = y.Field1DiffName, Field2 = y.Field2 }
I hope it helps whoever is facing this issue.