Is it possible to run native sql with entity framework? - sql

I am trying to search an XML field within a table, This is not supported with EF.
Without using pure Ado.net is possible to have native SQL support with EF?

For .NET Framework version 4 and above: use ObjectContext.ExecuteStoreCommand() if your query returns no results, and use ObjectContext.ExecuteStoreQuery if your query returns results.
For previous .NET Framework versions, here's a sample illustrating what to do. Replace ExecuteNonQuery() as needed if your query returns results.
static void ExecuteSql(ObjectContext c, string sql)
{
var entityConnection = (System.Data.EntityClient.EntityConnection)c.Connection;
DbConnection conn = entityConnection.StoreConnection;
ConnectionState initialState = conn.State;
try
{
if (initialState != ConnectionState.Open)
conn.Open(); // open connection if not already open
using (DbCommand cmd = conn.CreateCommand())
{
cmd.CommandText = sql;
cmd.ExecuteNonQuery();
}
}
finally
{
if (initialState != ConnectionState.Open)
conn.Close(); // only close connection if not initially open
}
}

Using Entity Framework 5.0 you can use ExecuteSqlCommand to execute multi-line/multi-command pure SQL statements. This way you won't need to provide any backing object to store the returned value since the method returns an int (the result returned by the database after executing the command).
Sample:
context.Database.ExecuteSqlCommand(#
"-- Script Date: 10/1/2012 3:34 PM - Generated by ExportSqlCe version 3.5.2.18
SET IDENTITY_INSERT [Students] ON;
INSERT INTO [Students] ([StudentId],[FirstName],[LastName],[BirthDate],[Address],[Neighborhood],[City],[State],[Phone],[MobilePhone],[Email],[Enrollment],[Gender],[Status]) VALUES (12,N'First Name',N'SecondName',{ts '1988-03-02 00:00:00.000'},N'RUA 19 A, 60',N'MORADA DO VALE',N'BARRA DO PIRAÍ',N'Rio de Janeiro',N'3346-7125',NULL,NULL,{ts '2011-06-04 21:25:26.000'},2,1);
INSERT INTO [Students] ([StudentId],[FirstName],[LastName],[BirthDate],[Address],[Neighborhood],[City],[State],[Phone],[MobilePhone],[Email],[Enrollment],[Gender],[Status]) VALUES (13,N'FirstName',N'LastName',{ts '1976-04-12 00:00:00.000'},N'RUA 201, 2231',N'RECANTO FELIZ',N'BARRA DO PIRAÍ',N'Rio de Janeiro',N'3341-6892',NULL,NULL,{ts '2011-06-04 21:38:38.000'},2,1);
");
For more on this, take a look here: Entity Framework Code First: Executing SQL files on database creation

For Entity Framework 5 use context.Database.SqlQuery.
And for Entity Framework 4 use context.ExecuteStoreQuery
the following code:
public string BuyerSequenceNumberMax(int buyerId)
{
string sequenceMaxQuery = "SELECT TOP(1) btitosal.BuyerSequenceNumber FROM BuyerTakenItemToSale btitosal " +
"WHERE btitosal.BuyerID = " + buyerId +
"ORDER BY CONVERT(INT,SUBSTRING(btitosal.BuyerSequenceNumber,7, LEN(btitosal.BuyerSequenceNumber))) DESC";
var sequenceQueryResult = context.Database.SqlQuery<string>(sequenceMaxQuery).FirstOrDefault();
string buyerSequenceNumber = string.Empty;
if (sequenceQueryResult != null)
{
buyerSequenceNumber = sequenceQueryResult.ToString();
}
return buyerSequenceNumber;
}
To return a List use the following code:
public List<PanelSerialList> PanelSerialByLocationAndStock(string locationCode, byte storeLocation, string itemCategory, string itemCapacity, byte agreementType, string packageCode)
{
string panelSerialByLocationAndStockQuery = "SELECT isws.ItemSerialNo, im.ItemModel " +
"FROM Inv_ItemMaster im " +
"INNER JOIN " +
"Inv_ItemStockWithSerialNoByLocation isws " +
" ON im.ItemCode = isws.ItemCode " +
" WHERE isws.LocationCode = '" + locationCode + "' AND " +
" isws.StoreLocation = " + storeLocation + " AND " +
" isws.IsAvailableInStore = 1 AND " +
" im.ItemCapacity = '" + itemCapacity + "' AND " +
" isws.ItemSerialNo NOT IN ( " +
" Select sp.PanelSerialNo From Special_SpecialPackagePriceForResale sp " +
" Where sp.PackageCode = '" + packageCode + "' )";
return context.Database.SqlQuery<PanelSerialList>(panelSerialByLocationAndStockQuery).ToList();
}

Keep it simple
using (var context = new MyDBEntities())
{
var m = context.ExecuteStoreQuery<MyDataObject>("Select * from Person", string.Empty);
//Do anything you wonna do with
MessageBox.Show(m.Count().ToString());
}

public class RaptorRepository<T>
where T : class
{
public RaptorRepository()
: this(new RaptorCoreEntities())
{
}
public RaptorRepository(ObjectContext repositoryContext)
{
_repositoryContext = repositoryContext ?? new RaptorCoreEntities();
_objectSet = repositoryContext.CreateObjectSet<T>();
}
private ObjectContext _repositoryContext;
private ObjectSet<T> _objectSet;
public ObjectSet<T> ObjectSet
{
get
{
return _objectSet;
}
}
public void DeleteAll()
{
_repositoryContext
.ExecuteStoreCommand("DELETE " + _objectSet.EntitySet.ElementType.Name);
}
}

So what do we say about all this in 2017? 80k consultations suggests that running a SQL request in EF is something a lot of folk want to do. But why? For what benefit?
Justin, a guru with 20 times my reputation, in the accepted answer gives us a static method that looks line for line like the equivalent ADO code. Be sure to copy it well because there are a few subtleties to not get wrong. And you're obliged to concatenate your query with your runtime parameters since there's no provision for proper parameters. So all users of this method will be constructing their SQL with string methods (fragile, untestable, sql injection), and none of them will be unit testing.
The other answers have the same faults, only moreso. SQL buried in double quotes. SQL injection opportunities liberally scattered around. Esteemed peers, this is absolutely savage behaviour. If this was C# being generated, there would be a flame war. We don't even accept generating HTML this way, but somehow its OK for SQL. I know that query parameters were not the subject of the question, but we copy and reuse what we see, and the answers here are both models and testaments to what folk are doing.
Has EF melted our brains? EF doesn't want you to use SQL, so why use EF to do SQL.
Wanting to use SQL to talk to a relational DB is a healthy, normal impulse in adults. QueryFirst shows how this could be done intelligently, your sql in .sql file, validated as you type, with intellisense for tables and columns. The C# wrapper is generated by the tool, so your queries become discoverable in code, with intellisense for your inputs and results. End to end strong typing, without ever having to worry about a type. No need to ever remember a column name, or its index. And there are numerous other benefits... The temptation to concatenate is removed. The possibility of mishandling your connections also. All your queries and the code that accesses them are continuously integration-tested against your dev DB. Schema changes in your DB pop up as compile errors in your app. We even generate a self test method in the wrapper, so you can test new versions of your app against existing production databases, rather than waiting for the phone to ring. Anyone still need convincing?
Disclaimer: I wrote QueryFirst :-)

