I'm using Entity Framework v6. I have a stored procedure as shown below
CREATE PROCEDURE [dbo].[GetCountryList]
(
#CustomerName VARCHAR(MAX),
#SearchCriteria VARCHAR(MAX)
)
AS
BEGIN
SET NOCOUNT ON
SELECT CountryID, CountryName FROM dbo.Table1
WHERE CustomerName = #CustomerName AND CountryName = #SearchCriteria
END
Now I have a model class
public class CountryName
{
public int CountryId { get; set; }
public string CountryName { get; set; }
}
So I want to get the result of the SELECT query in a List<CountryName> type
List<CountryName> countryList = null;
using (DbEntities dbContext = new DbEntities())
{
countryList = //my code to collect the result
}
Well, I could have run a LINQ to SQL directly on the table but unfortunately my requirement in to get the data from stored procedure. So, how can I do it?
You need to Import the stored procedure as a Function. Right-click on the workspace area of your Entity model and choose Add -> Function Import.
In the Add Function Import dialog, enter the name you want your stored procedure to be referred to in your model for example GetCountryListSP, choose your procedure from the drop down list, and choose the return value of the procedure to be Entities and choose CountryName from the drop down list.
Then in the code:
var result = db.GetCountryListSP();//Send parameters too
With this approach you prevent returning -1 of the stored procedure. Please check this post for more details about stored procedure problem with Entity Framework.
You can do it without importing. Something like that:
var countryList = dbContext.Database.SqlQuery<CountryName>("[GetCountryList]").ToList();
EntityFramework sometimes won't recognize or import SPs ))) So, that's why I saving my hours with this snippet.
Related
Here is a truncated example of what I'm trying to do:
var stuffTOSave = new List<SomeObject> {
public int OtherTableId { get; set; }
public List<Guid> ComponentIds { get; set; }
};
var sql = #"CREATE TABLE Components( ComponentId uniqueidentifier PRIMARY KEY )
INSERT INTO Components VALUES (#WhatGoesHere?)
SELECT * FROM OtherTable ot
JOIN Components c on c.ComponentId = ot.ComponentId
WHERE Id = #OtherTableId
DROP TABLE Components"
Connection.Execute(sql, stuffToSave);
I know from other SO questions that you can pass a list into an insert statement with Dapper, but I can't find any examples that pass a list as well as another parameter (in my example, OtherTableId), or that have a non-object list (List<Guid> as opposed to a List<SomeObject> that has properties with names to reference).
For the second issue, I could select the ComponentIds into a list to give them a name like:
stuffToSave.ComponentIds.Select(c => new { ComponentId = c })
but then I'm not sure what to put in my sql query so that dapper understands to get the ComponentId property from my list of ComponentIds (Line 7)
I would still like to know the real way of accomplishing this, but I have this workaround that uses string interpolation:
var sql = $#"CREATE TABLE Components( ComponentId uniqueidentifier PRIMARY KEY )
INSERT INTO Components VALUES ('{string.Join($"'),{Environment.NewLine}('", request.ComponentIds)}')
SELECT * FROM OtherTable ot
JOIN Components c on c.ComponentId = ot.ComponentId
WHERE Id = #OtherTableId
DROP TABLE Components"
I'm not worried about SQL Injection since this is just interpolating a list of Guids, but I'd rather avoid this method if possible.
I have below stored procedure :-
CREATE PROCEDURE [dbo].[DELETE_DATA_BY_TIMESTAMP]
#NUMBER_OF_DAYS_BEFORE int
AS
BEGIN
IF OBJECT_ID('dbo.TableFileID', 'U') IS NOT NULL
DROP TABLE TableFileID;
select FileID into TableFileID from
[dbo].[OUTPUT_JSON_LOG]
where OutJsonStatus in ('Success' , 'Failed')
and convert(date,CreatedOn)<convert(date,getdate()-#NUMBER_OF_DAYS_BEFORE)
DELETE FROM OUTPUT_JSON_LOG
.... Some DML Queries .....
select * from TableFileID
END
I want to get all the list of FileIds from select query in procedure :-
select * from TableFileID
I updated Entity framework edmx file.
in designer I can see function as :-
Public ObjectResult<Nullable<global::system.Int32>> DELETE_DATA_BY_TIMESTAMP(...)
{
....
....
return base.ExecuteFunction<Nullable<global::system.Int32>>("DELETE_DATA_BY_TIMESTAMP",..);
}
When I am calling this function :-
var FileIds=context.DELETE_DATA_BY_TIMESTAMP(...);
return FileIds.ToList();
It always shows count 0 for list.
But internally it processes all fileIds.
How can I get this list of fileIds with above procedure.
I'm using Entity Framework 6.1.3 and have used the database-first approach to let it generate the model files and the .EDMX. I also have the following stored procedure on SQL Server 2008 R2, which Entity Framework brought into the EDMX:
CREATE PROCEDURE [dbo].[FindEmployee]
#LastName nvarchar(50),
#employeeID nvarchar(50),
#securityID nvarchar(50)
AS
BEGIN
SET NOCOUNT ON;
select *
from Employee
where
(lastName = dbo.trim(#LastName) AND dbo.trim(#LastName) IS NOT NULL)
OR (employeeID = dbo.trim(#employeeID) AND dbo.trim(#employeeID) IS NOT NULL)
OR (securityID = dbo.trim(#securityID) AND dbo.trim(#securityID) IS NOT NULL)
order by
case when dbo.trim(#LastName) is not null then CONVERT(char(50), lastName) + CONVERT(char(50), firstName)
when dbo.trim(#employeeID) is not null then employeeID
when dbo.trim(#securityID) is not null then securityID
end
END
In a Windows WPF app, I let the user select the column to search on (lastName, employeeID, or securityID) from a combobox. The user provides a search value which will get plugged into that parameter in the call to the stored procedure. The stored procedure then returns a resultset from its SELECT which I'll use to populate a DataGrid.
I'm trying to call the stored procedure in this code; Note that the FindEmployee_Result is an auto-generated class in the EDMX for the stored procedure:
public FindEmployee_Result SearchEmployees(string lastName, string employeeID, string securityID)
{
var results = new FindEmployee_Result();
using (var ctx = new TestSelectionEntities())
{
results = ctx.FindEmployee(lastName,employeeID,securityID);
}
return results;
}
This code blows up with this error:
Cannot implicitly convert type 'System.Data.Entity.Core.Objects.ObjectResult' to 'TestSelection.data.FindEmployee_Result'
What am I missing? Thank you.
The solution is to use a List, as the stored procedure returns a resultset of FindEmployee_Result objects:
public List<FindEmployee_Result> SearchEmployees(string lastName, string employeeID, string securityID)
{
using (var ctx = new TestSelectionEntities())
{
return ctx.FindEmployee(lastName,employeeID,securityID).ToList();
}
}
Sorry long explanation but short question. For question you can directy look to 2 bold styled text except this.
I created my first EF6 CODE FIRST project to learn new features. I tried to implement new stored procedure based CRUD operations.
One of models entity for demonstration for here like this:
public partial class POCO
{
//identy
public int ID { get; set; }
// SQL Server Timestamp
// this.Property(t => t.RowVersion).IsFixedLength().HasMaxLength(8).IsRowVersion();
public byte[] RowVersion { get; set; }
public int MiscPropery { get; set; }
}
This entity uses SQL server's RowVersion as concurency token, which is "Database Generated Concurency" of course.
First: I created releted stored procedures on my own. I fallowed the EF6 documentation Stored Procedure Mapping
Then: EntityFramework 6 created database and stored procedures.
EntityFramework 6 created insert stored procedure:
CREATE PROCEDURE [dbo].[POCO_Update]
#ID [int],
#RowVersion_Original [rowversion],
#MiscPropery [int]
-- ------- I hope here: #RowsAffected int OUTPUT
AS
BEGIN
UPDATE [dbo].[POCOs]
SET [MiscPropery] = #MiscPropery
WHERE (([ID] = #ID) AND (([RowVersion] = #RowVersion_Original)
OR ([RowVersion] IS NULL AND #RowVersion_Original IS NULL)))
SELECT t0.[RowVersion], t0.[FaturaNo]
FROM [dbo].[POCOs] AS t0
WHERE ##ROWCOUNT > 0 AND t0.[ID] = #ID
END
EntityFramework 6 created delete stored procedure:
CREATE PROCEDURE [dbo].[POCO_Delete]
#ID [int],
#RowVersion_Original [rowversion]
-- ------- I hope here: #RowsAffected int OUTPUT
AS
BEGIN
DELETE [dbo].[POCOs]
WHERE (([ID] = #ID) AND (([RowVersion] = #RowVersion_Original)
OR ([RowVersion] IS NULL AND #RowVersion_Original IS NULL)))
END
My problems came at this point:
IN EntityFramework 6 documentation page, in "Concurrency Tokens" section says: (http://entityframework.codeplex.com/wikipage?title=Code%20First%20Insert%2fUpdate%2fDelete%20Stored%20Procedure%20Mapping)
Concurrency Tokens
Update and delete stored procedures may also need to deal with concurrency:
If the entity contains any concurrency tokens, the stored procedure should have an output parameter named RowsAffected that returns the number of rows updated/deleted.
And gives these samples on that documentation page:
// from documentation page
public class Person
{
public int PersonId { get; set; }
public string Name { get; set; }
[Timestamp]
public byte[] Timestamp { get; set; }
}
CREATE PROCEDURE [dbo].[Person_Update]
#PersonId int,
#Name nvarchar(max),
#Timestamp_Original rowversion,
#RowsAffected int OUTPUT -- ====> !!!!!
AS
BEGIN
UPDATE [dbo].[People]
SET [Name] = #Name
WHERE PersonId = #PersonId AND [Timestamp] = #Timestamp_Original
SET #RowsAffected = ##RowCount
END
But there is no output parameter named RowsAffected in stored procedures created by EF6.
Im a have to change autocreated stored procedures to follow documantation, or documentation is broken? Or everything goes under the curtains.
I want to be able to do the following:
I have a model and inside there I do have an entity.
This entity has the following structure:
public class Client
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
What I want now, is to just get the client name based on the id.
Therefore I wrote a stored procedure which is doing this.
CREATE PROCEDURE [Client].[GetBasics]
#Id INT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT
Name
FROM Client.Client
INNER JOIN Client.Validity ON ClientId = Client.Id
WHERE
Client.Id = #Id;
END
Now, going back to VS, I do update the model from the database with the stored procedure included.
Next step is to map this stored procedure to the client entity as a function import.
This also works fine.
Trying now to load one client's name results into an error during runtime...
"The data reader is incompatible with
the specified 'CSTestModel.Client'. A
member of the type, 'Id', does not
have a corresponding column in the
data reader with the same name."
I am OK with the message. I know how to fix this (returning as result set Id, Name, Description).
My idea behind this question is the following:
I just want to load parts of the entity, not the complete entity itself.
I have a restriction here to just use stored procedures for the entire communication towards/from the database.
Is there a solution to my problem (except creating complex types, LINQ on the result set itself)?
And if yes, can someone point me to the right direction?
Many thanks,
Dimi
Just project onto a POCO:
var q = from c in Context.Clients
select new NameOnlyPresentation
{
Id = c.Id,
Name = c.Name
};
... or just the name:
public string ClientName(int id)
{
return (from c in Context.Clients
where c.Id == id
select c.Name).FirstOrDefault();
}