Can not find out the function for stored procedure in Entity Framework - entity-framework

Based on a database myDB, I generate edmx for all table and compile the project. Then I create stored procedure myProc in myDB. Then I update the model by "Update Model from database" in the node Stored Procedure and add myProc. It is fine. Then "Create a function import" on myProc. It is fine. Then I compiled the project, it is fine.
The return type for this import function is scalars(string) because myProc return a string.
Then I want to use this function for this stored procedure, but I can find out the function.
How to find out the matching function and call it in code?

In EF 3.5 only functions that return Entities show up in ObjectServices.
I.e. importing pulls the Function into the Conceptual Model, but NOT into the code-generation.
We have addressed this problem in 4.0.
In the meantime you can call the function using eSQL.
i.e. something like this (pseudo code):
EntityConnection connection = ctx.Connection as EntityConnection;
//Open the connection if necessary
connection.Open()
//Create the command
EntityCommand command = new EntityCommand();
command.CommandText = "Function(#p1,#p2");
command.Parameters.Add(...);
command.Parameters.Add(...);
command.Connection = connection;
string s = command.ExecuteScalar().ToString();
Hope this helps
Alex

Related

Is there a database agnostic way to execute a stored procedure with params

Using Entity Framework Core 2.1 I have code that calls a stored procedure, passing in some params. During execution we are using Sql Server and everything works as expected. Our test cases however, run using Sqlite. Is there a decent way (without overly polluting the code with unwanted code added only to support testing) to get the code to run regardless of which database is actually being used?
Currently the code in question looks like this:
await MyContext.Database.ExecuteSqlCommandAsync("UpdateEndDate #p0, #p1, #p2", id, from, thru);
And causes a SqliteException:
Microsoft.Data.Sqlite.SqliteException
HResult=0x80004005
Message=SQLite Error 1: 'near "UpdateEndDate": syntax error'.
Source=Microsoft.Data.Sqlite
...
(Also wondering why are there SqlParameter and SQLiteParameter? Why cant there be a database agnostic parameter class?)
Configuration for DBContext in test harness is below:
public static SqliteConnection GetConnection()
{
SqliteConnection connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
connection.CreateFunction(
"NEWID",
Guid.NewGuid(),
g => g);
return connection;
}
serviceProvider = new ServiceCollection()
.<snipped>
.AddEntityFrameworkSqlite()
.AddDbContext<MyContext>(
(theServiceProvider, opt) => opt
.UseInternalServiceProvider(theServiceProvider)
.UseSqlite(GetConnection()))
.BuildServiceProvider();
MyContext cc = serviceProvider.GetService(typeof(MyContext)) as MyContext;
cc.Database.EnsureCreated();
Have you considered using the DbProviderFactory class? It became available in Core 2.1. Put both connection strings in your appSettings.json and in the startup set appropriate DbContext based on the environment it's running in.
https://learn.microsoft.com/en-us/dotnet/api/system.data.common.dbproviderfactory?view=netcore-2.1

Entity SQL doesn't work if edmx file in another project

I'm trying to use Enitity SQL to query data, but if the edmx file in another project, there will be an exception thrown. Below is my test steps.
Create a Class Library project and add an edmx file to it, create from database.
Create a Console Application, add the Class Library project to reference and copy the app.config file to this project.
Write the code as below
using (NorthwindEntities context = new NorthwindEntities())
{
string queryString = #"SELECT VALUE cus
FROM NorthwindEntities.Customers AS cus
WHERE cus.ID > 10";
ObjectQuery<Customers> cusQuery =
context.CreateQuery<Customers>(queryString);
List<Customers> cusList = cusQuery.ToList();
}
When I run the Console Application project, an exception is thrown: "'ID' is not a member of type 'NorthwindModel.Customers' in the currently loaded schemas."
It seems the schema doesn't loaded into the project, anyone has ideas?
Addional question: in this query, I select all the properties of this type, if I only select some of the properties, how to return an anonymous type of ObjectQuery?
Any suggestions are appreciate.
This is not an EF problem.
You have written the SQL statement by hand, you are not using the table definition that is in the EDMX file.
If you try to execute the SQL statement in the Query prompt of SQL Server, you will see that it fails there as well.
Try something like this:
var cus = from customers in context.Customers select customers;
var cusList = cus.ToList();

