NHibernate and SQL Server Temporal Tables - c#

I am using Fluent NHibernate to query data stored in a SQL Server temporal table. All I really want to do is to run a query such as
SELECT * FROM [MyDB].[dbo].[MyTable] for system_time as of '2022-12-28T21:00:00.0000000' where ...
Via NHibernate. It doesn't seem like there's any way built in to NHibernate that can run that query. I can run queries such as below but that only hits the current table, and not the history table.
session.QueryOver<MyDTO>().Where(x => x.TemporalPeriodStart > ...)

You can execute native SQL queries (and return non managed entities).
The query needs to specify:
Query String
A Result transformer
Example:
session.CreateSQLQuery("SELECT * FROM [MyDB].[dbo].[MyTable] for system_time as of '2022-12-28T21:00:00.0000000' where...")
.SetResultTransformer(Transformers.AliasToBean(typeof(CustomObject)))
Worth noting that the custom IResultTransformer should override "Equals" and "GetHashCode", otherwise a memory leak is possible since query translation won't be cached.
You can also use Named SQL query, which can be defined in the mapping document and called in the same way as a named HQL query...

Related

Does the query builder prevent SQL Injection?

Hi all i'm currently working in ASP.NET using the query builder to create select, insert, update queries etc etc on some datasets which i have created in my App_Code folder. I have realised for you to use a parameters in the queries you have to use a "?" like so
SELECT * FROM users WHERE email = ?
what i was wondering is does this actually protect your tables from sql injection or do you need to do more in the code in order to protect the queries?
Parameterized queries accept parameters and input them as the appropriate SQL data type. So for example creating this proc
CREATE PROCEDURE GetStudent (IN LN VARCHAR(200))
BEGIN
SELECT Name FROM Students WHERE LastName = LN;
END
And passing this value (assume this is in your C# code.
"'Bobby'; DROP TABLE STUDENTS;"
Will essentially execute this query
SELECT Name FROM Students WHERE LastName ='''Bobby;''DROP TABLE Students'
Which is quite safe.
Of course, you will have to adapt for your particular application needs, but the general point is that parameterized queries are safe against SQL Injection for all major RDMSs.

Same query with the same query plan takes ~10x longer when executed from ADO.NET vs. SMSS

My query is fairly complex, but I have simplified it to figure out this problem and now it is a simple JOIN that I'm running on a SQL Server 2014 database. The query is:
SELECT * FROM SportsCars as sc INNER JOIN Cars AS c ON c.CarID = sc.CarID WHERE c.Type = 1
When I run this query from SMSS and watch it in SQL Profiler, it takes around 350ms to execute. When I run the same query inside my application using Entity Framework or ADO.NET (I've tried both). It takes 4500ms to execute.
ADO.NET Code:
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
var cmdA = new SqlCommand("SET ARITHABORT ON", connection);
cmdA.ExecuteNonQuery();
var query = "SELECT * FROM SportsCars as sc INNER JOIN Cars AS c ON c.CarID = sc.CarID WHERE c.Type = 1";
var cmd = new SqlCommand(query, connection);
cmd.ExecuteNonQuery()
}
I've done an extensive Google search and found this awesome article and several StackOverflow questions (here and here). In order to make the session parameters identical for both queries I call SET ARITHABORT ON in ADO.NET and it makes no difference. This is a straight SQL query, so there is not a parameter sniffing problem. I've simplified the query and the indexes down to their most basic form for this test. There is nothing else running on the server and there is nothing else accessing the database during the test. There are no computed columns in the Cars or SportsCars table, just INTs and VARCHARs.
The SportsCars table has about 170k records and 4 columns, and the Cars table has about 1.2M records and 7 columns. The resulting data set (SportsCars of Type=1) has about 2600 records and 11 columns. I have a single non-clustered index on the Cars table, on the [Type] column that includes all the columns of the cars table. And both tables have a clustered index on the CarID column. No other indexes exist on either table. I'm running as the same database user in both cases.
When I view the data in SQL Profiler, I see that both queries are using the exact same, very simple query plan. In SQL Profiler, I'm using the Performance Event Class and the ShowPlan XML Statistics Profile, which I believe to be the proper event to monitor and capture the actual execution plan. The # of reads is the same for both queries (2596).
How can two exact same queries with the exact same query plan take 10x longer in ADO.NET vs. SMSS?
Figured it out:
Because I'm using Entity Framework, the connection string in my application has MultipleActiveResultSets=True. When I remove this from the connection string, the queries have the same performance in ADO.NET and SSMS.
Apparently there is an issue with this setting causing queries to respond slowly when connected to SQL Server via WAN. I found this link and this comment:
MARS uses "firehose mode" to retrieve data. Firehose mode means that
the server will produce data as fast as possible. This also means that
your client application must receive inbound data at the same speed as
it comes in. If it doesn't the data storage buffers on the server will
fill up and the processing will stop until those buffers empty.
So what? You may ask... But as long as the processing is stopped the
resources on the SQL server are in use and are tied up. This includes
the worker thread, schema and data locks, memory, etc. So it is
crucial that your client application consumes the inbound results as
quickly as they arrive.
I have to use this setting with Entity Framework otherwise lazy loading will generate exceptions. So I'm going to have to figure out some other workaround. But at least I understand the issue now.
How can two exact same queries with the exact same query plan take 10x longer in ADO.NET vs. SMSS?
First we need to be clear about what is considered "same" with regards to queries and query plans. Assuming that the query at the very top of the question is a copy-and-paste, then it is not the same query as the one being submitted via ADO.NET. For two queries to be the same, they need to be byte-by-byte the same, which includes all white-space, capitalization, punctuation, comments, etc.
The two queries shown are definitely very similar. And they might even share the same execution plan. But how was "same"ness determined for those? Was the XML the same in both cases? Or just what was shown graphically in SSMS when viewing the plans? If they were determined to be the same based on their graphical representation then that is sometimes misleading. The XML itself needs to be checked. Even if two query plans have the same query hash, there are still (sometimes) parts of a query plan that are variable and changes do not change the plan hash. One example is the evaluation of expressions. Sometimes they are calculated and their result is embedded into the plan as a constant. Sometimes they are calculated at the start of each execution and stored and reused within that particular execution, but not for any subsequent executions.
One difference between SSMS and ADO.NET is the default session properties for each. I thought I had seen a chart years ago showing the defaults for ADO / OLEDB / SQLNCLI but can't find it out. Either way, it doesn't need to be guess work as it can be discovered using the SESSIONPROPERTY function. Just run this query in the C# code instead of your current SELECT, and inspect the results in debug or print them out or whatever. Either way, run something like this:
SELECT SESSIONPROPERTY('ANSI_NULLS') AS [AnsiNulls],
SESSIONPROPERTY('ANSI_PADDING') AS [AnsiPadding],
SESSIONPROPERTY('CONCAT_NULL_YIELDS_NULL') AS [ConcatNullYieldsNull],
...;
Make sure to get all of the setting noted in the linked MSDN page. Now, in SSMS, go to the "Query" menu, select "Query Options...", and go to "Execution" | "ANSI". The settings coming back from the C# code need to match the ones showing in SSMS. Anything set different requires adding something like this to the beginning of your ADO.NET query string:
SET ANSI_NULLS ON;
{rest of query}
Now, if you want to eliminate the DataTable loading from being a possible suspect, just replace that line, just replace:
var cars = new DataTable();
cars.Load(reader);
with:
while(reader.Read());
And lastly, why not just put the query into a Stored Procedure? The session settings (i.e. ANSI_NULLS, etc) that typically matter the most are stored with the proc definition so they should work the same whether you EXEC from SSMS or from ADO.NET (again, we aren't dealing with any parameters here).

Will linq bring back all records from a stored procedure call, and then filter?

Given the EF linq below, will all the records in the stored proc usp_GetTestRecords() come across and then get filtered?
TestRecordsDBEntities dataContext = new TestRecordsDBEntities();
var tests = dataContext.usp_GetTestRecords();
var filtered = tests.Where(x => x.GroupId == groupId)
.OrderByDescending(y => y.Name)
.ToList();
Yes all data will be first fetched in the memory and then filtered on the client side. Using stored procedure with EF is not a good idea. You will loose the advantage of lazy,eager or explicit loading here. However if you let EF generate queries for you then it will be compiled will all filters and executed on server
As an alternative, you may want to consider creating a table value function rather than stored proc. The advantage here is that result-set of the function can be joined with other tables on the server side. The disadvantage is that you are limited in terms of what you can do inside of the function and the database does not have access to indexes for the function results that you could with indexed views.
See more about using TVS with EF at http://blogs.msdn.com/b/efdesign/archive/2011/01/21/table-valued-function-support.aspx
Sprocs will allways return all the records affected by te query associated to it. If you want to add a where clause just add a param to your sproc and perform the filtering in sql. Check this for more information How to pass Parameters to Stored Procedure from Entity Framework?

LINQ to SQL will not generate sargable query

I'm using LINQ To Sql (not Entity Framework), the System.Data.Linq.DataContext library, hitting a SQL Server 2005 database and using .Net Framework 4.
The table dbo.Dogs has a column "Active" of type CHAR(1) NULL. If I was writing straight SQL the query would be:
SELECT * FROM dbo.Dogs where Active = 'A';
The LINQ query is this:
from d in myDataContext.Dogs where d.Active == 'A' select d;
The SQL that gets generated from the above LINQ query converts the Active field to UNICODE. This means I cannot use the index on the dbo.Dogs.Active column, slowing the query significantly:
SELECT [t0].Name, [t0].Active
FROM [dbo].[Dog] AS [t0]
WHERE UNICODE([t0].[Active]) = #p1
Is there anything I can do to stop Linq to Sql from inserting that UNICODE() call (and thus losing the benefit of my index on dogs.Active)? I tried wrapping the parameters using the EntityFunctions.AsNonUnicode() method, but that did no good (it inserted a CONVERT() to NVARCHAR instead of UNICODE() in the generated sql), eg:
...where d.Active.ToString() == EntityFunctions.AsNonUnicode('A'.ToString());
Linq is meant to make it easier to write queries and does not always generate optimal SQL. Sometimes when high performance is required it is more efficient to write raw SQL directly against the database, the Linq datacontext supports mapping of SQL result to entities just like linq.
In your case I would suggest writing:
IEnumerable<Dog> results = db.ExecuteQuery<Dog>(
"SELECT * FROM dbo.Dogs where Active = {0}",
'A');
This is an old question, but I bumped into this recently.
Instead of writing
from d in myDataContext.Dogs where d.Active == 'A' select d;
Write
from d in myDataContext.Dogs where d.Active.Equals('A') select d;
This will produce the desired SQL without having to resort to any of the "hacks" mentioned in other answers. I can't say why for certain.
I've posted that as a question, so we'll see if we get any good answers.
There's not much you can do to the way LINQ queries are translated into SQL statements, but you can write a stored procedure that contains your queries and call that SP as a LINQ2SQL function. This way you should get full benefit of SQL Server optimizaions
You can do a little hack (as it is often required with LINQ to SQL and EF). Declare the property as NCHAR in the dbml. I hope that will remove the need to do the UNICODE conversion. We are tricking L2S in a benign way with that.
Maybe you need to also insert the EntityFunctions.AsNonUnicode call to make the right hand side a non-unicode type.
You can also try mapping the column as varchar.

Query conceptual model with Entity SQL

My application is based on Entity Framework. I am offering users to query a particular table by saving their queries in another table. For example, TopQuery table in database stores all the queries which are popular among users.
These queries are performed on table "TableData"
For test purposed, I have tried the following and it works. The only problem is that it returns all columns where as I would like to use columns that are mentioned by users in their queries.
string queryString =
#"SELECT VALUE table FROM TestEntities.TableData AS table where table.col1 = 'test'";
ObjectQuery<TableData> productQuery2 =
new ObjectQuery<TableData>(queryString, context);
My problem is that if user stores a query in database like this, it doesn't work.
SELECT table.col1, table.col2, table.col3 FROM TestEntities.TableData AS table where table.col1 = "test"
I get the exception: The specified cast from a materialized System.Data.Objects.MaterializedDataRecord' to'TestEntities.TableData'type is not valid.
I have also tried this without any luck.
"SELECT it.col1, it.col2 FROM TableData WHERE it.col1 = 'test'"
What should I do in such case?
Regards,
You will never get ObjectQuery<TableData> once you try to select only subset of columns. Using ObjectQuery<TableData> works only when you select whole entity as your first query did - that is a strongly typed approach enforced by Entity framework.
ESQL doesn't support projection in the way Linq-to-entities does (by allowing you to project to a new anonymous or non mapped type). When using projection with ESQL you must work with ObjectQuery<DbDataRecord>.

Categories

Resources