I have a simple table insert in entity framework to add a record to a Azure SQL Data-Warehouse table. I get this error on context.SaveChanges() - SQLException: Must declare the scalar variable "##ROWCOUNT
Reading a table works perfectly fine only the saving to a table fails.
context.Users.Add(user);
context.SaveChanges(); -> fails here.
Expected result - record should get inserted in the table
Actual result - Microsoft.EntityFrameworkCore.DbUpdateException: 'An error occurred while updating the entries. See the inner exception for details.'
Inner Exception
SqlException: Must declare the scalar variable "##ROWCOUNT".
I found out that EntityFramework is not supported for Azure SQL Data Warehouse. https://feedback.azure.com/forums/307516-sql-data-warehouse/suggestions/12868725-support-for-entity-framework
I used the SqlConnection and SqlCommand as a workaround.
`using (var cn = new SqlConnection(connectionString))
{
var query = "insert into Users([Id]) values (#Id)";
using (var cmd = new SqlCommand(query, cn))
{
cmd.Parameters.AddWithValue("#Id", 1);
cn.Open();
cmd.ExecuteNonQuery();
cn.Close();
}
}`
Related
I am using a third party library to perform mass inserts into a database PgBulkInsert . It takes inserts that would normally take 30 minutes and performs them in 30 seconds. We have noticed that overtime there is disk usage leakage, but we figure out that performing a table reindex appears to corrcect the issue. I am trying to use my JPA Entity Manager to perform a native update. The below code works but contains potential SQL injection vulnerability.
#Stateless
public class ReindexService {
#PersistenceContext(unitName = "my-ds")
private EntityManager em;
public void reindexTable(String table) {
String queryStr = "REINDEX TABLE " + table;
Query query = em.createNativeQuery(queryStr);
query.executeUpdate();
}
}
When I pass in string "alert" to index the alert table it yields the following SQL output
/* dynamic native SQL query */ REINDEX TABLE alert
When I attempt to use a positional parameter it yields a SQL error
String queryStr = "REINDEX TABLE ?";
Query query = em.createNativeQuery(queryStr);
query.setParameter(1, table);
query.executeUpdate();
This yields the following error output
/* dynamic native SQL query */ REINDEX TABLE ?
SQL Error: 0, SQLState: 42601
ERROR: syntax error at or near "$1"
Position: 46
I get a similar error when I try to use a name parameter
String queryStr = "REINDEX TABLE :table";
Query query = em.createNativeQuery(queryStr);
query.setParameter("table", table);
query.executeUpdate();
This yields the same error
/* dynamic native SQL query */ REINDEX TABLE ?
SQL Error: 0, SQLState: 42601
ERROR: syntax error at or near "$1"
Position: 46
Does anyone know how I can call a the native Postgresql reindex table command using my entity manager in a manner without adding a SQL injection vulnerability? I am using Hibernate 5.3.6.Final but would prefer a non-implementation specific solution.
I also tried to access the Connection and perform a JDBC call and it seems to give the error
final Session session = //get session from entity manager
session.doWork(conn -> {
try (PreparedStatement stmt = conn.prepareCall(REINDEX TABLE ?)) {
stmt.setString(1, table);
stmt.execute();
}
});
Yields the same errors as above
SQL Error: 0, SQLState: 42601
ERROR: syntax error at or near "$1"
Position: 15
Identifiers can't be passed as parameters. If you don't expect the table name to come from user input (it sounds a bit strange), you may try using an enumeration of all the tables which you want to reindex, and pass enumeration values to your service (and just concatenate strings).
If you do expect table names to come from untrusted sources, you can try enclosing identifier with double-quotes and escaping existing double-quotes.
There is also a function quote_ident in PostgreSQL which can be used to quote identifiers properly. So you can create a stored procedure which takes a regular argument from your JPA code and uses quote_ident and EXECUTE a constructed query
.
Our workaround was to create a Database Function and call it using a native query
The database function
CREATE OR REPLACE FUNCTION reindex_table(table_in text)
RETURNS void
SECURITY DEFINER
AS $$
BEGIN
EXECUTE FORMAT('REINDEX (VERBOSE) TABLE %I', table_in);
RETURN;
END;
$$ LANGUAGE plpgsql;
Here is the Service code for calling the function
public void reindexTable(String table) {
String queryStr = "select reindex_table(?)";
final Session session = //obtain Hibernate Session from Entitymanager
session.doWork(conn -> {
try (PreparedStatement stmt = conn.prepareCall(queryStr)) {
stmt.setString(1, table);
stmt.execute();
}
});
}
This is something weird ,But Just try, table is a reserved keyword in PostgreSQL. So try changing the variable name.
String queryStr = "REINDEX TABLE :tableName";
Query query = em.createNativeQuery(queryStr);
query.setParameter("tableName", "AUTH_IND");
query.executeUpdate();
From the Documentation :
"select" could be used to refer to a column or table named “select”, whereas an unquoted select would be taken as a key word and would therefore provoke a parse error when used where a table or column name is expected.
https://www.postgresql.org/docs/current/sql-syntax-lexical.html
Is this a bug?
My components are:
.NET 4.6.1
Entity Framework 6.0
Npgsql 3.0.5
PostgreSQL 9.5
First, I created a table and stored procedure in PostgreSQL 9.5
CREATE TABLE hello
(
msg text
)
WITH (
OIDS=FALSE
);
ALTER TABLE hello
OWNER TO postgres;
CREATE OR REPLACE FUNCTION sayhello()
RETURNS SETOF hello AS
$BODY$
select
*
from version()
$BODY$
LANGUAGE sql VOLATILE
COST 100
ROWS 1000;
ALTER FUNCTION sayhello()
OWNER TO postgres;
Second, I went to the .edmx file (Entity Framework 6.0), choose "update from database", selected the new table "hello" and the new stored procedure, "sayhello".
The Model Browser now shows the new table entity and the imported function.
Third, add a new procedure to the WCF file:
public string SayHello()
{
using (var ctx = new chaosEntities())
{
var x = ctx.sayhello();
return "Hello";
}
}
Set the WCF Service as Startup project and Start Debugging.
The WCF Test Client comes up. Executing SayHello() from the WCF Service leads to:
public virtual ObjectResult<hello> sayhello()
{
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<hello>("sayhello");
}
When this is executed, I get:
An exception of type 'System.Data.Entity.Core.EntityCommandCompilationException'
occurred in EntityFramework.dll but was not handled in user code
Additional information: An error occurred while preparing the command
definition. See the inner exception for details.
Inner Exception is: {"Value does not fall within the expected range."}
As I have several hundred stored procedures, any help on how to fix this is most appreciated.
TIA
Note: I suspect the problem is with NpgsqlServices.TranslateCommandTree, but I'm only guessing.
I could never get it to work the way I hoped (via EntityFramework), so I ended up doing this. I'd sure be open to a better solution!
The below code calls Npgsql directly to avoid the whole EntityFramework thing.
public string SayHello()
{
using (var ctx = new chaosEntities())
{
var b = ctx.Database.Connection.ConnectionString;
using (var conn = new Npgsql.NpgsqlConnection(connectionString: b))
{
conn.Open();
using (var tran = conn.BeginTransaction())
using (var command = conn.CreateCommand())
{
command.CommandText = "sayhello";
command.CommandType = CommandType.StoredProcedure;
var g = (string)command.ExecuteScalar();
return g;
}
}
}
}
Got a very strange scenario here.
Calling the following Code using DBContext the Remove Fails
int id = 5;
var sql = "SELECT * from dbo.Product WHERE productId = #id";
var data = dbContext.Set<Product>().SqlQuery(sql, new object[] {new SQLParameter("id", id)});
var entityToDelete = data.AsQueryable().FirstOrDefault();
dbContext.Set<Product>().Remove(entityToDelete);
dbContext.SaveChanges();
I receive the following Error Message:
Entities in 'MyEntities.Products' participate in the
'FK_Product_ProductType' relationship. 0 related 'ProductType' were
found. 1 'ProductType' is expected.
Now Every product has a foreign key reference to a ProductType. There is actually a value set for this Foreign Key in the Database.
Here is the kicker. When I debug and step into the code line by line it succeeds? Very strange? Why is this happening? any ideas?
UPDATE
It succeeds when I watch the variable entityToDelete when Debugging. But fails if I remove the watch. Which makes me think it has something to do with Lazy Loading.
I don't understand how ADO.NET recognizes a concurrency violation unless it's doing something beyond what I'm telling it to do, inside its "black box".
My update query in SQL Server 2000 does something like the following example, which is simplified; if the rowversion passed to the stored proc by the client doesn't match the rowversion in the database, the where-clause will fail, and no rows will be updated:
create proc UpdateFoo
#rowversion timestamp OUTPUT,
#id int,
#foodescription varchar(255)
as UPDATE FOO set description = #foodescription
where id = #id and rowversion = #rowversion;
if ##ROWCOUNT = 1
select #rowversion from foo where id = #id;
I create a SqlCommand object and populate the parameters and assign the command object to the SqlDataAdapter's UpdateCommand property. Then I invoke the data adapter's Update method.
There should indeed be a concurrency error because I deliberately change the database row in order to force a new rowversion. But how does ADO.NET know this? Is it doing something more than executing the command?
In the RowUpdated event of the SqlDataAdapter there will be a Concurrency error:
MySqlDataAdapter += (sender, evt) =>
{
if ((evt.Status == UpdateStatus.Continue) && (evt.StatementType == StatementType.Update))
{
// update succeeded
}
else
{
// update failed, check evt.Errors
}
}
Is ADO.NET comparing the rowversions? Is it looking at ##rowcount?
I understand stored procedures mapping is not supported by my understanding is that I should be able to call stored procedures.
I have quite a few complex stored procedures and with the designer I could create a complex type and I was all good.
Now in code first let's suppose I have the following stored procedure, just put together something silly to give an idea. I want to return a student with 1 address.
In code I have A Student and Address Entity. But no StudentAddressEntity as it's a link table.
I have tried the following but I get an error
Incorrect syntax near '."}
System.Data.Common.DbException {System.Data.SqlClient.SqlException}
ALTER Procedure [dbo].[GetStudentById]
#StudentID int
AS
SELECT *
FROM Student S
left join StudentAddress SA on S.Studentid = sa.studentid
left join Address A on SA.AddressID = A.AddressID
where S.StudentID = #StudentID
C# code:
using (var ctx = new SchoolContext())
{
var student = ctx.Database.SqlQuery<Student>("GetStudentById,#StudentID",
new SqlParameter("StudentID", id));
}
Any examples out there how to call sp and fill a complexType in code first, using out parameters etc.. Can I hook into ADO.NET?
Trying just an SP that returns all students with no parameters I get this error
System.SystemException = Cannot create a value for property
'StudentAddress' of type
'CodeFirstPrototype.Dal.Address'. Only
properties with primitive types are
supported.
Is it because I have in a way ignore the link table?
Any suggestions?
I believe that your exception actually is:
Incorrect syntax near ','.
because this is invalid statement: "GetStudentById,#StudentID". It should be without comma: "GetStudentById #StudentID".
The problem with stored procedures in EF is that they don't support loading navigation properties. EF will materialize only the main entity and navigation properties will not be loaded. This is solved for example by EFExtensions. EFExtensions are for ObjectContext API so you will have to check if it is also usable for DbContext API.
Using EFExtentions it will look something like
using (var context = new SchoolContext())
{
var command = context.CreateStoreCommand("GetStudentById", CommandType.StoredProcedure,
new SqlParameter("StudentID", id));
using (command.Connection.CreateConnectionScope())
using (var reader = command.ExecuteReader())
{
// use the reader to read the data
// my recommendation is to create a Materializer using EFExtensions see
// http://blogs.msdn.com/b/meek/archive/2008/03/26/ado-entity-framework-stored-procedure-customization.aspx
// ex
var student = Student.Materializer.Materialize(reader).SingleOrDefault();
return student;
}
}