How can I override SQL scripts generated by MigratorScriptingDecorator

Using Entity Framework 4.3.1 Code first, and Data Migrations.
I have written a utility to automatically generate the Migration scripts for a target database, using the MigratorScriptingDecorator.
However, sometimes when re-generating the target database from scratch, the generated script is invalid, in that it declares a variable with the same name twice.
The variable name is #var0.
This appears to happen when there are multiple migrations being applied, and when at least two result in a default constraint being dropped.
The problem occurs both when generating the script form code, and when using the Package Manager console command:
Update-Database -Script
Here are the offending snippets form the generated script:
DECLARE #var0 nvarchar(128)
SELECT #var0 = name
FROM sys.default_constraints
WHERE parent_object_id = object_id(N'SomeTableName')
and
DECLARE #var0 nvarchar(128)
SELECT #var0 = name
FROM sys.default_constraints
WHERE parent_object_id = object_id(N'SomeOtherTableName')
I would like to be able to override the point where it generates the SQL for each migration, and then add a "GO" statement so that each migration is in a separate batch, which would solve the problem.
Anyone have any ideas how to do this, or if I'm barking up the wrong tree then maybe you could suggest a better approach?
So with extensive use of ILSpy and some pointers in the answer to this question I found a way.
Details below fo those interested.
Problem
The SqlServerMigrationSqlGenerator is the class ultimately responsible for creating the SQL statements that get executed against the target database or scripted out when using the -Script switch in the Package Manager console or when using the MigratorScriptingDecorator.
Workings
Examining the Genearate method in the SqlServerMigrationSqlGenerator which is responsible for a DROP COLUMN, it looks like this:
protected virtual void Generate(DropColumnOperation dropColumnOperation)
{
RuntimeFailureMethods
.Requires(dropColumnOperation != null, null, "dropColumnOperation != null");
using (IndentedTextWriter indentedTextWriter =
SqlServerMigrationSqlGenerator.Writer())
{
string value = "#var" + this._variableCounter++;
indentedTextWriter.Write("DECLARE ");
indentedTextWriter.Write(value);
indentedTextWriter.WriteLine(" nvarchar(128)");
indentedTextWriter.Write("SELECT ");
indentedTextWriter.Write(value);
indentedTextWriter.WriteLine(" = name");
indentedTextWriter.WriteLine("FROM sys.default_constraints");
indentedTextWriter.Write("WHERE parent_object_id = object_id(N'");
indentedTextWriter.Write(dropColumnOperation.Table);
indentedTextWriter.WriteLine("')");
indentedTextWriter.Write("AND col_name(parent_object_id,
parent_column_id) = '");
indentedTextWriter.Write(dropColumnOperation.Name);
indentedTextWriter.WriteLine("';");
indentedTextWriter.Write("IF ");
indentedTextWriter.Write(value);
indentedTextWriter.WriteLine(" IS NOT NULL");
indentedTextWriter.Indent++;
indentedTextWriter.Write("EXECUTE('ALTER TABLE ");
indentedTextWriter.Write(this.Name(dropColumnOperation.Table));
indentedTextWriter.Write(" DROP CONSTRAINT ' + ");
indentedTextWriter.Write(value);
indentedTextWriter.WriteLine(")");
indentedTextWriter.Indent--;
indentedTextWriter.Write("ALTER TABLE ");
indentedTextWriter.Write(this.Name(dropColumnOperation.Table));
indentedTextWriter.Write(" DROP COLUMN ");
indentedTextWriter.Write(this.Quote(dropColumnOperation.Name));
this.Statement(indentedTextWriter);
}
}
You can see it keeps track of the variables names used, but this only appears to keep track within a batch, i.e. a single migration. So if a migratin contains more than one DROP COLUM the above works fine, but if there are two migrations which result in a DROP COLUMN being generated then the _variableCounter variable is reset.
No problems are experienced when not generating a script, as each statement is executed immediately against the database (I checked using SQL Profiler).
If you generate a SQL script and want to run it as-is though you have a problem.
Solution
I created a new BatchSqlServerMigrationSqlGenerator inheriting from SqlServerMigrationSqlGenerator as follows (note you need using System.Data.Entity.Migrations.Sql;):
public class BatchSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
protected override void Generate
(System.Data.Entity.Migrations.Model.DropColumnOperation dropColumnOperation)
{
base.Generate(dropColumnOperation);
Statement("GO");
}
}
Now to force the migrations to use your custom generator you have two options:
If you want it to be integrated into the Package Manager console, add the below line to your Configuration class:
SetSqlGenerator("System.Data.SqlClient",
new BatchSqlServerMigrationSqlGenerator());
If you're generating the script from code (like I was), add a similar line of code to where you have your Configuration assembly in code:
migrationsConfiguration.SetSqlGenerator(DataProviderInvariantName,
new BatchSqlServerMigrationSqlGenerator());

