Entity Framework 4.1 Raw SQL - sql

I am developing an ASP.Net MVC 3 application using Entity Framework 4.1. For a particular complex query that I need to execute I have decided to write a raw SQL query and pass it to the built in dbSet.SqlQuery method.
I have a Service method like below where I assign the SQL query to a string variable called query. As the query is passed two parameters, I have parameterized these to prevent SQL Injection.
public IList<User> GetAvailableLocums(int shiftID, int shiftDateID)
{
var query ="Select .... where t1 = #p0 and t2 = #p1";
ObjectParameter _shiftID = new ObjectParameter("p0", shiftID);
ObjectParameter _shiftDateID = new ObjectParameter("p1", shiftDateID);
object[] parameters = new object[] { _shiftID, _shiftDateID };
return _UoW.User.GetWithRawSql(query, parameters).ToList();
}
I then pass the query and the parameters to a method in my repository which executes the query for me.
public IEnumerable<TEntity> GetWithRawSql(string query, params object[] parameters)
{
return dbSet.SqlQuery(query, parameters).ToList();
}
I know the query is correct as I have tested it in SQL Server Management Studio, however, I currently get the following error when I try to run this code
No mapping exists from object type System.Data.Objects.ObjectParameter
to a known managed provider native type
Does anyone have any suggestions as to how I can fix this?
Thanks for your help.

Folks
The problem was that I was using ObjectParameter to create my Parameters. I instead changed this to SqlParameter and it worked fine. See below.
Change from this
ObjectParameter _shiftID = new ObjectParameter("p0", shiftID);
To this
SqlParameter _shiftID = new SqlParameter("p0", shiftID);
And it worked. Hope this helps someone else.

From a quick Google search, it looks like your close. I think you are missing setting the return type for your SQL query:
return dbSet.SqlQuery<TEntity>(query, parameters).ToList();
This just tells Entity Framework how to map it.

Related

EF Core, FromSqlRaw with USE HINT

I'm confused !
I have an ASP.Net Core 3 WebApi application, and this works fine:
var results = _context.Users.ToList();
However, if I try to add a "HINT" to the SQL...
var results = _context.Users.FromSqlRaw("SELECT * FROM t_user OPTION(USE HINT('ENABLE_PARALLEL_PLAN_PREFERENCE'))").ToList();
... then it throws this exception...
Incorrect syntax near the keyword 'OPTION'.
Database 'HINT' does not exist. Make sure that the name is entered correctly.
Why would this SQL run successfully in SQL Server Management Studio, but get misunderstood by the WebApi ?
Update # 1
Damn. When you tell EF Core to use Raw SQL, it actually puts that SQL into a sub-clause, and this is why I'm seeing the error.
So, this is the (perfectly valid) SQL which I'm trying to run:
SELECT * FROM t_user
OPTION(USE HINT('ENABLE_PARALLEL_PLAN_PREFERENCE'))
..but, looking at SQL Server Profiler, EF Core is actually trying to this SQL, and this isn't valid...
SELECT [u].[user_id], [u].[user_name]
FROM (
SELECT * FROM t_user
OPTION(USE HINT('ENABLE_PARALLEL_PLAN_PREFERENCE'))
) AS [u]
Damn...
So how do we use Hints from EF Core ?
Update # 2
Because I'm using EF Core, I followed the instructions in this Microsoft article to add a DbCommandInterceptor to my query.
In their example, it intercepts the SQL and appends " OPTION (ROBUST PLAN)" to the string. It shows that it's trying to run this SQL:
SELECT [u].[user_id], [u].[user_name]
FROM t_user
OPTION (ROBUST PLAN)
If I take the Microsoft example, and change it to use my HINT, then I still get the same error of it saying "Database 'HINT' does not exist."
private static void ManipulateCommand(DbCommand command)
{
if (command.CommandText.StartsWith("-- Use hint: robust plan", StringComparison.Ordinal))
{
command.CommandText += " OPTION(USE HINT('ENABLE_PARALLEL_PLAN_PREFERENCE'))";
}
}
Ahhhh! Why can't I use a Hint ?!
It seems that for some reason USE HINT('ENABLE_PARALLEL_PLAN_PREFERENCE') is treated as USE <db_name>
USE
Changes the database context to the specified database or database snapshot in SQL Server.
Then the erorr: "Database 'HINT' does not exist." has perfect sense.
Now as for ENABLE_PARALLEL_PLAN_PREFERENCE it is undocummented query hint:
https://github.com/MicrosoftDocs/sql-docs/issues/2442
Unfortunately, we have to decline on adding information about ENABLE_PARALLEL_PLAN_PREFERENCE. That is an undocumented query hint that is meant for troubleshooting purposes and is to be used at the direction of Microsoft Support. We normally do not document certain query hints/trace flags intentionally as it may lead to performance issues or other unintended consequences.
So please be cautious when setting it as default or using in production code.
You could try to use OPTION(QUERYTRACEON 8649) which will the behave same as mentioned ENABLE_PARALLEL_PLAN_REFERENCE, but it will require administrator priviliges:
private static void ManipulateCommand(DbCommand command)
{
if (command.CommandText.StartsWith("-- Use hint: robust plan", StringComparison.Ordinal))
{
command.CommandText += " OPTION(QUERYTRACEON 8649)";
}
}
To sum up: Before setting this hint in application code, I would recommend resolve the real underlying issue(maxdop/Cost Threshold for Parallelism/...)
EDIT:
Raw SQL Queries
Composing with LINQ requires your raw SQL query to be composable since EF Core will treat the supplied SQL as a subquery. SQL queries that can be composed on begin with the SELECT keyword. Further, SQL passed shouldn't contain any characters or options that aren't valid on a subquery, such as:
A trailing semicolon
On SQL Server, a trailing query-level hint (for example, OPTION (HASH JOIN))
On SQL Server, an ORDER BY clause that isn't used with OFFSET 0 OR TOP 100 PERCENT in the SELECT clause
When you tell Ef core the output of your query is an entity then EF wrap your query into that entity but if you define a Keyless Entity Types for your query, EF first execute the query and then map result into your model.
first create model like your user model:
public class KeyLessUser
{
public string UserId { get; set; }
public string UserName { get; set; }
}
And add it to DbContext with HasNoKey:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<KeyLessUser>().HasNoKey();
}
Finally execute your query like this:
var results = _context.Set<KeyLessUser>().FromSqlRaw("SELECT * FROM t_user OPTION(USE HINT('ENABLE_PARALLEL_PLAN_PREFERENCE'))").ToList();
Tested and works fine.