Related

Does r2dbc repository support dynamic SQL conjecture?

I was migrating traditional JPA based project to reactive Project, which was based on r2dbc.
There have many dynamics SQL conjecture in our project before, like if user pass parameter exchange value as CME,we will add "and exchange='CME'" at the end of SQL and run it by JDBCTemplate:
List<Object> parameters = new ArrayList<>();
String sql = QUERY_BrokerIDs_Old;
sql += " and exchange = ?";
parameters.add(exchange);
if (!StringUtils.isEmpty(filter)) {
sql += " and memberID like ?";
parameters.add("%" + filter.trim().toUpperCase() + "%");
}
sql += " order by id";
if (page > 0 && pageSize > 0) {
int offset = (page - 1) * pageSize;
String pageClause = PAGE_CLAUSE;
sql += pageClause;
parameters.add(offset);
parameters.add(pageSize);
}
return jdbcTemplate.queryForList(sql, parameters.toArray(), String.class);
I found r2dbc repository provide multiple convient methods, like findAll, getXxxByXxx,I really like it. And we also can declare the SQL in #Query like:
#Query("SELECT * FROM person WHERE lastname = :lastname")
Flux<Person> findByLastname(String lastname);
But does it support dynamic parameter as query conditions in its repository class? Or I can do some customize on it to implement it?
Otherwise I only can implement it by SQL conjecture and run it by template, like the old way before…

SQL Server update in C#