Create and User User Define Function in edmx

i want to create one function in edmx which return scaler value,
how to create it in SSDL and how to access it in code?
One problem you have is your SSDL is automatically generated by the 'EntityModelGenerator', so editing it will be wiped out by a rebuild. Your edits need to be done in th EDMX file.
So firstly, you have to decide is (1) your return value a calculation of sorts (i.e. adding values together in the application, rather than at database level), or (2) is it a direct call to a database stored procedure?
(1) First step is to add the function XML definition into your EDMX file:
<Function Name="LineTotal" ReturnType="decimal">
<Parameter Name="lineTotal" Type="MyDbModel.OrderDetail">
<DefiningExpression>
od.Price * od.Quantity
</DefiningExpression>
</Parameter>
</Function>
Now, although your EDMX knows about this function, your IntelliSense won't. So you have to add some code to make this work. It is a best practice to place these functions in a seperate class.
public class ModelDefinedFunctions
{
[EdmFunction("MyDbModel" , "LineTotal")] //Model Name and Function Name
public static decimal LineTotal(OrderDetail od)
{
throw new NotSupportedException("LineTotal cannot be directly used.");
}
}
Entity Framework will know to redirect this function call to the EDMX instead. Any direct call to this method, where the model does not exist will throw an exception.
You can then call it in your LINQ queries like
var productValues = from line in model.OrderDetails
select new
{
od.ProductID,
od.Price,
od.Quantity,
LineTotal = ModeDefinedFunctions.LineTotal(line)
};
(2) If you are adding a stored procedure directly, it is easier to drag and drop it onto the EDMX designer. There is a [FunctionImport()] attribute, but I haven't used it. You can drag and drop and see what code it generates in the EDMX file?
Alternatively, you can call the model.ExecuteCommand(<spname> , params object[] values
) stored procedure execution method.

How do I call a stored procedure from Jasper Report?

