Entity Query in Query, is it possible - c#

I'm not really sure how to ask this question. I need to create an object, I believe it is called a projection, that has the result of one query, plus from that need to query another table and get that object into the projection.
This is a C# WCF Service for a Website we are building with HTML5, JS, and PhoneGap.
EDIT: Getting an error on the ToList (see code) - "The method or operation is not implemented."
EDIT3: changed the Entity Object company_deployed_files to IQueryable AND removed the FirstOrDefault caused a new/different exception Message = "The 'Distinct' operation cannot be applied to the collection ResultType of the specified argument.\r\nParameter name: argument"
Background: This is a kind of messed up Entity Model as it was developed for Postgresql, and I don't have access to any tools to update the model except by hand. Plus some design issues with the database does not allow for great model even if we did. In other words my two tables don't have key constrains(in the entity model) to perform a join in the entity model - unless someone shows me how - that honestly might be the best solution - but would need some help with that.
But getting the below code to work would be a great solution.
public List<FileIDResult> GetAllFileIDFromDeviceAndGroup ( int deviceID, int groupID)
{
List<FileIDResult> returnList = null;
using (var db = new PgContext())
{
IQueryable<FileIDResult> query = null;
if (deviceID > 0)
{
var queryForID =
from b in db.device_files
where b.device_id == deviceID
select new FileIDResult
{
file_id = b.file_id,
file_description = b.file_description,
company_deployed_files = (from o in db.company_deployed_files
where o.file_id == b.file_id
select o).FirstOrDefault(),
IsDeviceFile = true
};
if (query == null)
{
query = queryForID;
}
else
{
// query should always be null here
}
}
if (groupID > 0)
{
var queryForfileID =
from b in db.group_files
where b.group_id == groupID
select new FileIDResult
{
file_id = b.file_id,
file_description = b.file_description,
company_deployed_files = (from o in db.company_deployed_files
where o.file_id == b.file_id
select o).FirstOrDefault(),
IsDeviceFile = false
};
if (query != null)
{
// query may or may not be null here
query = query.Union(queryForfileID);
}
else
{
// query may or may not be null here
query = queryForfileID;
}
}
//This query.ToList(); is failing - "The method or operation is not implemented."
returnList = query.ToList ();
}
return returnList;
}
Edit 2
The ToList is throwing an exception.
I'm 98% sure it is the lines: company_deployed_files = (from o in db.company_deployed_files where o.file_id == b.file_id select o).FirstOrDefault()
End Edit 2

Related

need to use Count() for null and non null values in Linq

Here is my code:
var value = query.Select(
a => new CHART_MODEL
{
TEXT = "xyz",
COUNT1 = (a.TOPLAM_FIYAT != null).ToString().Count(),
COUNT2 = (a.TOPLAM_FIYAT == null) ? ToString().Count() : 0
}
).ToList();
Here is the error:
System.NotSupportedException: 'LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression.'
and CHART_MODEL
public class CHART_MODEL
{
public int COUNT1 { get; set; }
public int COUNT2 { get; set; }
public string TEXT { get; set; }
}
My question is: I have to calculate null and non null values with Count(). but it doesn't allow me to write. I don't know how to rearrange linq query code in correct way with Count().
This is an error you get because EF doesn't know how to execute the .ToString() method in sql.
Methods like that which EF doesn't have correlated methods in plain sql, will throw an error like the one you received.
What you can do is use an Immediate Execution.methods like: ToList or ToArray will force the execution of the query, hence loading the data into memory and when the data is loaded the rest of the operators are performed using Linq to objects on that the data that was brought in memory.
So, you can load the data using toList() and after that use the rest of the code and methods like toString() won't throw an error.
query.toList().Select(..)
You can read more about Deferred Execution vs Immediate Execution here:
https://samirbehara.com/2016/01/04/deferred-execution-vs-immediate-execution-in-linq/
The simplest way would be something like
var value = new CHART_MODEL
{
TEXT = "xyz",
COUNT1 = query.Where(a=>a.TOPLAM_FIYAT != null).Count(),
COUNT2 = query.Where(a=>a.TOPLAM_FIYAT == null).Count()
};
Note, this will issue two select statements.
If you really want to avoid the two select statements, you have to introduce a dummy grouping such as
var value = (from r in query
group r by 1 into results
select new CHART_MODEL
{
TEXT = "xyz",
COUNT1 = results.Where(a => a.TOPLAM_FIYAT != null).Count(),
COUNT2 = results.Where(a => a.TOPLAM_FIYAT == null).Count()
}).Single();
Just complementing on #sgmoore's answer, you can place you filter condition inside the .Count method itself:
var value = new CHART_MODEL
{
TEXT = "xyz",
COUNT1 = query.Count(a => a.TOPLAM_FIYAT != null),
COUNT2 = query.Count(a => a.TOPLAM_FIYAT == null)
};
There is the new is null and is not null syntax introduced in C#9. It reads really nice, but it is still not recommended by Microsoft to use in Linq-to-SQL.

