HasDbFunction, table value function without a mapped entity class - c#

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.

Related

How to return anonymous objects as the result of LINQ-to-SQL queries

I'm creating a tool that allows users to run SQL SELECT commands and the results will be displayed in an HTML table. I am using ASP.NET Core MVC and trying to avoid using SqlConnection/SqlCommand/etc. if possible, going straight through DataContext.
For example, if someone types in
"SELECT Id, Name FROM Table"
and the results are
Id | Name
------------
1 | 'Bob'
2 | 'Harry'
I want Json(results) to be
[
{ Id: 1, Name: "Bob" },
{ Id: 2, Name: "Harry"}
]
where I suppose that results would be of type IEnumerable<object>.
What I have now is a method in my repository like
public List<object> RunQuery(string query)
{
using(MyDbContext context = new MyDbContext())
{
return context.MiscTable.FromSql<object>(query).ToList();
}
}
but then I get an exception like
$exception {System.InvalidOperationException: The required column 'SomeColumn' was not present in the results of a 'FromSql' operation.
where SomeColumn is some column on MiscTable. So apparently I can only query specific tables using FromSql. Is there a way to do what I'm trying to do?
You can't use .FromSqlwith ad-hoc models. The fields returned from the query must exactly match the model (no missing fields).
Ad-hoc/non-entity models are not supported yet in EF Core but are on the roadmap.
https://github.com/aspnet/EntityFramework/wiki/Roadmap
Raw SQL queries for non-Model types allows a raw SQL query to be used to populate types that are not part of the model (typically for denormalized view-model data).
Also anonymous classes can't be returned, since their type is only know at compile type. You should create models for your entities in the database (usually 1 model per table, but you can also have more models in one table via inheritance). That's what ORM is for.
If you just want raw queries, use ADO.NET directly. ORMs are there for mapping tables to classes/objects.
I believe you can use .Include() to add additional tables to the dbset your FromSql operates on. Check out this link for examples and additional context.
https://learn.microsoft.com/en-us/ef/core/querying/raw-sql#composing-with-linq
From your example, your query might look like this:
return context.MiscTable.Include(t => t.RelatedTable).FromSql<object>(query).ToList();
Disclaimer: I didn't actually try this, so I'm not sure it will actually work. But it seems reasonable.

Get EF Table by type (parameter)

I have this very old database with all the tables named like this ANAGT*postfix* and all the columns are namede like this *prefix*_*column name*.
I have to query all the tables with the same query more or less, but obviously the names of the columns are all different so I would have to do some trick with a sql statement with the strings, but I prefer to get a strong typed exception in case of errors using the EF.
So I've created some interfaces and implemented them on partial classes from the EF model
public partial class *table name* : IIdentificable, ICountabile {}
IIdentificabile means that there's a method with a common name that gets all the ids (instead of query *prefix*_ID), so when I'll have to query the table I'll just have to do
var result = from elem in myContext.[Table]
where elem.GetId() == 1
select elem;
I've created a generic method that gets all the types from a certain interface (IIdentificable for instance) and now I have to get the EF table by type.
var tablesType = typeof(myModel).Assembly
.GetExportedTypes()
.Where(t=>t.IsAssignableFrom(typeof(T)));
foreach(Type t in tablesType){
var query = from elem in myContext.GetTable(t) //and here's what I can't do
select elem.GetId() == 1
select elem;
}
I think another approach may be better. In the EF Model you can rename the C# Objects and properties. Using the properties of a selected property in the model, you can change the Name to get rid of the prefix. Similarly, the properties of a selected table in the model allow you to change both the Entity Set name (the name of a collection of them) and the Name of the C# class. The names of the underlying database tables and columns aren't affected.

How to find the mapping tables in from the EF generated Classes

I have a mvc 4 application which is entity framework database first based. Therefore lots of the classes are generated based on database tables via EF.
Currently, I created a method to retrive data from a table and display them in a dropdownlist, which works fine. However, I am thinking to modify that method to a generic type method, and passing the class type as the method parameter, and I would like c# code to find the mapping table in the database and retrive the data.
Following is my modified method
Thanks for your helps!
public static SelectList FromDbTableToSelectList<T>() where T: class
{
var db = new TableEntities();
//find the mapping database table
Dictionary<Guid, string> dic = db.MappingTable.ToDictionary(v => v.tableColumn1, v => v.tableColumn2);
return new SelectList(dic, "Key", "Value");
}
I don't understand your what your code does. However, there is a trick to find mapping table name (Entity Set Name in EF glossary) of an entity.
Suppose that you have a table in your db named Products. If you create your model with the Pluralize and singularize option checked, EF will create an entity class named Product.
Then, whenever you perform a query, EF naming convention thinks that your table name is dbo.Products. This is a general rule - in fact a naming convention.
From the other hand, if you uncheck the Pluralize and Singularize option, EF creates an entity class named Products for you and in this case, the class an the table have same names.
So, according to the selection of P&S option, you can infer table names from entity names and you don't need any code for it...

Entity Framework - get multiple results from an SQL statement

With EF I can return a collection of objects like so
entities.Customers.ToArray();
And I can include other tables, so I can effectively get 2 result sets back in the one query
entities.Customers.Include("Invoice").ToArray();
or if I have some custom SQL I can achive a similar result:
SqlDataReader reader = GetReaderFromSomewhere("SELECT * FROM Customer");
entities.Translate<Customer>(reader).ToArray();
But how do I get multiple results back from my own SQL? What I was thinking was something like this
SqlDataReader reader = GetReaderFromSomewhere("SELECT Customer.Name AS CustomerName, Invoice.Number AS InvoiceNumber FROM Customer JOIN Invoice ON Customer.ID = Invoice.CustomerID");
entities.Translate<Customer>(reader).Include<Invoice>().ToArray();
In the above example I have prefixed all the returned data with the table name so that the Translate method can know which columns belong to which tables. I'm presuming the Tranlate method does not support this but EF must do something similar when the include method is called. So my question is, how can I get the functionality of Include when using Translate?
AFAIK you can not do that manually. What EF does is, it dynamically generate a class based on the LINQ query(and the Includes) that can materialize entities. Basically this class knows which columns will be mapped to which properties.
However you can use a micro ORM like Dapper which can do Multi Mapping. But this will only work for querying. So change tracking and CUD operations will not be available.

Entity Framework - get records in multiple tables using stored procedure

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.

Categories

Resources