Say I got this situation: I have to filter one of my entities with data which I get from an stored procedure:
var results = from c in db.Customer
join p in db.GetSPResults() on c.Id equals p.Id
select c;
on my Context class I got this:
public ObjectResult<Example> GetSPResults()
{
return (this as IObjectContextAdapter).ObjectContext.ExecuteFunction<Example>("Proc_Example");
}
So far I run into 2 problems:
I get an InvalidOperationException when code strikes the ExcecuteFunction line:
The FunctionImport 'xxx' could not be found in the container 'xxx'.
Assuming you guys can help me to solve that problem, would it be possible to query that way? Using those stored procedure results like a context entity? I think EF won't allow that cause it's not an entity, nor a "Constant Value".
I'm using EF 4.3.
instead of calling your procedure like that by name as string you can import it in your entity model then you can have a type safe / strongly typed method call directly on your DbContext.
basically you need to execute a function import, see here for an example: Using stored procedures with Entity Framework
Update: for POCO / Code first, see here: EF Code-First - Mapping stored procedures
Related
Everything I've found so far, if you are calling a table value function the return value must be an IQueryable. For example:
public IQueryable<AlbumsByGenre> ufn_AlbumsByGenre_ITVF(int genreId)
=> FromExpression(() => ufn_AlbumsByGenre_ITVF(genreId));
Most often when I'm using a table value function the table type that is returns is a DTO. That is, it doesn't match any actual tables in the database. Here is a example:
CREATE FUNCTION dbo.ufn_AlbumsByGenre_ITVF(#GenreId int)
RETURNS TABLE
AS
RETURN(
SELECT
ar.ArtistName,
al.AlbumName,
g.Genre
FROM Genres g
INNER JOIN Albums al
ON g.GenreId = al.GenreId
INNER JOIN Artists ar
ON al.ArtistId = ar.ArtistId
WHERE g.GenreId = #GenreId
);
Creating an entity for the return type results in an unnecessary, unused, and unwanted table in the database. In this instance the table name is "AlbumsByGenre".
Is there any way to have the return type be an unmapped type to prevent the unnecessary table?
Currently (as of EF Core 6.0) the type must be a model type (with or without key). There are plans for Raw SQL queries for unmapped types for EF Core 7.0 which might or might not allow the mapping you are asking for.
So for now your type must be registered in the model (cannot be unmapped). But creating associated table is not mandatory and can be avoided by configuring it with ToView(null), e.g.
modelBuilder.Entity<AlbumsByGenre>()
.HasNoKey() // keyless
.ToView(null); // no table or view
For me .ToView(null) still generates a table for the AlbumsByGenre.
My workaround is to map this type to the raw SQL query (tested on Entity Framework Core 6.0.9). Here I call the function with default parameters (for the author's case, the NULL value should be then allowed).
modelBuilder.Entity<AlbumsByGenre>()
.HasNoKey()
.ToSqlQuery("SELECT * FROM ufn_AlbumsByGenre_ITVF(NULL)");
Yes just create a class that has ArtistName, AlbumName and Genre however it should not be an IQueryable just a List<AlbumsByGenre> or IEnumerable<AlbumsByGenre> or an ICollection.
IQueryable is a delayed querybuilder for a table or view.
SQL functions results are not further queryable in SQL Server, so just drop the IQueryable.
I have some complex entities in my context.
DbSet<EntityA> AEntities { get; set; }
DbSet<EntityB> BEntities { get; set; }
where EntityA inherits EntityB (it's a superset of it). There is a column Discriminator that was created by Entity Framework to distinguish between the 2. Up to here, no problem.
EntityA also has an Owned Type:
modelBuilder.Entity<EntityA>().OwnsOne(p => p.OwnedThing,
ot =>
{
// ot......;
ot.ToTable("SectionTrailInfo");
});
As long as I use only EF to query the database, it's still all right.
But then for performance reasons I need to query an BEntities with a SQL Stored Proc.
// ...
return await BEntities.FromSqlInterpolated($"EXEC GetManyRecords #Id = {id}").ToListAsync();
That also worked before introducing the Owned Entity in EntityA. Now I get a 'FromSqlRaw' or 'FromSqlInterpolated' was called with non-composable SQL and with a query composing over it. error. The error is thrown as soon as I try the .ToQueryString() method.
I have tried modifying the Stored Proc to Left Join the Owned Type but it doesn't change a thing. In fact the query is never formed and never sent to the DB server. So I have to tell EF Core in a way or another how to deal with the Owned Type (that is not even part of this Entity, but a related one).
My only other option is to re-design completely my schema to removed the Owned Type but I am hoping I can avoid that.
Thanks in advance.
As kindly pointed out by #IvanStoev, the way to go is to use a Table-Valued-Function instead of a Stored Proc, in SQL.
I'm trying to create a generic method to get data from SQL Server by one stored procedure, but it doesn't return any data:
public static IEnumerable<T> Select<T>(string SQL)
{
string spName = "exec spGetData #SQL";
var parametros = new object[]{
new SqlParameter("#SQL", SQL)
};
IEnumerable<T> result;
using (DBContext db = new DBContext())
{
result = from a in db.ObjectContext.ExecuteStoreQuery<T>(spName, parametros).ToList() select a;
}
return result;
}
Someone could help me?
EF handles stored procedures and creates entity result sets from the function mapping of the stored proc result select to be consumed off of the EF context.
Steps
In the designer choose 'Update Model From Database`.
When you get to the page which allows one to add tables/views/stored procs select the stored proc of interest.
Once the wizard is finished EF will contain the stored proc in the Model Browser which can be displayed by right clicking the EF design surface and selecting Model Browser.
In the Model Browser look for the stored proc in the Model Store -> Stored Procedures / Functions folder. Verify that the mapping is good by looking at the Model-> Function Imports and that the result entity can be found in the Complex Types folder.
From there you can call your stored proc off of an EF context and it returns a list of the result set to the code.
I give a more thorough explanation on my blog article and provide hints and work arounds to some problems with complex entity failures : Entity Framework Stored Procedure Instructions
What steps i should follow to use the following sql scalar value function within entity framework.
select dbo.GetDefaultAccount(5,1,48)
I tried creating a static class under the same namespace of the edmx and defining the function as following
[EdmFunction("Model.Store", "GetDefaultAccount")]
public static int? GetDefaultAccount(int id, Int16 type, int assocId)
{
throw new NotSupportedException();
}
While accessing it from linq like below
var Accountno = (from s in dbcontext.TranSetups select new { Accountno = CutomEdmxFunctions.GetDefaultAccount(5, 1, 48) })
.FirstOrDefault().Accountno;
I get the following error
cannot be translated into a LINQ to Entities store expression because no overload matches the passed arguments
Thanks
Unfortunately, working scalar-valued functions in Entity Framework is not as easy as it supposed to be. After a long googling I found only two methods to work with scalar-valued functions, best described here, but remember: all edits to the .edmx file will be lost when you update the EDM from the database. You will have to re-edit the .edmx file each time. Good luck.
I am trying to get to grips with the Entity framework, and have a requirement to order results by distance from a point on the globe. I have decided on previous advice to do this using a stored procedure which I have successfully done populating a view. However I need to return multiple tables, which I understand I cannot do directly using stored Procedures on the Entity Framework. If this is not correct, I would be grateful if someone could advise how I might do this.
Anyway I therefore have defined a simple sp (SELECT id FROM table) and then wanted to perform a linq query to join this with the equivalent object in my model as follows:
var sp = db.StoredProcedure();
var ret = from x in db.X
join y in sp on x.ID equals y.ID
select x;
However when I perform this I get the following exception resulting from the query:
"Unable to create a constant value of type 'System.Collections.Generic.IEnumerable'1'.Only primitive types('suchas Int32, String, Guid') are supported in this context."
Why is this happening? Is this the right approach? (Note that my final sp will be more complex, and I will be returning multiple classes from the select in 'ret')
Use EF Extensions
Stored procedures are really badly supported in EF. Even if they return entity results, they don't provide any name mappings, so you have to rename columns in stored procedures yourself.
But. There's project called Entity Framework Extensions that will make all kinds of different scenarios with stored procedures possible.
Using EF extensions you can use stored procedures in any way you want:
you can do column remappings in your custom materializer (so your stored procedure returns same columns as they are in the DB without the need to rename columns to entity property names)
you can return multiple result sets (great for 1:* and : relations)
you can use scalar stored procedures or even procedures that don't return anything
you can consume results from a stored procedure that returns multiple entities per row (when having 1:1 relation between two of them)
you can also use output parameters which is great if you create a stored procedure that does paging (returns a subset of records as entities and returns out parameters with total count)
etc.
You can do preety much anything. We've used EF Extensions with much success on a some project. We wrote our own materializers (basically a lamba expression) for entities that our stored procedures returned. And then we materialized their results.
I don't think EF4 will support stored procedures to this level anyway, so getting acquainted to EF Extensions is always valuable.
The EF in .NET 3.5 SP1 cannot map procs which return scalar values (in .NET 4.0 you can). The SP must return all the values necessary to materialize a full entity, which is likely more than just the ID.
Also, it's almost never correct to use the "join" reserved word in LINQ to Entities. You traverse relationships in your client schema instead.
Start by writing a proc which returns all values necessary for an entity type. Map that proc. Then do:
IQueryable<MyEntity> q = from e in Context.MyEntities
select e;
Then move on from there.