ORA-00904 Occasional Invalid identifier with linq query

I have two different linq expressions that are referencing the same column in the database. One works just fine, but the other throws an invalid identifier exception (ORA-00904).
Most of the questions I've found feature naked sql queries with some syntax errors. Others have to do with the entity model, but seeing as how it doesn't run into the issue in one query, I'm not convinced the issue is with the model.
The one that works:
public List<DateTime> GetAvailableDates()
{
var retData = new List<DateTime>();
using (var context = new CASTDbContext())
{
var result = context.SomeDataEntity.Select(x => x.CAPTURE_DATE).Distinct().ToList();
if(result != null && result.Count > 0)
{
retData = result;
}
}
return retData;
}
The one that doesn't work:
public List<SomeDataModel> GetSomeDataByDate(DateTime date)
{
var retData = new List<SomeDataModel>();
using (var context = new SomeDbContext())
{
var result = context.SomeDataEntity
.Where( y => DbFunctions.TruncateTime(y.CAPTURE_DATE) == date.Date).ToList(); // the line that's throwing the exception
if (result != null && result.Count > 0)
{
foreach (var item in result)
{
retData.Add(mapper.Map<SomeDataModel>(item));
}
}
}
return retData;
}
The issue ended up being a different part of the model, but just some info on Oracle perils:
The first query worked fine because it was only referencing one specific field that had a matching column in the database (oracle doesn't care about the rest of the model in that instance for some reason).
The second query didn't work because it was trying to pull every column from the table, and there was one field missing from the model.

Troubleshooting LINQ Query Performance in ASP.NET MVC

I'm having trouble with a LINQ query after joining a new table to it. Actually, it returns the data I'm expecting and it runs fast in testing. However, it seems that, as more users connect to the database, the query begins to timeout. For example, everything was working fine for the first 30 or 45 minutes in Production, but then at about 8:20 AM, it started to timeout. Again, I assume this is due to increased usage of the database on the whole.
Here is a little background on the ASP.NET MVC (5) application, in case that helps.
A user submits a referral to our clinic
The referral contains one or more orders
If the person information supplied does not match an existing person, I do several things, including inserting records in an "orders" table (one record for each order selected on the referral).
If the person information supplied does match an existing person in our system, then I "hold" the referral in a queue until it is manually resolved by either matching it to an existing person or by overriding it and creating a new person in the system. At this time, any orders selected in the referral are created in the table.
So, the two main tables to think about in this scenario are the "referral" (named "Referrals" in my code) and "order" (named "ReferralPPAs" in my code) tables. Until now, I have not needed to link the query in question from the Referrals table to the ReferralPPAs table (linking the query to the ReferralPPAs table seems to be what is slowing the query down once database/application usage increases).
Also, in case this helps, the referrals are entered by external users, while the orders I created from the referral are worked in a separate application with internal staff as the users, though it's all in the same database. The ReferralPPAs table is probably being used pretty heavily most of the day.
The query looks like this:
IQueryable<ReferralListViewModel> referrals = (from r in _context.Referrals
join cu in _context.ClinicUsers on r.ClinicId equals cu.ClinicId
/* Here is the seemingly problematic join */
from ppa in _context.ReferralPPAs
.Where(p => p.ref_id == r.seq_no.ToString())
.DefaultIfEmpty()
/* End of seemingly problematic join */
join ec in _context.EnrolledClinics on r.ClinicId equals ec.ClinicId
join pm in _context.ProviderMasters on ec.ClinicId equals pm.ClinicId
join ml in _context.MasterLists on pm.HealthSystemGuid equals ml.Id
join au in _context.Users on r.ApplicationUserId equals au.Id
where cu.UserId == userId
select new ReferralListViewModel()
{
ClinicName = pm.Description,
ClinicId = r.ClinicId,
ReferralId = r.seq_no,
EnteredBy = (au.FirstName ?? string.Empty) + " " + (au.LastName ?? string.Empty),
PatientName = (r.LastName ?? string.Empty) + ", " + (r.FirstName ?? string.Empty),
DateEntered = r.create_timestamp,
Status = ppa != null ? ppa.Status : string.Empty
});
So, without the join I make reference to above, I experience no problems and it runs quite fast. Adding the join also appears to be fast, again, until a certain number of users are on the system (at least that's my assumption).
A couple of other things I've tried to help improve performance and prevent the problem. I set the UseDatabaseNullSemantics to True, which seems to make a big difference in the overall performace.
_context.Configuration.UseDatabaseNullSemantics = true;
I also wondered if the problem was an issue of locking on the table in question, so I tried wrapping the query in a transaction to do a ReadUncommitted.
using (var transaction = _context.Database.BeginTransaction(System.Data.IsolationLevel.ReadUncommitted))
{
//query
}
Again, while this improves the overall performance a little bit, it didn't seem to ultimately resolve the problem.
If anyone has any thoughts, ideas, or suggestions on how to tackle this, I would greatly appreciate it.
Based on the additional information from the comments, looks like the Guid to String conversion in the join condition
p.ref_id == r.seq_no.ToString()
translated to
t1.ref_id = LOWER(CAST(t2.seq_no AS nvarchar(max))))
makes the query not sargable, while the implicit SqlServer conversion
t1.ref_id = t2.seq_no
works just fine.
So the question is how to remove that cast. There is no option for that and also query expression tree does not allow removing it. It would be nice if the SqlServer provider sql generator was doing that optimization, but it doesn't and there is no easy way to hook into it.
As a workaround I can offer the following solution. It uses a custom IDbCommandTreeInterceptor and DbExpressionVisitor to modify the DbCommandTree of the query.
Here is the interception code:
using System;
using System.Data.Entity.Core.Common.CommandTrees;
using System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure.Interception;
using System.Linq.Expressions;
using System.Reflection;
namespace EFHacks
{
public class MyDbCommandTreeInterceptor : IDbCommandTreeInterceptor
{
public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext)
{
if (interceptionContext.OriginalResult.DataSpace != DataSpace.SSpace) return;
var queryCommand = interceptionContext.Result as DbQueryCommandTree;
if (queryCommand != null)
{
var newQuery = queryCommand.Query.Accept(new GuidToStringComparisonRewriter());
if (newQuery != queryCommand.Query)
{
interceptionContext.Result = new DbQueryCommandTree(
queryCommand.MetadataWorkspace,
queryCommand.DataSpace,
newQuery);
}
}
}
}
class GuidToStringComparisonRewriter : DefaultExpressionVisitor
{
public override DbExpression Visit(DbComparisonExpression expression)
{
if (IsString(expression.Left.ResultType) && IsString(expression.Right.ResultType))
{
var left = expression.Left;
var right = expression.Right;
if (RemoveCast(ref right) || RemoveCast(ref left))
return CreateComparison(expression.ExpressionKind, left, right);
}
return base.Visit(expression);
}
static bool IsGuid(TypeUsage type)
{
var pt = type.EdmType as PrimitiveType;
return pt != null && pt.PrimitiveTypeKind == PrimitiveTypeKind.Guid;
}
static bool IsString(TypeUsage type)
{
var pt = type.EdmType as PrimitiveType;
return pt != null && pt.PrimitiveTypeKind == PrimitiveTypeKind.String;
}
static bool RemoveCast(ref DbExpression expr)
{
var funcExpr = expr as DbFunctionExpression;
if (funcExpr != null &&
funcExpr.Function.BuiltInTypeKind == BuiltInTypeKind.EdmFunction &&
funcExpr.Function.FullName == "Edm.ToLower" &&
funcExpr.Arguments.Count == 1)
{
var castExpr = funcExpr.Arguments[0] as DbCastExpression;
if (castExpr != null && IsGuid(castExpr.Argument.ResultType))
{
expr = castExpr.Argument;
return true;
}
}
return false;
}
static readonly Func<DbExpressionKind, DbExpression, DbExpression, DbComparisonExpression> CreateComparison = BuildCreateComparisonFunc();
static Func<DbExpressionKind, DbExpression, DbExpression, DbComparisonExpression> BuildCreateComparisonFunc()
{
var kind = Expression.Parameter(typeof(DbExpressionKind), "kind");
var booleanResultType = Expression.Field(null, typeof(DbExpressionBuilder), "_booleanType");
var left = Expression.Parameter(typeof(DbExpression), "left");
var right = Expression.Parameter(typeof(DbExpression), "right");
var result = Expression.New(
typeof(DbComparisonExpression).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null,
new[] { kind.Type, booleanResultType.Type, left.Type, right.Type }, null),
kind, booleanResultType, left, right);
var expr = Expression.Lambda<Func<DbExpressionKind, DbExpression, DbExpression, DbComparisonExpression>>(
result, kind, left, right);
return expr.Compile();
}
}
}
and DbConfiguration to install it:
class MyDbConfiguration : DbConfiguration
{
public MyDbConfiguration()
{
AddInterceptor(new EFHacks.MyDbCommandTreeInterceptor());
}
}
Tested and working in EF6.1.3 and EF6.2 with SqlServer database.
But use it with care.
First, it works only for SqlServer.
Second, it's hackish because I had to use internal field and internal class constructor in order to skip the check for equal types of the comparison operation operands. So some future EF6 update might break it.

