Use stored procedure in Entity Framework (code first) - c#

I use this code to define my stored procedure
CREATE PROCEDURE [dbo].[SP]
(#Country NVARCHAR(20))
AS
BEGIN
SET NOCOUNT ON;
SELECT c.*,O.* from Customers
as c inner join orders O on c.CustomerID=o.CustomerID
where c.Country=#Country
END
and this is my C# code:
IList<Entities.Customer> Customers;
using (var context = new NorthwindContext())
{
SqlParameter categoryParam = new SqlParameter("#Country", "London");
Customers = context.Database.SqlQuery<Entities.Customer>("SP #Country", categoryParam).ToList();
}
Problem is here :
I want to message the data from Orders table and my stored procedure generate this to me. How can I get the Orders data in my C# code? Remember I want to execute this stored procedure only once.

Take a look at Does Entity Framework Code First support stored procedures? and http://blogs.msdn.com/b/wriju/archive/2011/05/14/code-first-4-1-using-stored-procedure-to-insert-data.aspx which talk about executing a stored proc via a DbContext object.
I think perhaps your issue is that you aren't getting the orders back as part of your query? is this correct? If so this is because you are only selecting customers. You need to either create an object of the same schema as you expect to be returned from your query (ie that has both customer and order properties) or select into a dynamic type (eww).
Having said that i strongly recommend doing this in linq instead:
from c in context.Customers.Include(c=>c.Orders)
where c.Country == country
select c;
This is a much better approach as you are using EF for what its designed for and not querying for something which doesn't fit your model

Related

How to get stored procedure parameter types and sizes in Oracle?

To call a stored procedure with output parameters from C#, I need to obtain the datatype and the size of the respective parameters. I am using the Oracle.DataAccess library. How can I get this information from stored procedure metadata on an Oracle database?
Someone gave the tables to query for SQL Server in this answer (How to determine size property for stored procedure output parameters in C# data access layer), however I'm looking to call stored procedures in Oracle.
Here is the query
select a.OBJECT_NAME, data_type, sequence, in_out, data_length, data_precision, Data_scale, radix
from USER_PROCEDURES P inner join USER_ARGUMENTS a on P.OBJECT_NAME = a.OBJECT_NAME
where a.OBJECT_NAME = 'Stored Proc name'
order by sequence
I'd suggest a little modification:
select a.package_name,a.OBJECT_NAME,a.argument_name, a.data_type, a.sequence, a.in_out, a.data_length, a.data_precision, a.Data_scale, a.radix
from USER_ARGUMENTS a
join user_OBJECTS O ON O.OBJECT_ID = A.OBJECT_ID
where a.OBJECT_NAME = 'your stored procname'
and o.OBJECT_TYPE in ('PACKAGE','PROCEDURE')
order by a.object_id,sequence;
If you have a procedure called Get_ID (for example), and you also have a procedure within a package called Get_ID, the object_name for both of them is the same, so you will get the arguments for both the "real" procedure and the procedure within the package. If you want both, then leave the two object types there, if you only want the "real" procedure, then delete 'PACKAGE'

Stored Procedure - Linq to Entities - Support Join (Includes() equivelant)

I'm new to stored procedures. I've created the following simple stored procedure:
USE db
GO
CREATE PROCEDURE dbo.GetSearchResults
#BatchId NVARCHAR(20)
AS
SELECT * FROM dbo.SearchResult sr
LEFT OUTER JOIN dbo.SearchResultItem sri ON sri.SearchResultID = sr.SearchResultID
WHERE sr.BatchID = #BatchId AND (sri.Name = 'sales_rank' OR sri.Name = 'sale_price' OR sri.Name = 'list_price')
GO
I am currently calling this stored procedure in my C# application as follows:
public List<SearchResult> GetSearchResult(string batchId, ModelContainer ctn)
{
var parameters = new object[] { new SqlParameter("#BatchId", batchId)};
List<SearchResult> searchResults = ctn.ExecuteStoreQuery<SearchResult>("exec GetSearchResults #BatchId", parameters).ToList();
return searchResults;
}
This is great, however my only issue is that it does not load the SearchResultItem (child) table, which I also need (see left outer join in stored procedure).
In my edmx model, in the Function Imports section, I have tried setting the stored procedure to return a collection of 'Complex' and 'Entities'. Neither of these have worked.
Could anyone offer any advice?
EDIT - Using Entity Framework 4.0
What you want to do is use multiple select statements within your stored procedure - one for each entity type to return, then use ObjectContext.Translate<T>(...) with a DbReader resulting from the DbCommand execution to import the results into your ObjectContxt/DbContext. The navigation properties will be hooked up automatically. Read this to get you started
Important note: the fields returned by the select statements must match the property names of the entity classes exactly
Update: since you're using EDMX, you can alter the EDMX to do the importing for you (linked page demonstrates this as well)

Stored procedure returns int instead of result set

I have a stored procedure that contains dynamic select. Something like this:
ALTER PROCEDURE [dbo].[usp_GetTestRecords]
--#p1 int = 0,
--#p2 int = 0
#groupId nvarchar(10) = 0
AS
BEGIN
SET NOCOUNT ON;
DECLARE #query NVARCHAR(max)
SET #query = 'SELECT * FROM CUSTOMERS WHERE Id = ' + #groupId
/* This actually contains a dynamic pivot select statement */
EXECUTE(#query);
END
In SSMS the stored procedure runs fine and shows result set.
In C# using Entity Framework it shows returning an int instead of IEnumerable?
private void LoadTestRecords()
{
TestRecordsDBEntities dataContext = new TestRecordsDBEntities();
string id = ddlGroupId.SelectedValue;
List<TestRecord> list = dataContext.usp_GetTestRecords(id); //This part doesn't work returns int
GridView1.DataSource = list;
}
Generated function for usp_GetTestRecords
public virtual int usp_GetTestRecords(string groupId)
{
var groupIdParameter = groupId != null ?
new ObjectParameter("groupId", groupId) :
new ObjectParameter("groupId", typeof(string));
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction("usp_GetTestRecords", groupIdParameter);
}
I get this when I have a stored procedure that includes an "exec" call into a temporary table, such as:
insert into #codes (Code, ActionCodes, Description)
exec TreatmentCodes_sps 0
It appears that Entity Framework gets confused as to what should be returned by the procedure. The solution I've come across is to add this at the top of the sproc:
SET FMTONLY OFF
After this, all is well.
I got the same problem, and found solution here
Move to your .edmx
At Model Browser Window/Function Imports find your procedure then double click it
Change the return type to you want
Save .edmx and check the return type again.
It should be what you need now.
Entity Framework can't tell what your stored procedure is returning. I've had success creating a table variable that mirrors the data from your SELECT statement. Just insert into the table variable then do a select from that table variable. EF should pick it up.
See Ladislav Mrnka's answer in this Stack Overflow post
https://stackoverflow.com/a/7131344/4318324
I had the same basic problem.
Adding
SET FMTONLY OFF
To a procedure you are trying to import during the import will address this problem.
It's a good practice to remove the line afterwards unless the purpose of the database is solely to provide schema for EF (Entity Framework).
The main reason for caution is that EF uses this setting to prevent data mutations when trying to obtain metadata.
If you refresh your entity model from a database any procedures with this line in them can potentially update the data in that database just by trying to obtain the schema.
I wanted to add a further note on this so it's not needed to fully scan through the other link.
if you want to try to use FMTONLY here are a couple things to keep in mind.
when FMTONLY is on:
1) only the schema is returned (no) rows.
similar to adding a blanket false statement to your where clause (ie "where 1=0")
2) flow control statements are ignored
Example
set fmtonly on
if 1=1
begin
select 1 a
end
else
begin
select 1 a,2 b
end
while 1=1
select 1 c
The above returns NO rows whatsoever and the metadata for each of the three queries
For this reason some people suggest toggling it off in a way that takes advantage of it's non-observance of flow control
if 1=0
begin
set fmtonly off
end
In fact you could use this to introduce logic that tracks this
set fmtonly off
declare #g varchar(30)
set #g = 'fmtonly was set to off'
if 1=0
begin
set fmtonly off
set #g = 'fmtonly was set to on'
end
select #g
Think VERY CAREFULLY before trying to use this feature as it is both deprecated and potentially makes sql extremely hard to follow
the MAIN concepts that need to be understood are the following
1. EF turns FMTONLY on to prevent MUTATING data from executing stored procedures
when it executes them during a model update.
(from which it follows)
2. setting FMTONLY off in any procedure that EF will attempt to do a schema scan
(potentially ANY and EACHONE) introduces the potential to mutate database
data whenever *anyone* attempts to update their database model.
Entity Framework will automatically return a scalar value if your stored procedure doesn't have a primary key in your result set. Thus, you'd have to include a primary key column in your select statement, or create a temp table with a primary key in order for Entity Framework to return a result set for your stored procedure.
I had the same problem, I changed the name of return fields by 'AS' keyword and addressed my problem. One reason for this problem is naming column names with SQL Server reserved keywords.
The example is fallows:
ALTER PROCEDURE [dbo].[usp_GetProducts]
AS
BEGIN
SET NOCOUNT ON;
SELECT
, p.Id
, p.Title
, p.Description AS 'Description'
FROM dbo.Products AS p
END
Best solution I found is to cheat a little.
In the store procedure, comment everything, put a first line with a select [foo]='', [bar]='' etc...
Now update the model, go to the mapped function, select complex type and click on Get Column Information and then Create Complex Type.
Now comment the fake select and un-comment the real store procedure body.
When you generated your model class for your stored procedure, you chose scalar return result by mistake. you should remove your stored procedure from your entity model, then re-add the stored procedure. In the dialog for the stored procedure, you can choose the return type you are expecting. Do not just edit the generated code.. this may work now, but the generated code can be replaced if you make other changes to your model.
I have pondered this a bit and I think I have a better/simpler answer
If you have a complex stored that gives entity framework some difficultly (for current versions of Entity Framework that are using the FMTONLY tag to aquire schema)
consider doing the folowing at the beginning of your stored procedure.
--where [columnlist] matches the schema you want EF to pick up for your stored procedure
if 1=0
begin
select
[columnlist]
from [table list and joins]
where 1=0
end
if you are okay loading your result set into a table variable
you can do the following to help keep your schema in sync
declare #tablevar as table
(
blah int
,moreblah varchar(20)
)
if 1=0
begin
select * from #tablevar
end
...
-- load data into #tablevar
select * from #tablevar
If you need to do this, then you might be better off just making a partial of the dbcontext and creating the C# function yourself that will use SqlQuery to return the data you need. Advantages over some of the other options is:
Don't have to change anything when the model updates
Won't get overwritten if you do it directly in the generated class (someone above mention this as if it's an option :) )
Don't have to add anything to the proc itself that could have side effects now or later on
Example Code:
public partial class myEntities
{
public List<MyClass> usp_GetTestRecords(int _p1, int _p2, string _groupId)
{
// fill out params
SqlParameter p1 = new SqlParameter("#p1", _p1);
...
obj[] parameters = new object[] { p1, p2, groupId };
// call the proc
return this.Database.SqlQuery<MyClass>(#"EXECUTE usp_GetTestRecords #p1, #p2, #groupId", parameters).ToList();
}
}
Just change to
ALTER PROCEDURE [dbo].[usp_GetTestRecords]
--#p1 int = 0,
--#p2 int = 0
#groupId nvarchar(10) = 0
AS
BEGIN
SET NOCOUNT ON;
SELECT * FROM CUSTOMERS WHERE Id = #groupId
END
I know this is an old thread but in case someone has the same problems I'll tell my woes.
As a help to find the issue, run sql profiler when you add your stored proc. Then you can see what entity framework is passing as parameters to generate your resultset. I imagine nearly always it will pass null parameter values. If you are generating sql on the fly by concatenating string values and parameter values and some are null then the sql will break and you wont get a return set.
I haven't needed to generate temp tables or anything just an exec command.
Hope it helps
During import
SET FMTONLY ON
can be used for taking the sp schema.
If you change the sp and want to update the new one, you should delete the old defined function from edmx file (from xml), because although deleting sp from model browser, it is not deleted in edmx. For example;
<FunctionImport Name="GetInvoiceByNumber" ReturnType="Collection(Model.Invoice_Result)">
<Parameter Name="InvoiceNumber" Mode="In" Type="Int32" />
</FunctionImport>
I had the same problem, and when I delete the FuctionImport tag of corresponding sp totally, the model updated right. You can find the tag by searching the function name from visual studio.
You may have luck opening up the model browser, then going to Function Imports, double clicking the stored procedure in question and then manually clicking "Get Column Information" and then clicking "Create New Complex Type". This usually sorts out the problem.
Well I had this issue as well but after hours of online searching none of above methods helped.
Finally I got to know that It will happen if your store procedure is getting some parameters as null and which generate any error in query execution.
Entity Framework will generate method for store procedure by defining the complex entity model. Due to that null value your store procedure will return and int value.
Please check your store procedure either its providing empty result set with null values. It will fix your problem. Hopefully.
I think this is a problem of permissions on the database, I don't know what exactly could be, but, in my job we use Active Directory users to grant applications connect to databases, this accounts are specially created for the applications, each app has its own user account, well, as a developers I have permissions for read, write and other basic things, no alter, and no advanced features, and I have this same problem running Visual Studio with my normal account, then, what I did was to open Visual Studio selecting the option "as a different user" on the context menu, and I put the AD login granted for the application and voila!, now my Stored Procedures are loading with all the fields I was expected, before that, my Stored Procedures was returning as int. I hope this help someone, maybe the VIEW DEFINITION permissions on database account do the trick
If SQL Authentication is in place, verify that the user credential that is being used to connect Entity Framework to the database has the proper rights to read from CUSTOMERS table.
When Entity Framework uses SQL Authentication to map complex objects (i.e stored procedures that SELECTs more than one column), if any of the tables from within such stored procedure don't have set up the Read permission, the mapping will result in returning INT instead of the desired Result set.

linq to stored procedures multiple select

MSDN docs say that I can write a stored procedure like this
CREATE PROCEDURE MultipleResultTypesSequentially
AS
select * from products
select * from customers
then read it from LINQ like this
IMultipleResults sprocResults =
db.MultipleResultTypesSequentially();
// First read products.
foreach (Product prod in sprocResults.GetResult<Product>())
{
Console.WriteLine(prod.ProductID);
}
// Next read customers.
foreach (Customer cust in sprocResults.GetResult<Customer>())
{
Console.WriteLine(cust.CustomerID);
}
what if one of my select statements return something other then a regular table object - with a join or just selecting certain columns?
how do I let LINQ know that I want to read the next SELECT ??? basically , what I'm wondering is this example from MSDN reading Products first then Customers because they are written in that order in the stored procedure , or is writing .GetResult<Customer>() telling c# to find the result that maps to type Customer? and also what would the foreach loop look like for this unknown type?
I did a quick test with a similar stored procedure and found that if your foreach loops are not in the correct order, an InvalidOperationException is thrown, so it doesn't look like C# is able to find the correct result based on the type used in GetResult<>.
As for your select statement returning something other than a table, if you drag a stored procedure from the Server Explorer onto the Linq to Sql designer, the designer will autogenerate a class based on procedures output. I created a procedure with a couple of joined tables, neither of which existed in my project, and Linq to Sql created a type for me, named like StoredProcedureNameResult, so a procedure called GetCreditCard had a type named GetCreditCardResult.

Returning a list from an SqlCommand object - how can I give the database as little work as possible?

I am writing a private method for my class. I am passing as a parameter to this a list of integers, representing the ID of a row in my SQL Server 2008 table.
I wish to return a List<string> of the "Name" (a column) on all rows where one of the passed in integers is equal to an "ID". So if I pass in the List<int> {1, 2, 3 }.
I want to essentially run the commmand (SELECT Name FROM Table WHERE ID = 1 OR ID = 2 OR ID = 3).ToList<string>().
The database I am using is very busy, and thus it is very important that I optimise my solution as much as possible. With this in mind, I am wondering if it would be better practice for me to create a link to this DB using a .dbml file and use Linq to SQL to query the database?
Or simply to create an SQLCommand object, execute it once, iterate over a reader and save it in a List? What is the most optimal way to do this ? Is creating a .dbml file to represent a very busy database bad practice ?
Creating a .dbml has very little do to with server-side performance; that changes the tooling at the calling end - but the server won't really notice the difference between commands coming from .dbml vs hand-coded, at least: not for things this simple (I should note that for complex queries, hand-coded queries can often out-perform machine-generated queries).
In terms of performance at the caller; a .dbml is just a wrapper around all the usual command/reader/etc - it can't make things faster. In some cases it might make it slower, if it doesn't do a good job of parsing an expression, or doesn't cache the parsed outcome (in terms of the TSQL).
What I will say, though, is that dapper will handle this very nicely for you:
var ids = new List<int>{1,2,3};
var names = conn.Select<string>("select Name from Table where ID in #ids",
new {ids}).ToList();
dapper will spot the in #ids usage, and will expand that as parameters, executing:
select Name from Table where ID in (#p__0, #p__1, #p__2)
(or something like that) - passing 1, 2 and 3 as those values.
That gives you:
convenience at the caller
performance at the caller (dapper is heavily optimized)
full parameterization
allowing for optimal query-plan re-use at the server
More generally, dapper will also happily handle general entity mapping, for example:
int id = 12345;
var customer = conn.Select<Customer>("select * from Custom where Id = #id",
new { id }).Single();
Several things I would do:
A. Use a table valued parameter
CREATE TYPE LocationTableType AS TABLE
( ID INT);
GO
B. Use a stored procedure (with your TVP)
CREATE PROCEDURE dbo. usp_GetLocationNames
#TVP LocationTableType READONLY
AS
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT Name
FROM dbo.Location l
JOIN #TVP t ON l.ID = t.ID
C. Allow dirty reads - SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
D. Don't count rows - SET NOCOUNT ON;
E. Cache the resultset for a period of time
Since I know very little about your application or your situation, these items are 'generally' what I would do with most procs. Obviously, if you were checking someone's bank account balance before dispensing cash you would not allow dirty reads, nor cache the resultset. But in most situations, these things are acceptable.
In order to limit the traffic is better to reduce the number of roundtrip to the database, so is betetr if you issue just one command, maybe using the IN clause instead of multiple or and parametrize your query.
If you select by numeric IDs then it is safe to form WHERE clause dynamically (i.e. WHERE ID IN (1,2,3,...)
More advanced way is to create SP with XML parameter. Sample code snippet:
DECLARE #xmlIds AS XML
SET #xmlIds = '<Ids><ID>1</ID><ID>2</ID></Ids>'
SELECT Name FROM Table
WHERE ID IN (
SELECT
Data.row.value('.', 'INT')
FROM
#XmlIds.nodes('/Ids/ID') As Data(row))
You can do all of this natively using native C# and Sql 2008.
In Sql 2008 it introduced User-Defined Table Types, and Table-Valued Parameters for Stored Procedures.
So the following would give you exactly what you want,
CREATE TYPE UdtId AS TABLE
(
[ID] INT NOT NULL
PRIMARY KEY NONCLUSTERED ([ID] ASC)
)
CREATE PROCEDURE spGetCustomerByIds
#IDS UdtId READONLY
AS
BEGIN
SELECT
C.*
FROM Customer C
INNER JOIN UdtId I ON
C.ID = I.ID
END
Hopefully the C# code behind it will be obvious, but it would look like,
public foo GetCustomerDataByIds(IEnumerable<int> ids)
{
using (var command = new SqlCommand())
using (var adatper = new SqlAdapter())
using (var dataSet = new DataSet())
{
command.Text = "spGetCustomerByIds";
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("#IDS", GetDataTableOfIds(ids));
// execute and return the stuff you're after
}
}
private DataTable GetDataTableOfIds(IEnumerable<int> ids)
{
var table = new DataTable();
table.Columns.Add(new DataColumns("ID", typeof(int));
foreach (var id in ids)
{
table.Rows.Add(id);
}
return table;
}

Categories

Resources