Handling multiple resultset from a stored procedure using .SqlQuery with Entity Framework

I'm trying to return 2 results from a stored procedure i.e. PageCount and list of rows.
I created the an object:
public class MyData
{
public int PageCount {get;set;}
public ICollection<MyRowObject> Items {get;set;}
}
and I'm calling the following EF code:
var result = this.Database.SqlQuery<T>('EXEC MySP #param1', parameters)
.FirstOrDefault();
But when I run it, it only returns the PageCount and the Items collection which contains the rows is set to null.
The SP is definitely working when executed in SQL as it is returning the PageCount and the list of matching rows.
Any ideas why this is not working on how I can fix this?
Thanks
Please note that I have found various questions on StackOverflow but they are all using a reader and I'm just curious as to whether or not it can be achieved using .SqlQuery.
Update 1:
I don't know if this is achievable but here's a link I thought I'd share as it's a nicely written article but once again, it's using the Reader.
Entity Framework 6 Multiple Result Sets with Stored Procedures
For Return Multiple ResultSet using Entity Framework from Stored Procedure, Please see the below link. I think it's useful for you.
https://www.codeproject.com/Tips/1072284/Return-Multiple-Result-Set-using-Entity-Framework

Using SQL instead of Linq to query database and return Json

I am developing an application using ASP.Net MVC and AngularJS. I am not familiar with linq and honestly I don't like it. But I am very familiar with SQL and I can perform complex queries. So, my question is, I want you to look at the below code from my MVC controller:
public JsonResult GetSupervisor()
{
var db = new scaleDBEntities();
return this.Json((from userObj in db.Users
select new
{
supervisorId = userObj.Id,
supervisorfName = userObj.usrFirstName,
supervisorlName = userObj.usrLastName,
})
, JsonRequestBehavior.AllowGet
);
}
How can I change the link query into SQL query?
I believe that I can do something like this:
var blogNames = db.Database.SqlQuery<string>("SELECT Name FROM dbo.Blogs").ToList();
So, if this is right, how can i use in in my return this.Json() for my angular?
Using the example you provided, something like this should work:
return this.Json(new {blogNames});
You're just creating an anonymously-typed object that the JSON serializer can use to produce an object like this:
{
"blogNames": ["blog one", "blog two"]
}
It'll be more complicated if you're trying to produce more complex results from a more complex query. But, well, that's what an ORM is for. I'd echo Gert Arnold's advice to embrace LINQ, rather than just deciding you don't like it because you're not used to it.