I try to UPDATE data in my SQL Server database and I get this error:
System.Data.SqlClient.SqlException
Incorrect syntax near 'de'
Unclosed quotation mark after the character string ')'
private void BtEnrMod_Click(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection("Data Source=.\\BD4X4;Initial Catalog=BD4X4;Integrated Security=True");
con.Open();
SqlCommand cmd = new SqlCommand("UPDATE Service SET Type = " + TxBxService.Text + ", Prix = " + TxBxPrix.Text + "WHERE Code = " + LbCodeAff.Text + "')", con);
int i = cmd.ExecuteNonQuery();
if (i != 0)
{
MessageBox.Show("Service Modifié");
}
else
{
MessageBox.Show("Erreur");
}
this.Close();
con.Close();
}
Replace the one liner that declares your command with this code block:
SqlCommand cmd = new SqlCommand("UPDATE Service SET Type = #t, Prix = #p WHERE Code = #c", con);
cmd.Parameters.AddWithValue("#t", TxBxService.Text);
cmd.Parameters.AddWithValue("#p", TxBxPrix.Text);
cmd.Parameters.AddWithValue("#t", LbCodeAff.Text);
Always avoid writing an sql where you string concatenate in a value provided by the user in a text box; it's the number one security horror you can make with sql. Always use parameters to put values in, like you see here. For more info on this SQL injection hacking, see http://bobby-tables.com
If you ever fin yourself in a situation where you think you have to concatenate to make an sql, don't concatenate a value in; concatenate a parameter in and add the value into the parameters collection. Here's a hypothetical example:
var cmd = new SqlCommand("","connstr");
strSql = "SELECT * FROM table WHERE col IN (";
string[] vals = new[]{ "a", "b", "c" };
for(int x = 0; x<vals.Length; x++){
strSql += ("#p"+x+",");
cmd.Parameters.AddWithValue("#p"+x, vals[x]);
}
cmd.CommandText = strSql + ")";
This uses concatenation to make an sql of SELECT * FROM table WHERE col IN (#p0, #p1, #p2) and a nicely populated parameters collection
When you're done grokking that, read the link Larnu posted in the comments. There are good reasons to avoid using AddWithValue in various scenarios but it will always be preferable to concatenation of values. Never ditch the use of parameters "because I read a blog one time about how AddWithValue is bad" - form parameters using the new parameter constructor, or use AddWithValue shortcut, but never concat values
Or better still than all of this, use an ORM like Entity Framework, nHibernate or Dapper and leave most of this boring boilerplate low level SQL drudgery behind. These libraries do most of this wrangling for you; EF and nH even write th sql too, dapper you write it yourself but it takes care of everything else
Using a good ORM is like the difference between writing creating a UI manually line by line of position, font, anchor, event code for every button, label and text box versus using the windows forms designer; a world apart and there's no sense in taking hours to create manually what software can do more comprehensively, faster and safer for you in seconds

ExecuteSqlCommand possible to access table in another context?

I've built a query that involves joining 2 tables that exist in separate databases. I'd like to run this query within my .NET Core 2.1 application. Here is what I've got:
Query:
INSERT INTO Database2.dbo.Table2
SELECT * FROM Table1
WHERE Col1 = 5
This query works just fine within SQL Operations studio.
C#:
using(var context = ConnectionHelper.getContext(dbInfo))
{
string MySQLQuery =
" INSERT INTO Database2.dbo.Table2 " +
" SELECT * FROM Table1 " +
" WHERE Col1 = 5 ";
try
{
context.Database.ExecuteSqlCommand(MySQLQuery);
}
catch(Exception e)
{
Console.WriteLine(e.Message); // This doesn't get called, the query doesn't throw an error.
}
}
When I run the query through .NET Core, nothing happens. I expect ~1000 rows to be written to Database2.dbo.Table2, but 0 are written. No Error message is logged, so .NET Core seems to think it succeeded in performing the given SQL query. I'm assuming the error is being caused by my reference to Database2.
The solution to the problem was to avoid using ExecuteSqlCommand within a given context. Since I didn't specifically need any features of EF for this query, I ended up using SqlCommand.ExecuteNonQuery() from the System.Data.SqlClient library. Here's a working example:
using (SqlConnection con = new SqlConnection(< your connection string >))
{
con.Open();
SqlCommand command = new SqlCommand();
command.CommandText = " SQL STATEMENT HERE ";
command.Connection = con;
command.ExecuteNonQuery();
}

Why do SQL joins fail in Oracle?

I just try simple joins with C# using oracle db. Should be no big deal. But it ALWAYS fails. It works in MS-Access. Where is the problem ? (OleDb or Odbc makes no difference here, I tried both)
Edit:
Might Oracle version be the problem ? (seems we are using 8.1.7.0.0 and 8.1.5.0.0 modules)
Code:
using System;
using System.Data.Odbc;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string n = Environment.NewLine + "--------------------------------" + Environment.NewLine + Environment.NewLine;
// connect
string connectionString = "dsn=TEST;uid=read;pwd=myPwd";
OdbcConnection connection = new OdbcConnection(connectionString);
connection.Open();
// select (key is actually text not numeral)
string query = "select * from INFOR.ZEITEN where (KEY = 0)";
query = "select a.KEY, b.GREG from INFOR.ZEITEN a inner join INFOR.ZEITEN b on (a.AUSWEIS = b.AUSWEIS) where (a.KEY like '1')";
try
{
query = query.Replace(Environment.NewLine, " ");
Console.WriteLine(n + query);
OdbcCommand command = new OdbcCommand(query, connection);
OdbcDataReader reader = command.ExecuteReader(); // throws exception
if (reader != null)
Console.WriteLine(n + "success, now read with reader!");
}
catch (Exception e)
{
Console.WriteLine(n + e.Message + n + e.StackTrace);
}
// wait
Console.ReadKey();
}
}
}
Output:
And the successful, simple select:
ANSI joins (ex. inner join) were first supported in 9i. You will need to use the old syntax:
select a.KEY, b.GREG
from INFOR.ZEITEN a,
INFOR.ZEITEN b
where (a.AUSWEIS = b.AUSWEIS)
and (a.KEY like '1')
Note that the like operator is equivalent to = in this case, but you probably know that
I think the KEY is numeric then you can't use LIKE. It is because the WHERE KEY = 0 works fine.
The word key is a reserved word. That means that it is a very poor choice for an identity. You need to escape it with a double quote. This might work:
query = "select a.\"KEY\", b.GREG
from INFOR.ZEITEN a inner join
INFOR.ZEITEN b
on (a.AUSWEIS = b.AUSWEIS)
where (a.\"KEY\" like '1')";
I am guessing the \" will work in this context, but there might be another method to insert this character.
What's the error? Could you edit your question and add the actual error the system's throwing at you?
Firstly, I would personally recommend using the ODP .NET (Oracle Data provider for .NET). You can download the latest version for Oracle 12c here. Or look it up for the version you need.
ODBC is a very old driver written in C and works using the native Windows RPC technique.
For full .NET support you're better off with ODP .NET.
Secondly, check if you have any constraints on the tables that's causing the sql to fail.

How can Jdbc template be used for UPDATE query with IN clause?

I've gone through various forums to handle the IN clause using spring's namedParamJdbcTemplate but i still do not get the stuff I'm exactly looking for.
Below is my issue:
I've the following method:
public void updateBatchTableForStatus(List<Integer> reportShellIds, String scheduleType) {
Map<String,List<Integer>> shellIds = Collections.singletonMap("reportShellIds", reportShellIds);
MapSqlParameterSource parameters = new MapSqlParameterSource();
parameters.addValue("reportShellIds", shellIds, Types.NUMERIC)
parameters.addValue("eventType", scheduleType, Types.VARCHAR);
this.namedParamJdbcTemplate.update(GET_EVENT_METADATA_INFO, parameters);
}
The query refered in above method is as defined below:
public static final String SQL_UPDATE_BATCH_LOOKUP_TABLE_FOR_STATUS_BY_BATCH_IDS = "" +
"UPDATE " +
TABLE_BATCH_REF + " BLK " +
"SET " +
"BLK.EXECUTION_STATUS_CODE = :eventType " +
"WHERE " +
"BLK.BATCH_ID in(:reportShellIds) ";
Datatype for BATCH_ID column is Number(24,0) and for the EXECUTION_STATUS_CODE column Varchar.
I'm using Oracle db.
However, the above method throws a SQL exception.
Can someone pls tell me where I'm wrong and what is the fix for it ?
Many thanks in advance.
Best Regards
LB
You are binding reportShellIds to a Map, but it needs to be a List for Spring to bind it correctly. Perhaps you meant shellIds.values() or the variable reportShellIds?
You can use another method with simple Map<String,Object> and place the array as is into the parameter map
public int update(String sql, java.util.Map<java.lang.String,?> paramMap)