The data reader is incompatible with the specified Entity Framework - c#

I have a method that will return the bare min results from a sproc to fill a select menu. When I want the bare min results I pass bool getMin = true to the sproc, and when I want the complete record I pass bool getMin = false.
This is causing the Entity FrameWork error of "The data reader is incompatible with the specified"
The most relevant portion of the error
{"Message":"An error has occurred.","ExceptionMessage":"The data reader is incompatible with the specified 'CatalogModel.proc_GetFramingSystems_Result'. A member of the type, 'FrameType', does not have a corresponding column in the data reader with the same name.","ExceptionType":"System.Data.EntityCommandExecutionException",
Obviously the error is telling me that when the data reader attempted to set the property 'FrameType' that is was not in the query results.
Now I understand the error, what I am wanting to know is that am I goning to have t split up this sql sproc into two sprocs or is there a work around for this?
My function below
public static IEnumerable<IFramingSystem> GetFramingSystems(int brandID, string frameType, string glazeMethod, bool getMin)
{
using (CatalogEntities db = new CatalogEntities())
{
return db.proc_GetFramingSystems(brandID, frameType, glazeMethod, getMin).ToList<IFramingSystem>();
};
}
My TSQL below
ALTER proc [Catelog].[proc_GetFramingSystems]
#BrandID INT,
#FrameType VARCHAR(26),
#GlazeMethod VARCHAR(7) ='Inside',
#getMin BIT = 0
as
BEGIN
SET NOCOUNT ON;
IF #getMin =0
BEGIN
SELECT c.ID,c.Name,c.Descr,c.FrameType,c.isSubFrame,
c.GlassThickness,c.GlassPosition,c.GlazingMethod,c.SillProfile
from Catelog.Component c
WHERE c.MyType ='Frame'
AND c.FrameType = #FrameType
AND c.GlazingMethod = #GlazeMethod
AND c.ID IN(
SELECT cp.ComponentID FROM Catelog.Part p JOIN
Catelog.ComponentPart cp ON p.ID = cp.PartID
WHERE p.BrandID = #BrandID
)
ORDER BY c.Name
END
ELSE
SELECT c.ID,c.Name,c.Descr
from Catelog.Component c
WHERE c.MyType ='Frame'
AND c.FrameType = #FrameType
AND c.GlazingMethod = #GlazeMethod
AND c.ID IN(
SELECT cp.ComponentID FROM Catelog.Part p JOIN
Catelog.ComponentPart cp ON p.ID = cp.PartID
WHERE p.BrandID = #BrandID
)
ORDER BY c.Name
SET NOCOUNT OFF;
END;

To me it seems that both branches of the IF return different data, the first branch returns 9 columns where the second - only three. I believe the EF can't reflect the IFramingSystem from the latter. Specifically, the column FrameType (and 5 other columns) are obviously missing:
...
SELECT c.ID,c.Name,c.Descr <- where are the remaining columns
from Catelog.Component c
...

I understand this is an old post; but, I wanted to share what I learned tonight about this. What I found that the 'most relevant portion of the error message is stating' is this.
db.proc_GetFramingSystems(brandID, frameType, glazeMethod, getMin).ToList<IFramingSystem>();
is expecting a column to be returned from the stored procedure with the alias of 'FrameType'.
I ran into this when I created a POCO (plain old clr object) class of my table with more programmer friendly names. Instead of a strongly typed name of say 'email_address', I wanted 'EmailAddy' and so forth. I created a mapped class stating this among other mapped columns.
this.Property(t => t.EmailAddy).HasColumnName("email_address");
Although this is necessary for other parts of EF to work, the mapping class is not referenced when executing a db.SqlQuery.
So, when the code below executes
var a = new SqlParameter("#fshipno", shipno);
return _context.db.SqlQuery<EmailList>("exec spGetEmailAddy #fshipno", a).ToList();
It generated the same error except instead of 'FrameType', it mentioned 'EmailAddy'.
The fix... I had to alias the 'email_address' column in my stored procedure to 'EmailAddy' to get it to map the returned dataset to my POCO.
EDIT: I have found that this only works if your method is returning an IEnumerable:
public IEnumerable<myPOCO> GetMyPoco()
You will get the same error message if you are attempting to return a single POCO object.
public myPOCO GetMyPoco()

If you are inserting/deleting/updating (these are considered by EF as 'non-query'), and can be called by our code using
MyDbContext.Database.ExecuteSqlCommand(insert into Table (Col1,Col2) values (1,2));
But if are doing select query for a raw SQL statement, then use
MyDbContext.DbSet<Table_name>.SqlQuery(select * from table_name).ToList();
or
MyDbContext.Database.SqlQuery(select * from table_name).ToList();
()
The SqlQuery() function, in EF, for strange reasons, throw exception Insert/delete/update operation. (The exception thrown is "A member of the type, does not have a corresponding column in the data reader with the same name.") But it has actually performed operation if you open your Sql Management Studio and check for the entries.
FYI http://www.entityframeworktutorial.net/EntityFramework4.3/raw-sql-query-in-entity-framework.aspx

Related

Can't get stored procedure results with Entity Framework 6

I have a stored procedure which returns a 0 or a 1 depending on whether or not a specified email address exists in my database:
CREATE PROCEDURE [DatabaseSchema].[EmailAddressIsDuplicate] (#emailAddress nvarchar(255))
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS(
SELECT *
FROM [DatabaseSchema].[EmailUpdatesRegistrant]
WHERE EmailAddress = #emailAddress
)
RETURN 1
ELSE
RETURN 0
RETURN 0
END
GO
And I'm trying to derive the results of this stored procedure from an Entity Framework 6 database context:
using (DatabaseContext dbContext = new DatabaseContext())
{
ObjectParameter param = new ObjectParameter("emailAddress", typeof(bool));
var result = dbContext.EmailAddressIsDuplicate(emailAddress);
}
I'm getting lots of errors.
Error #1: Using the code above, var result is always set to -1.
Error #2: I tried navigated to Edit Function Import and set the Returns a Collection Of to a Boolean scalar value. This throws the following error:
The data reader returned by the store data provider does not have enough columns for the query requested.
Error #3: I went back and set the Edit Function Import return value to None. Then I tried the following code from this answer:
using (DatabaseContext dbContext = new DatabaseContext())
{
var p = new SqlParameter("#emailAddress", emailAddress);
var result = dbContext.Database.SqlQuery<bool>("DatabaseSchema.EmailAddressIsDuplicate", p);
}
No immediate errors thrown, but I have no idea whether or not I can derive useful data from var result. Trying to cast result to bool throws the following error:
Cannot convert type 'System.Data.Entity.Infrastructure.DbRawSqlQuery' to 'bool'
Any ideas on how I can see the results of this stored procedure (0 or 1)?
You could try adding an output parameter (#result) in the stored procedure signature:
CREATE PROCEDURE [DatabaseSchema].[EmailAddressIsDuplicate]
(#emailAddress nvarchar(255), #result bit out)
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS(SELECT *
FROM [DatabaseSchema].[EmailUpdatesRegistrant]
WHERE EmailAddress = #emailAddress)
SET #result = 1
ELSE
SET #result = 0
RETURN #result
END
GO
(you'll have to re-define your EF Model Function definition accordingly)
using (DatabaseContext dbContext = new DatabaseContext())
{
ObjectParameter isDuplicate = new ObjectParameter("isDuplicate", typeof(bool));
var result = dbContext.EmailAddressIsDuplicate(emailAddress, isDuplicate);
bool emailIsDuplicate = (bool)isDuplicate.Value;.
}
If you want to call the stored procedure directly with an out parameter you could follow this suggestion:
Database.SqlQuery calling stored procedure that has multiple output parameters
REASON - The template builder for EF (including v6) incorrectly sets the SP up as returning an INT containing the row count rather than the return value because it incorrectly calls the wrong ObjectContext.ExecuteFunction (found in the template-generated class YourDatabaseEntities that is the child of the DBContext).
Why wrong ExecuteFunction? - The result set incorrectly says the row count of changed rows rather than the return value or output parameters because it calls a different ExecuteFunction that discards the results. The flyover intellisense hint of the ObjectContext.ExecuteFunction says "Executes a stored procedure ….; discards any results returned from the function; and returns the number of rows affected by the execution" rather than the usual "Executes a stored procedure …. with the specified parameters".
WHY -1: I believe the SET NOCOUNT ON is causing the SP to return no count result and that Microsoft's ExecuteFunction returns that as error code.
SP FIXES - 1) You have to comment out SET NOCOUNT ON .
2) You have to change stored procedure to do the SELECT command as last statement instead of the RETURN command.
SOLUTION FIX - 1) After fixing SP, delete SP from Function Imports folder and the Data Store's SP folder. 2) Reload the SP into the EDMX by using the "Update Model from Database" 3) Rebuild all of your data project where the EDMX resides. 4) Exit Visual Studio and return. 5) Rebuild overall solution.
See: Entity Framework (Database first) has incorrect return result from stored procedure
Implement the stored procedure in C# to a value using parameters.
Resource: https://msdn.microsoft.com/en-us/library/yy6y35y8(v=vs.110).aspx
This way, the values can be stored to a variable from the ExecuteReader.
Add the value to model similar to adding a value to a property. The stored procedure could be called from ActionResult. Though this may require adding the stored procedure to a separate layer, that simply runs the stored procedure and adds the value to model afterwards.
try this
CREATE PROCEDURE [DatabaseSchema].[EmailAddressIsDuplicate] (#emailAddress nvarchar(255))
AS
BEGIN
SELECT *
FROM [DatabaseSchema].[EmailUpdatesRegistrant]
WHERE EmailAddress = #emailAddress
SELECT ##ROWCOUNT
END
GO
using (DatabaseContext dbContext = new DatabaseContext())
{
var result = dbContext.Database.SqlQuery<int32>("exec DatabaseSchema.EmailAddressIsDuplicate {0}", emailAddress).FirstOrDefault();
}
Anything other 0 in the return value indicates there is a match and the number indicates the number of matches

SQL Server ISNULL and Entity Framework stored procedures

I have a SQL Server DB that my MVC app has access to via Entity Framework 6. I have a stored proc that does some totals and stores it in a temp table, then returns the results via a final select statement (simplified version):
select isnull(TotalDataShared, 0) as TotalDataShared from #main;
There are cases that TotalDataShared can be null, so I have to check for it. The auto-generated EF entity for the proc result looks like this (also simplified):
public partial class getUsageTotals_Result
{
public decimal TotalDataShared { get; set; }
}
My repo method calls the proc like this:
public UsageTotals GetCurrentUsage(int accountId)
{
var ret = _context.getUsageTotals(accountId).First();
return new UsageTotals {
DataShared = (double)ret.TotalDataShared
};
}
When TotalDataShared in #main is not null everything works fine. When It's null, I get the following error in my app:
The 'TotalDataShared' property on 'getUsageTotals_Result' could not be
set to a 'System.Double' value. You must set this property to a
non-null value of type 'System.Decimal'.
It seems to me that it's ignoring the ISNULL command. I've tried wrapping the last select in a table var since I've had issues in the past returning data from temp tables before in EF, but doesn't do any good.
I've also tried using COALESCE() and it gives the same results. I've also tried removing and recreating the object in EF, to no avail. I've also tried returning a non-zero float value if it's null (ISNULL(TotalDataShared, 4.441)) but that doesn't change anything either.
Not sure what to try next. Any ideas?
UPDATE:
Adding stored proc skeleton:
create proc getUsageTotals #accountid int
as
if object_id('tempdb..#main') is not null drop table #main;
create table #main (
TotalDataShared float,
)
. . .
. . .
select isnull(TotalDataShared, 4.441) as TotalDataShared from #main;
go
The final two lines of the proc are the select and "go", so the return value should be the select statement, unless I'm missing something.
Try this:
In your Entity Framework model (open the .edmx file), right-click on
the diagram background and select Model Browser.
Open the Complex Types tree.
Open your stored procedure, maybe called getUsageTotals_Result.
Right-click on the TotalDataShared line and select Properties.
Change Nullable to true.
I hope this is what you need.
As suggested in a comment, the procedure returns 0 rows if there are no results in the #main table. So _context.getUsageTotals(accountId) may return zero rows, and you can easily tackle that by:
var ret = _context.getUsageTotals(accountId)
.FirstOrDefault(); // FirstOrDefault!
return new UsageTotals {
DataShared = (double)(ret == null ? 0 : ret.TotalDataShared);
};

Oracle CHAR datatypes dont work with Entity Framework

I have an issue returning data from on Oracle database using a WHERE clause which targets a CHAR column.
I have provided the steps below which should allow the issue to be recreated:
Database Setup
Run the following SQL to create the database table and insert mock data:
CREATE TABLE ORDERS (ORDER_NUMBER CHAR(10 BYTE));
INSERT INTO ORDERS VALUES ('123456');
SELECT REPLACE(ORDER_NUMBER, ' ', '#') as ORDER_NUMBER from ORDERS
ORDER_NUMBER 123456####
As you can see, the value stored in the table is padded out with spaces to 10 characters (replaced by '#' to make them apparent).
Entity Framework Setup
public class Order
{
public string OrderNumber { get; set; }
}
public class OrderMap : EntityTypeConfiguration<Order>
{
public OrderMap()
{
// Primary Key
HasKey(t => t.OrderNumber);
Property(t => t.OrderNumber)
.HasColumnName("ORDER_NUMBER")
.IsRequired()
.IsFixedLength()
.HasMaxLength(10)
.HasColumnType("CHAR")
.IsUnicode(false);
}
}
public class OrdersContext()
{
static OrdersContext()
{
Database.SetInitializer<OrderContext>(null);
}
public DbSet<Order> Orders { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new OrderMap());
}
}
The Problem
class Program
{
static void Main(string[] args)
{
using (var context = new OrdersContext())
{
var order1 = context.Orders
.FirstOrDefault(o => o.OrderNumber == "123456");
// This works, the record is found in the database and I did not have to pad the filter sting to 10 characters
Debug.WriteLine("OrderNumber:" + order1.WorksOrder);
var orderFilter = "123456";
var order2 = context.Orders
.FirstOrDefault(o => o.WorksOrder == orderFilter);
// This fails. Using a variable to specify the filter does not work. No record is found.
Debug.WriteLine("OrderNumber:" + order2.WorksOrder);
}
}
}
So, the issue is that when I want to use a variable (orderFilter), the statement does not return a record. I can make this work by padding orderFilter as follows, but I don't think I should have to do this:
var orderFilter = "123456".PadRight(10);
var order2 = context.Orders.FirstOrDefault(o => o.WorksOrder == orderFilter);
// This now works
Debug.WriteLine("OrderNumber:" + order2.WorksOrder);
It appears as through the SQL generated when using a bind variable does not set the correct datatype on the bind variable. If I enable tracing, we see the following SQL:
Opened connection at 22/04/2015 12:15:12 +01:00
SELECT "Extent1"."ORDER_NUMBER" AS "ORDER_NUMBER", FROM "ORDERS"
"Extent1" WHERE ("Extent1"."ORDER_NUMBER" = :p__linq__0) AND (ROWNUM
<= (1) )
-- p__linq__0: '123456 ' (Type = Object)
-- Executing at 22/04/2015 12:15:12 +01:00
-- Completed in 13 ms with result: OracleDataReader
I have also tried using DbFunctions.AsNonUnicode(orderFilter) but this seems to have little effect.
Can anyone explain what is going on here? How do I filter on CHAR datatypes without padding the filter or trimming the column data?
NOTE
I cannot change the data type of the column in the database.
This issue is logged in several places but with no definitive answer:
Oracle selecting CHAR datatype
ODP.NET / EF6 - CHAR datatype in WHERE clause
https://community.oracle.com/thread/3654585
Version Numbers of all the things:
Entity Framework 6.1.3
.Net Framework 4.5.1
Oracle.ManagedDataAccess 4.121.2.0 ODAC 12c Release 3
Oracle.ManagedDataAccess.EntityFramework 6.121.2.0
Oracle database is 11.2.0.3.0
I can explain what is going on. The solution is to change the CHAR column (which I note you say you can't) otherwise be aware of the subtleties when comparing against it.
Tom Kyte's section on the CHAR datatype in his superb book "Expert Oracle Database Architecture" is the source for this answer. The following is based on the 2nd edition pages 499 - 502.
Short answer
Character literals get promoted whilst variable length bind variables do not.
Explanation
(All the following were run using SQLPlus against 12.1.0.2 Database (but nothing shown is unique to 12c)
Create and populate a table with CHAR and VARCHAR2 columns:
CREATE TABLE ORDERS (ORDER_NUMBER_CHAR CHAR(10 BYTE),
ORDER_NUMBER_VARCHAR2 VARCHAR2(10 BYTE));
INSERT INTO ORDERS(ORDER_NUMBER_CHAR,
ORDER_NUMBER_VARCHAR2)
VALUES ('123456',
'123456');
In the first query which uses the VARCHAR2 column in the WHERE clause, it works as expected and a row is returned.
SELECT *
FROM orders
WHERE order_number_varchar2 = '123456';
In the next query the WHERE clause uses the CHAR column. This query returns a row which means an implicit conversion has taken place whereby the CHAR(6) literal gets promoted to a CHAR(10)
SELECT *
FROM orders
WHERE order_number_char = '123456';
The implicit promotion must have happened because the strings are of different
lengths which is shown by this query which returns no rows
SELECT *
FROM orders
WHERE order_number_char = order_number_varchar2;
The next example shows that the VARCHAR2 bind variable is not promoted in the same way as the character literal so this query returns no rows
variable vc2 VARCHAR2(10 BYTE);
exec :vc2 := '123456';
SELECT *
FROM orders
WHERE order_number_char = :vc2;
If the correct CHAR bind variable is used then the record will be found, the final query returns the row as expected.
variable the_char CHAR(10 BYTE);
exec :the_char := '123456';
SELECT *
FROM orders
WHERE order_number_char = :the_char;
You should also be aware that the application will be impacted if the CHAR column ever changes size, i.e. if it was increased to 20 then the PadRight method would need to be changed accordingly.
Finally I thought it was also worth quoting Tom's summing up of the CHAR datatype in full.
It is for these reasons—the fixed-width storage, which tends to make
the tables and related indexes much larger than normal, coupled with
the bind variable issue—that I avoid the CHAR type in all
circumstances. I cannot even make an argument for it in the case of
the one-character field, because in that case it is really of no
material difference. The VARCHAR2(1) and CHAR(1) are identical in all
aspects. There is no compelling reason to use the CHAR type in that
case, and to avoid any confusion, I “just say no,” even for the
CHAR(1) field.
the problem is EF6 passing parameter as varchar2 type to oracle.
1. using
Install-Package EntityFramework.Functions -Version 1.4.1
to add a function RightPad(string str, int length, string padChar) map to Oracle RPAD function
set MaxLength of Entity properity
add a GetColumnMaxLength function
write code like below, you can get you data and the database index is working
.
int length = GetColumnMaxLength(dbContext, "Vessel","VesselName");
dbContext.Set<Vessel>().Where(m=>m.VesselName.StartWith(RPad(vesselName, length,""))
using StartWith instead of = symbol. because when you extend the column length on db before publishing program, StartWith can make old program working well.but
you still need to change Entity properity's MaxLength and publish later
Try to Compare with Trim() on the entity fields....

Linq Specified cast is not valid

I have checked lots of posts having this error but none had this particular problem.
(Also I am new to C sharp have been a java dev)
I am getting an exception as
System.InvalidCastException was caught
on the table2.Field("MEME_CK") line below in the code snippet.
There are about 3K rows in the table2, I couldn't find a way to avoid wrong casting for line table2.Field("MEME_CK")
Data can either be null, not present, valid or invalid. So I tried using nullable operator on the generic parameter cast. Also saw there is DBNull class that can possibly represent non existent value.
Is there a way to preprocess column data before doing "equals test" or joining in the code below ?
How can I avoid casting even after using nullable type?
Following code basically do a join on two data tables based on MemberID i.e. MEME_CK or MemeCk and creates new object with CapHeadID, MemeCk etc as fields.
var query =
(from table1 in searchResult.AsEnumerable()
join table2 in memberInfo.AsEnumerable()
on table1.Field<decimal?>("MemeCk") equals
table2.Field<decimal?>("MEME_CK")
select new
{
CapHeadID = table1.Field<decimal>("CapHeadID"),
MemeCk = table1.Field<decimal>("MemeCk"),
Suffix = table2.Field<decimal>("MEME_SFX"),
Suscriber = table2.Field<string>("SBSB_ID"),
BusinessArea = table2.Field<string>("TEAM"),
MemberName = table2.Field<string>("MemberName"),
WorkTypeName = table1.Field<string>("WrkName"),
SSN = table2.Field<string>("MEME_SSN"),
AssignedUser = table1.Field<string>("AssignedUser")
}).Distinct().OrderBy(a => (a.Suscriber.IsNotNil() ?
a.Suscriber : "")).Take(3000);
You're using AsEnumerable to convert the queries away from IQueryable... basically this means LINQ won't try to generate SQL code for the join, and the join will be done in C#.
With that in mind, you could try just get them as objects - like this:
from table1 in searchResult.AsEnumerable()
join table2 in memberInfo.AsEnumerable()
on table1.Field<object>("MemeCk") equals
table2.Field<object>("MEME_CK")
select new...
I'm not sure if that will work, but it might do

How to access 'Results', 'Messages', and 'Return Value' of a Stored Procedure using Entity Framework 4?

QUESTION
How do I access the 'Results', 'Messages', and 'Return Value' of a Stored Procedure using Entity Framework 4.4 and C# 4.0?
Below is the Stored Procedure that takes three parameters. One way or another when I run the Store Procedure I should, I hope, be able to access all three values for 'Results', 'Messages', and 'Return Value'.
Can someone help me figure out how to do that with EF? Using the code that is generated out of EF all I seem to be able to access is the 'Results' of the query ( returned rows )
Stored Procedure
USE [THIS_DB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[THIS_PROCEDURE]
#FIRST_PARAM CHAR(17) = NULL,
#SECOND_PARAM CHAR(2) = NULL,
#THIRD_PARAM CHAR(5) = NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE #ReturnValue INT = 0;
IF COALESCE(#SECOND_PARAM, 'XX') NOT IN ('XX', 'YY')
BEGIN
RAISERROR('Invalid #SECOND_PARAM value: %s; #SECOND_PARAM mXXt be XX or YY.', 2, 1, #SECOND_PARAM ) WITH SETERROR;
SET #ReturnValue = -50100;
END
IF COALESCE(#SECOND_PARAM, 'XX') = 'YY'
BEGIN
RAISERROR('#SECOND_PARAM value: %s; YY is valid, but currently is not supported, returning XX results.', 2, 1, #SECOND_PARAM) WITH SETERROR;
SET #ReturnValue = -50105;
END
IF COALESCE(#THIRD_PARAM, 'XX-EN') NOT IN ('XX-EN')
BEGIN
RAISERROR('Invalid #THIRD_PARAM value: %s; #THIRD_PARAM mXXt be XX-EN.', 2, 1, #THIRD_PARAM) WITH SETERROR;
SET #ReturnValue = -50101;
END
SELECT DISTINCT
THESE.VALUES
FROM dbo.THIS_TABLE
WHERE THESE.CONDITIONS;
IF ##ROWCOUNT = 0
BEGIN
DECLARE #SP_MATCHCOUNT INT
EXEC #SP_MATCHCOUNT = [dbo].[MATCHTABLE] #PATTERNH = #PATTERN
IF #SP_MATCHCOUNT > 0
BEGIN
RAISERROR('Mapping from HERE to HERE not found for PATTERN: %s.', 2, 1, #PATTERN) WITH SETERROR
SET #ReturnValue = -50103;
END
ELSE
BEGIN
RAISERROR('PATTERN Pattern not found for PATTERN: %s.', 2, 1, #PATTERN) WITH SETERROR
SET #ReturnValue = -50104;
END
END
RETURN #ReturnValue
END
CODE
public virtual ObjectResult<THIS_PROCEDURE_RESULT> THIS_PROCEDURE_METHOD(string FIRST, string SECOND, string THIRD)
{
var FIRST_PARAM = FIRST != null ?
new ObjectParameter("FIRST", FIRST) :
new ObjectParameter("FIRST", typeof(string));
var SECOND_PARAM = SECOND != null ?
new ObjectParameter("SECOND", SECOND) :
new ObjectParameter("SECOND", typeof(string));
var THIRD_PARAM = THIRD != null ?
new ObjectParameter("THIRD", THIRD) :
new ObjectParameter("THIRD", typeof(string));
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<THIS_PROCEDURE_RESULT>("THIS_PROCEDURE", FIRST_PARAM, SECOND_PARAM, THIRD_PARAM);
}
So, first things first :-) Just want to make sure we're on the same page before I answer the 3 parts of the question. EF is designed to be an ORM (object-relational-mapper). That means its purpose for being is to translate relational data to code objects (and vice-versa). The mechanism it uses for this is result sets (not return values). So most of the plumbing inside EF is specifically designed to operate on result sets, and also to automatically generate SQL for getting those result sets. However, since people requested it, EF now has the capability to execute stored procedures, but that ability is not comprehensive, and is sort of a side-effect to the main capabilities of the product. Having said that, EF does use ADO.NET under the covers, and that's where you are going to get your answers because ADO.NET does handle all your scenarios.
First problem - how to get results. EF will execute the SP in this case, and presumably, it's mapped to some object that has properties that match the result columns. That means that EF will create a collection (enumerable query result set to be more precise) of objects, each of which represents a row of data in the results. In your case, the return of your method is ObjectResult. ObjectResult is a collection of objects, and each item is of type THIS_PROCEDURE_RESULT, which in turn has a property for each mapped column of the result.
Second problem - how to get messages. If Raiserror is used with a certain range of severity, will cause ADO.NET to throw and exception (of type SqlException). EF will just just surface (pass through) that error. That SQLException instance will contain all the error & message information. To see it, you just have to catch the error:
try
{
// call EF SP method here...
}
catch(SqlException se)
{
Debug.WriteLine(se.Message);
}
catch(Exception e)
{
// all non-DB errors will be seen here...
}
However, if the Raiserror statement is of a warning or info severity, ADO.NET will not throw an exception. In that case, you have to use an event of the connection object to see info and warning messages from the databse. To do this in EF, you have to get the EntityConnection from the EF object context, and then get the Store Connection from the EntityConnection. If you are using SQL Server (SqlClient ADO.NET provider), this will be a SqlConnection instance. That instance contains an event called InfoMessage. You can hook up an event handler to that event to trap messages. More info here: http://support.microsoft.com/kb/321903
Last problem - how to get Return Value. This one is going to suck. Based on my first paragraph, EF isn't really designed to arbitrarily handle SP calls. While it will map result sets to object collections, it doesn't handle return values from SPs. You will have to use ADO.NET without the EF layer in order to access the Parameters collections of the SqlCommand object. One of the parameters is of parameter-type ReturnValue, and it will contain the return value itself.

Categories

Resources