When does Entity framework Linq query Hits Sql Databse?

i'm new in entity framework.Below is my code,
So in my code i have created object of my db context and then i have a query 'queryForAuthentication' and in that i have used two tables 'conDb.SystemMasters' and joined with conDb.SystemAdminMasters , so will hit twice or how does it manage . i want to know when does entity framework will hit in to database ?
QuizzrEntities conDb = new QuizzrEntities();
List<OnLoginData> lstOnLogoonData = new List<OnLoginData>();
string userpassWordHash = string.Empty;
var queryForAuthentication =from systemObj in conDb.SystemMasters
where systemObj.StaffPin == dminLoginInput.StaffPin
join admin in conDb.SystemAdminMasters on systemObj.SystemId equals admin.SystemID
select new
{
admin.PasswordSalt,
admin.PasswordHash,
systemObj.StaffPin,
admin.UserName,
admin.SystemID
};
if (queryForAuthentication.Count() > 0)
{
CheckStaffPin = true;
var GetUserUsingUsernamePasword = queryForAuthentication.Where(u => u.UserName.ToLower() == AdminLoginInput.UserName.ToLower());
if (GetUserUsingUsernamePasword.ToList().Count == 1)
{
checkuserName = true;
string DBPasswordSalt = queryForAuthentication.ToList()[0].PasswordSalt,
DBPasswordHash = queryForAuthentication.ToList()[0].PasswordHash,
StaffPin = queryForAuthentication.ToList()[0].StaffPin;
userpassWordHash = Common.GetPasswordHash(AdminLoginInput.Password, DBPasswordSalt);
if ((DBPasswordHash == userpassWordHash) && (AdminLoginInput.StaffPin.ToLower() == StaffPin.ToLower()))
{
checkPassword = true;
CheckStaffPin = true;
}
else if (DBPasswordHash == userpassWordHash)
{
checkPassword = true;
}
else if (AdminLoginInput.StaffPin.ToLower() == StaffPin.ToLower())
{
CheckStaffPin = true;
}
}
}
So in my code i have created object of my db context and then i have a query 'queryForAuthentication' and in that i have used two tables 'conDb.SystemMasters' and joined with conDb.SystemAdminMasters , so will hit twice or how does it manage .
i want to know when does entity framework will hit in to database ?
It's hits the database whenever you fire a query. And query will be fired whenever you perform ToList, First, FirstOrDefault etc. operation. Till then it only builds the query.
try Code
QuizzrEntities conDb = new QuizzrEntities();
List<OnLoginData> lstOnLogoonData = new List<OnLoginData>();
string userpassWordHash = string.Empty;
var queryForAuthentication =(from systemObj in conDb.SystemMasters
where systemObj.StaffPin == dminLoginInput.StaffPin
join admin in conDb.SystemAdminMasters on systemObj.SystemId equals admin.SystemID
select new
{
PasswordSalt= admin.PasswordSalt,
PasswordHash= admin.PasswordHash,
StaffPin= systemObj.StaffPin,
UserName= admin.UserName,
SystemID = admin.SystemID
}).FirstOrDefault();
If(queryForAuthentication !=null)
{
-----------------
-----------------
*****Your Code*******
}
In entity framework also work with sql query based. If you are disconnected using .ToList() then only the record taken from local otherwise it's works as DBQuery. if you check the result view in debug view it's Execute the Query and Return the data.
If you are processing the data is discontinued from the base it's executed finally where you want the result.
You processing data locally then you can disconnect the connection between linq and sql using call .ToList(). it's Processing only one time the Object weight is high more than query.
var queryForAuthentication =from systemObj in conDb.SystemMasters
where systemObj.StaffPin == dminLoginInput.StaffPin
join admin in conDb.SystemAdminMasters on systemObj.SystemId equals admin.SystemID
select new
{
admin.PasswordSalt,
admin.PasswordHash,
systemObj.StaffPin,
admin.UserName,
admin.SystemID
}.ToList() ; // It will fetch the data
//Check from inmemory collection
if (queryForAuthentication.Count > 0)
//As you already have the data in memory this filter applied against inmemory collection not against database.
var GetUserUsingUsernamePasword = queryForAuthentication
.Where(u =>u.UserName.ToLower() == AdminLoginInput.UserName.ToLower());

