I am doing transition for a project from Webforms to MVC application using Entity Framework database first approach and have database ready along with all stored procedures in place.
I successfully created an .edmx file and was able to use my stored procedures and it worked great when there was any insert or update operation to perform. But the real problem occurred when I was using select query in one of my stored procedures.
For example, there is an Employee table which has following columns:
EmpId, FirstName, LastName, Age, Salary
I have a stored procedure GetAllEmpDetails which has following select query.
Select
EmpId, (FirstName + ' ' + LastName) as FullName, Salary
from
Employee
Now when I am trying to bind the result of this stored procedure with the Employee class which has 5 properties as per the table structure, then I am getting an error that value for Age property is expected but it is not available in the resultset.
I know there is no FullName property as well, so my question is how to solve this problem with the model class generated (as in this case Employee) so that it can tackle these dynamism?
How to map a stored procedure in EF?
Since you are doing Database First Approach and you have an EDMX file, let EF generate the class of the stored procedure result for you. You may have many stored procedures and you want to avoid creating the classes manually: After all that is the whole point of using an ORM tool. Also some of your stored procedures may have parameters. Doing it the way below will handle all that for you. It is actually pretty simple.
To get EF to do this for you, follow the steps to below:
Double click your EDMX file
Choose Update Model from Database
You will see the dialog similar to below:
Make sure you have checked the boxes as shown.
That will add the stored procedure and you will see it in your model browser as shown below:
If you want to change the class name auto-generated by EF then do so. I strongly suggest you do this and give your class a meaningful names that follow .NET naming conventions. The convention I follow is remove any verbs from the stored procedure name and append the word result to the end. So you will end up with name as shown below:
Press OK
Some Notes
This is much better than writing the classes manually in case your stored procedure name, or the parameters it needs, or the result it returns changes. This approach will work for user defined functions as well.
A Gotcha
There will be times when the stored procedure will not appear in the selection in the wizard dialog, that is because of this. Simply add this to the beginning of your stored procedure:
SET FMTONLY OFF -- REMEMBER to remove it once the wizard is done.
public class EmployeeProcedure
{
[Column("EmpId")]
public int EmployeeId { get; set; }
[NotMapped]
public string FullName { get; set; }
public double Salary { get; set; }
}
after that call this:
this.Database.SqlQuery<EmployeeProcedure>("GetAllEmpDetails");
Related
Just ramping up on .NET Core and running into some unsolvable problems.
I have a stored procedure called GetLeads whose schema basically returns
LEADID (INT)
SALENO (INT)
OFFICE (INT)
PACKAGE (NVARCHAR(4)
SALEDATE (DATETIME)
For sake of an example, there is a table in the same database where that Stored Procedure resides called Prospects. Its schema is
LEADID (INT)
FNAME (NVARCHAR(50))
LNAME (NVARCHAR(50))
ADDR1 (NVARCHAR(150))
ADDR2 (NVARCHAR(150))
EMAIL (NVARCHAR(200))
PHONENO (NVARCHAR(200))
CONTACTDATE (DATETIME)
According to the docs, I can use the FromSql method of a context model to call a stored procedure. But what if the stored procedure returns a schema having a only a few (if any) of the fields that match the model of the context from which the call is made?
If I construct a Query such as:
var _ctx = new DbContext();
var pkgs = _ctx.Prospects.FromSql("EXEC dbo.GetPackages").ToList();
This obviously will fail due the fact that returned recordset doesn't contain fields relevant to the Prospects model.
Conversely, using something like:
var pkgs = _ctx.Database.ExecuteSqlCommand("EXEC dbo.GetPackages");
Is pointless as well because it will only return the number of records affected by the command execution.
So the FromSql method available in .NET Core obviously has its set of limitations although it's documented as available for making stored procedure calls.
Ideally, I'd like to see the Core Framework have the ability to make direct calls to Stored Procedures and Functions in much the same manner as is with EF within the .NET Framework and simplify things. But I realize that functionality isn't yet available in Core; though I hope it eventually does.
Anyway, enough rambling and to the point.
How or what currently is the most practical and widely accepted way to call a stored procedure under these set of circumstances in .NET Core? I know ADO.NET is an option but I'm speaking strictly within the paradigm of the Core Framework using EF
I'm going to give some credit to Selvin for his last comment as it did prompt me down the path for a solution. But I really needed an explicit answer.
I'm going to also acknowledge the much appreciated comment from Rahul for using Snickler as an alternative. But I wanted a solution that solely involved nothing more than EF and .NET Core. Quite honestly, all the comments are greatly appreciated
So here are the actual steps required to always be able to get the FromSql method to work with Stored Procedures within the Entity Framework. I am using the Database First approach using MVC but the overall methodology for the solution shouldn't matter.
Note: If the EF context has already been created, Step 1 can be skipped.
Step 1: Generate the EntityFramework context and associated Entity Models from the database using the dotnet utility.
dotnet ef dbcontext scaffold "Server=[YourDBServerName];Initial Catalog=[YourDatabaseName];Integrated Security=False;Persist Security Info=False;User ID=[YourUserId];Password=[YourUserPassword];" Microsoft.EntityFrameworkCore.SqlServer -o Models
Step 2: Add a ViewModel that defines all the fields that your stored procedure will return. Note: You could alternately add this as a Model instead, but I prefer to keep the stored procedure entities distinctly separated from the Models generated directly from the database for brevity and a clear separation of the architecture.
Equally important, the entity class MUST have a primary key or it won't work. EF requires that all entities have a primary key.
public partial class Lead
{
[Key]
public int leadid { get; set; }
public int saleno { get; set; }
public int office { get; set; }
public string package { get; set; }
public datetime saledate { get; set; }
}
Step 3: Add the stored procedure entity Model created in Step 2 to the Database context as a virtual DbSet. This should be located in the Models directory created by Step 1 and would have the word "Context" prepended in the name.
public virtual DbSet<Lead> Lead { get; set; }
After performing these steps 2 and 3 you can all any stored procedure using the FromSql method within the Core Framework
var _ctx = new DbContext();
var pkgs = _ctx.Prospects.FromSql("EXEC dbo.GetPackages").ToList();
Note: Since record sets returned from stored procedures don't require the presence of a primary key you could either:
Modify the Stored Procedure to include a Primary Key such as an identity field
Identify a couple of fields within the returned record set that would constitute as being viable primary keys and create a composite key within the Model
However the 2nd option requires some additional work and considerations because you'll have to use fluent API to set composite primary keys. EF Core won't allow Data Annotations to set composite keys.
Hopefully this shared information will help others who journey the same path with the similar question
I am wondering if there is a way, using Entity Framework, of mapping results from arbitrary sql, like ResultSetMapping in Doctrine. I know i can create a configuration class doing the mapping but that requires me registering the class as an entity type.
What i am trying to do is utilize the materialization element (Object Services) of EF without the rest of EF. Sometimes i have results from a proc with cryptic or less meaningful column names, but need to map to classes with more meaningful property names but have no permission to alter the proc definition, e.g:
exec dbo.getRecDetail #var
returning columns :
sd, ed, nm, ....
which should be mapped to a class such as:
class Obj{
public DateTime StartDate; //sd
public DateTime EndDate; //ed
public String Name; //nm
....
}
If you are using .EDMX file (designer) with database first approach you can import your stored procedures into the model (simply select stored procedures in wizard) and map their results to complex types.
It actually works with arbitrary SQL queries as well but those queries cannot be imported into the model automatically (because they don't exist in your database). It requires manual modification of storage model in EDMX file (it is XML - check documentation for Function and CommandText elements). Once you do that you cannot use update from database feature of the designer any more because it would delete your custom queries.
If you are using code first there is currently no option to map results of stored procedures or custom queries. You can only use automatic mapping which requires your class to have properties with exactly same name as columns in result sets.
Okay, so i've studied c# and asp.net long enough and would like to know how all these custom classes i created relate to the database. for example.
i have a class call Employee
public class Employee
{
public int ID { get; set; }
public string Name { get; set; }
public string EmailAddress { get; set; }
}
and i have a database with the following 4 fields:
ID
Name
EmailAddress
PhoneNumber
it seems like the custom class is my database. and in asp.net i can simple run the LINQ to SQL command on my database and get the whole schema of my class without typing out a custom class with getter and setter.
so let's just say that now i am running a query to retrieve a list of employees. I would like to know how does my application map to my Employee class to my database?
by itself, it doesn't. But add any ORM or similar, and you start to get closer. for example, LINQ-to-SQL (which I mention because it is easy to get working with Visual Studio), you typically get (given to you by the tooling) a custom "data context" class, which you use as:
using(var ctx = new MyDatabase()) {
foreach(var emp in ctx.Employees) {
....
}
}
This is generating TSQL and mapping the data to objects automatically. By default the tooling creates a separate Employee class, but you can tweak this via partial classes. This also supports inserts, data changes and deletion.
There are also tools that allow re-use of your existing domain objects; either approach can be successful - each has advantages and disadvantages.
If you only want to read data, then it is even easier; a micro-ORM such as dapper-dot-net allows you to use our type with TSQL that you write, with it handling the tedious materialisation code.
Your question is a little vague, imo. But what you are referring to is the Model of the MVC (Model-View-Controller) architecture.
What the Model , your Employee Class, manages data of the application. So it can not only get and set (save / update) your data, but it can also be used to notify of a data change. (Usually to the view).
You mentioned you where using SQL, so more then likely you could create and save an entire employee record by sending an Associative Array of the table data to save it to the database. Your setting for the Class would handle the unique SQL syntax to INSERT the data. In larger MVC Frameworks. The Model of your application inherits several other classes to handle the proper saving to different types of backends other than MS SQL.
Models will also, normally, have functions to handle finding records and updating records. This is normally by specify a search field, and it returning the record, of which would include the ID and you would normally base this back into a save / update function to make changes to record. You could also tie into this level of the Model to create revision of the data you are saving
So how the model directly correlates to your SQL structure is dependent on how you right it. Or which Framework you decide to use. I believe a common one for asp.net is the Microsoft's ASP.Net MVC
Your class cannot be directly mapped to the database without ORM tool, The ORM tool will read your configuration and will map your class to DB row as per your mappings automatically. That means you don't need to read the row and set the class fields explicitly but you have to provide mapping files and have to go through the ORM framework to load the entities, and the framework will take care of the rest
You can check nHibernate and here is getting started on nHibernate.
I am calling a stored procedure with EntityFramework. But custom property that i set in partial entity class is null.
I have Entities in my edmx (I called edmx i dont know what to call for this). For example I have a "User" table in my database and so i have a "User" class on my Entity.
I have a stored procedure called GetUserById(#userId) and in this stored procedure i am writing a basic sql statement like below
"SELECT * FROM Users WHERE Id=#userId"
in my edmx i make a function import to call this stored procedure and set its return value to Entities (also select User from dropdownlist). It works perfectly when i call my stored procedure like below
User user = Context.SP_GetUserById(123456);
But i add a custom new column to stored procedure to return one more column like below
SELECT *, dbo.ConcatRoles(U.Id) AS RolesAsString
FROM membership.[User] U
WHERE Id = #id
Now when i execute it from SSMS new column called RolesAsString appear in result.
To work this on entity framework i added a new property called RolesAsString to my User class like below.
public partial class User
{
public string RolesAsString{ get; set; }
}
But this field isnt filled by stored procedure when i call it.
I look to the Mapping Detail windows of my SP_GetUserById there isnt a mapping on this window. I want to add but window is read only i cant map it. I looked to the source of edmx cant find anything about mapping of SP.
How can i map this custom field?
You have to create a complex type for the SP instead of using the partial class.
Try adding the property to the User entity in the model browser. It might work if defined in the model, not as a partial class... or, in the end, the simplest may be have it return to an entity, and convert the SP result into the User result, as a last resort.
HTH.
Very old thread but I encountered this exact issue today.
One difference is that I haven't imported the SP into the dbContext but chose to use SQLQuery to return a list of Contact entities.
var allContacts = _dbContext.Database.SqlQuery<Contacts>
("CRM.GetAllContactsForCustomer #customerID, param1).ToList();
Like TS I had created a partial Class to hold an extra property that the SP would return.
public partial class Contacts
{
public EnumContactOrigin ContactOrigin { get; set; }
}
This property does NOT get filled when executing the SP, same as when you do so with an imported SP in the dbContext.
For some reason I came up with the idea to create a wrapper class for Contacts to hold that extra property.
public class ExtendedContacts : Contacts
{
public new EnumContactOrigin ContactOrigin { get; set; }
}
And guess what, it works! ContactOrigin does now get filled correctly.
var allContacts = _dbContext.Database.SqlQuery<ExtendedContacts>
("CRM.GetAllContactsForCustomer #customerID, param1).ToList();
In my case I'm ok with it being a wrapper class as I only use the list of contacts to show in a grid.
I would love to know though why this works? And if there is another way of retrieving extra properties into Contacts without the need of this ExtendedContacts class.
I have a very simple stored procedure which returns multiple record sets. All of the record sets have aliased columns so they all look the same.
E.g.
SELECT TOP 10 FooId AS Id, Name As Name FROM Foos
SELECT TOP 10 BarId AS Id, Name As Name FROM Bars
...
For my EF setup, i'm using POCOs and have my own DataContext (no code-generation).
Now, i have created a "Function Import" using the technique detailed here.
But the problem is, it's creating a complex type with Id and Name, not a type that can hold multiple collections of Id and Name. Can EF not detect that i am returning multiple record sets?
So the stored proc gets executed properly, but the only records that come back are from the 1st select statement, the other ones are discarded. So i only get back 10 records.
Here's how im executing the SPROC in my custom DataContext:
public ObjectResult<SomeSimpleProc_Result> GetSomeStuff()
{
return base.ExecuteFunction<SomeSimpleProc_Result>("SomeSimpleProc);
}
And the Return Result POCO:
public class SomeSimpleProc_Result
{
#region Primitive Properties
public int Id
{
get;
set;
}
public string Name
{
get;
set;
}
#endregion
}
The end result is i want to an object which has 0-* objects in it (in the above case, 3 objects). Each object should have a set of simple objects in it (Id, Name).
I think the problem is definitely with the customization of the "Function Import". How should i be creating the complex type? Or should i be using "Returns a collection of Entities".
Any ideas?
I think multiple resut sets are not supported out of the box. Here is a blog post about using them in EF v1. EF v4 also doesn't support them directly - comments in this article contains following statement by Danny Simmons (he used to be dev manager for EF and Linq-To-Sql):
Unfortunately we weren’t able to get
full support for multiple results into
the product this time around. We did,
however, add the method Translate
to ObjectContext which allows you to
materialize objects from a DataReader.
...
Edit: To make this little bit up to date: EF 4.5 (.NET 4.5 + VS2012) supports stored procedures with multiple result sets but at least in Beta it looked like the support is not implemented in UI and EDMX validation also complained about some problems but at runtime it worked correctly.