How do I call a stored procedure from Jasper Report?
The JasperReports Ultimate Guide contains this information about using store procedure:
Certain conditions must be met to put stored procedure calls in the SQL query string of a report template:
The stored procedure must return a java.sql.ResultSet when called through JDBC.
The stored procedure cannot have OUT parameters.
Below are the steps to call a stored procedure to build a report using iReport 4.5/4.5.1 JasperReport and using Oracle Express database.... hope this helps....
1.In your iReport designer go to Tools --> Options --> and in the Classpath Tab click Add JAR and add the OJDBC14.jar to the classpath.
2.Go to Query Executer tab and set the following Language: plsql Query Executer Factory: com.jaspersoft.jrx.query.PlSqlQueryExecuterFactory Fields Provider Class: com.jaspersoft.ireport.designer.data.fieldsproviders.SQLFieldsProvider
3.Select Database JDBC Connection
4.Select Oracle as the JDBC Driver as shown in the image below and verify the connection by clicking the Test button (Make sure you check the Save Password check box)
5.Create a blank report by giving a report name and save it.
6.Open the report in the designer and right click on the report name and click on Edit Query
7.Set the query language to plsql
8.Call your procedure with in { }
{call PUBLISHER_AND_BOOKS(&P(P_PUBLISHER_ID), &P(ORACLE_REF_CURSOR))}
Note: P_PUBLISHER_ID is of type string and ORACLE_REF_CURSOR is of type java.sql.ResultSet data type custom parameter. You can create this by clicking the New Parameter button. If you have more input parameters use ‘,’ as your delimiter as shown in the above example.
9.Click Ok and proceed with the report design.
10.In the designer window right click on Fields and add click Add Fields and make sure all the field name matches the column name in your stored procedure
11.Now right click on the parameters and add the parameters matching the stored procedure NOTE: make sure you uncheck the "Use for Prompt" in the property for your out parameter in our example its ORACLE_REF_CURSOR is the out parameter.
12.Drag and drop the fields in the report detail band as shown below
13.Click preview to run the report you will be prompted with the input parameter
All the steps are captured in detail with images and available in the below link hope this helps...
http://meezageekyside.blogspot.com/#!/2012/04/jasper-reports-ireport-45-using-oracle.html
<queryString>
<![CDATA[Call procedure_name ($P{parm1},$P{parm2},"$P!{parm3}","$P!{parm4}","$P!{parm5}",$P{parm6},$P{parm7});]]>
</queryString>
With MySQL You can call the stored procedure just like you call any other query, using the queryString.
Here is how i tackled the issue, you can easily do this by using a JR scriptlet (java bean).
Below is the sample java code and once you have the jar file add it to the jasper report
classpath and reference the same on your report properties.
-- Please make sure you use identicle names as given in your report when accessing variable/parameter
values and in setting Variable values (you can't set parameter values, just the vaiables)
package com.scriptlets;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Date;
import net.sf.jasperreports.engine.JRDefaultScriptlet;
import net.sf.jasperreports.engine.JRScriptletException;
public class Icdf extends JRDefaultScriptlet {
public void afterReportInit() throws JRScriptletException {
// get the current connection from report via parameters
Connection conn = (Connection) this
.getParameterValue("REPORT_CONNECTION");
int userId = 100; //use this.get__ to access from report
try {
if (conn != null)
callOracleStoredProcOUTParameter(conn, userId); // SP call
} catch (SQLException e) {
e.printStackTrace();
}
}
private void callOracleStoredProcOUTParameter(Connection conn, int userId)
throws SQLException {
CallableStatement callableStatement = null;
String getDBUSERByUserIdSql = "{call someStoredProcedureName(?,?,?)}";
try {
callableStatement = conn.prepareCall(getDBUSERByUserIdSql);
// setting parameters of the callablestatement
callableStatement.setInt(1, userId);
callableStatement.registerOutParameter(2, java.sql.Types.VARCHAR);
callableStatement.registerOutParameter(3, java.sql.Types.DATE);
// execute getDBUSERByUserId store procedure
callableStatement.executeUpdate();
// get the required OUT parameters from the callablestatement
String userName = callableStatement.getString(2);
Date createdDate = callableStatement.getDate(3);
// --just to check, you can view this on iReport console
System.out.println("UserName : " + userName + "CreatedDate : " + createdDate);
// set the values to report variables so that you can use them in
// the report
this.setVariableValue("variable_name1", userName);
this.setVariableValue("variable_name2", createdDate);
} catch (SQLException e) {
e.printStackTrace();
} catch (JRScriptletException e) {
e.printStackTrace();
}
}
}
very easy:
1. At tab outline. Right click to file jasper --> select Dataset & Query ...
2. Select DB, language plsql, & call with query follow
Exp:
{call packageName.procedureName( $P{a}, $P{b}, $P{c}, $P{d}) }
Good luck!
JasperReport doesn't support calling stored procedure/function directly from his SQL datasource. The best way I found to overcome that limitation is to create a Java bean that calls the stored procedure (via JDBC or Hibernate) and returns a collection of objects which represent the result set. If you're using iReport, just change the Data Source Expression to use that Java bean. There's good section on data sources in the (not free) iReport manuals.
Two possibilities for calling a procedure from Jasper reports,
If your procedure returns some result set and if you would like to
use in the report, then you have to create a function, type and type
table (for holding the result set) for calling the procedure.
If the procedure does some DML operation, then you can call directly
(without a function)
In jasper studio(I'm using v5.5.1) you can call sp like this:
Select SQL as language
just Exec your sp and pass parameters to it
here is my sp that take to DateTime parameters and returns a set of result
EXEC dbo.SP_Report #p1=$P{date_from}, #p2=$P{date_to}
In jasper report using queryString we can call a procedure like this
example:
<![CDATA[{call PACKAGE.PROCEDURENAME($P{PARAM_NAME1},$P{PARAM_NAME2},$P{PARAM_NAME3},$P{PARAM_NAME4},$P{PARAM_NAME5},$P{PARAM_NAME6},$P{PARAM_NAME7},$P{OUT_PARAM_NAME8})}]]>
We can pass IN as well as OUT paramters and If you use cursor as out parameter and parameter class should be resultset(java.sql.ResultSet)