Entity Framework Core: The multi-part identifier could not be bound

We are using a .NET Core application (meanwhile running on localhost) and a SQL database running on SQL Server where we receive data from. In the following method which is called inside the controller we are using Entity Framework Core to query the data in SQL database:
public ActionResult InspectionDetails(string inspectionId, string lang)
{
// load all checks for an inspection
var inspectionChecks = (from ic in _context.InspectedChecks.Where(x => x.InspectionId.ToString() == inspectionId)
from cp in _context.CheckPoints.Where(x => x.CategoryId == ic.CategoryId && x.CheckId == ic.CheckId)
from tChecks in _context.Translations.Where(x => x.TranslationKey == cp.Title && x.Lang.ToLower() == lang.ToLower()).DefaultIfEmpty()
from tChecksFallBack in _context.Translations.Where(x => x.TranslationKey == cp.Title && x.Lang.ToLower() == "en").DefaultIfEmpty()
select new InspectedChecks2
{
Id = ic.Id,
CheckDate = ic.CheckDate,
Category = ic.CategoryId.ToString(),
Check = tChecks.TextValue ?? tChecksFallBack.TextValue,
CheckingUser = ic.CheckingUser,
Result = ic.Result,
Passed = ic.Passed,
InspectionId = ic.InspectionId,
ToleranceValue = ic.ToleranceValue,
Images = null,
Unit = ic.Unit
}).ToList();
return Content(JsonConvert.SerializeObject(inspectionChecks));
}
if the InspectionDetails method is called the following exception is thrown:
System.Data.SqlClient.SqlException: "The multi-part identifier "x0.title" could not be bound.
The multi-part identifier "x0.title" could not be bound."
Further infromation: "cp" contains the disired values, so everything works fine till there.
What we tried so far:
Run the same query directly on the SQL database --> Success
Run two querys in the InspectionDetails method (first query: set a var to the value of "cp", second query: like above, except using
with the new var insted of "cp") --> NO Success
Anyone who had a similar issue or knows how to fix it?
Thanks in Advance

Categories