Entity Framework and LINQ together Batch Update

Afaik Entity Framework 6 doesn't support for batch insert/update/delete.
Is there anyway to make an batch update over an IQueryable object. As an example I have
var query = _db.People.Where(x=>x.Name.Contains(parameter));
an IQueryable (query) object and I want to get the generated sql. Then I hope I can create an update command with this select query like this
Update filteredPerson
Set filteredPerson.Status = 'Updated'
from (here it comes IQueryable Generated SQL :) ) as filteredPerson
over DbContext raw sql execution commands. BTW I don't need EF properties like change tracking and auto detecting. It is just a batch operation.
I know it is pretty risky but I am going to use it for a small piece of code.
Some other logics are appricated. If you know something better, I would like to hear it.
REASON: Why I want to do it this way, because I don't want to spoil the seperation of layers. And there is some validation and filtering comes into the queryable object from other layers. So it is hard to convert it to stored procedure. At the other hand it must be faster than other standard queries.
Again I know there is no support in Entity Framework 6 for batch operations. But other questions are bit outdated. That's another reason why I want to ask this again.
While I was writing the question, I was guessing how I am going to solve it. But I was looking for some more proper way of it. In the end, I know what am I doing and tried to be simple for my colleagues who looking to the same code after me. I know it has some risky usages but I let the exceptions to CLR to handle it. After this excuse :) , I wrote the code like this:
Let's say I have an IQueryable object which is generated with this way:
string parameter = "John";
AdventureWorks2012Entities _db = new AdventureWorks2012Entities();
var query = _db.People.AsQueryable();
//Some parameters added from different layers
query = query.Where(x => x.FirstName.Contains(parameter));
Then I want a batch update over this IQueryable object.
var sqlFrom = query.ToString(); //This is the query which becomes "from table"
var dbq = query.AsDbQuery().GetObjectQuery(); //This does some magic with reflection
var linqParams = dbq.Parameters
.Select(x => new System.Data.SqlClient.SqlParameter(x.Name, x.Value)).ToList();
linqParams.Add(new System.Data.SqlClient.SqlParameter("#ModDate", DateTime.Now));
var sqlBatchUpdate = #"Update filteredPerson Set ModifiedDate = #ModDate From (#FilteredPerson) as filteredPerson"
.Replace("#FilteredPerson", sqlFrom);
var affectedRows = _db.Database.ExecuteSqlCommand(sqlBatchUpdate, linqParams.ToArray());
That's it! Now I don't have to repeat same business logic in stored procedure again. And it is more faster than a foreach and SaveChanges combo.
So I ended up with this for very basic usage. As a fast solution It brings more problems no doubt! But I know I can easily wrap around it for new purposes. So It is up to programmer who wants to use it with more preferences.
Also the code which does the reflection and casting is below and I added a gist for full code:
public static ObjectQuery<T> GetObjectQuery<T>(this DbQuery<T> query)
{
var internalQueryField = query.GetType()
.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
.Where(f => f.Name.Equals("_internalQuery"))
.FirstOrDefault();
var internalQuery = internalQueryField.GetValue(query);
var objectQueryField = internalQuery.GetType()
.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
.Where(f => f.Name.Equals("_objectQuery"))
.FirstOrDefault();
var objectQuery = objectQueryField.GetValue(internalQuery) as ObjectQuery<T>;
return objectQuery;
}
Here is the Gist file. Hope It helps somebody out there.

Entity Framework Dynamic Mapping

I have a EntityAttributeValue database that I have no control over. To get the data I use a stored proc as follows.
public class PhotoDataContext : DbContext, IPhotoDataContext
{
public IEnumerable<PhotoRegistration> GetPhotoRegistration()
{
return this.Database.SqlQuery<PhotoRegistration>("SP_Photo") ;
}
}
This works fine for when the POCO maps perfectly. Now I have a stored procedure that can return dynamic fields. All fields are of string type.
I am using EF 4.1 Code First and am passing the POCO's to Silverlight via WCF.
Any ideas how to stop the auto mapping of EF and to pivot the data back into a name value pair that WCF will be happy to serialize.
Thanks
J
Use ADO.NET = SqlConnection, SqlCommand, SqlDataReader (or equivalent if you don't use SQL Server). Entity framework doesn't like dynamic result sets.