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.
Related
I am new to Entity Framework and Linq, I would like to understand how dbset/dbcontext works when executing the following kind of LINQ request:
from x in db.Products select x
db is the data context object, Products the dataset.
Is the search/loading of these records done directly at the base table level or is a first search done on the dbset? And then we complete and retrieve the records that are not yet tracked/in the dbset?
In other words what is the path of the loading of these elements?
Thank you,
Is the search/loading of these records done directly at the base table level or is a first search done on the dbset?
It's done so at the "base table level" in the Database Server by receiving the results directly from the query and then DataContext will return any existing entity that it already have track of and create new entities if it doesn't have any track for those records.
And then we complete and retrieve the records that are not yet tracked/in the dbset?
The DataContext will create new entities for those records and it'll be tracked if you didn't explicitly specify AsNoTracking.
In other words what is the path of the loading of these elements?
The way it works from this document is that when you make a LINQ Query like this:
from x in db.Products select x
It will generates a LINQ Expression and then pass that expression to the Database Provider to generate the actual database query specific to the database engine it's made for (it may not have all of the query compiled, so some of the query may be computed from the application side.)
It will then execute the query and receive the result from that query and if the query is made with tracking, then it'll return any existing entity that the DataContext already have track of and create new entities if not.
The entity and record will be tied by the key and whenever there is any part of the query using Keyless Entity Type, the whole query would be made as a NoTracking query.
Note that if the database record for such existing entity have changed, it will not update the values in the existing entity, you will have to manually reload that entity like so:
db.Entry(product).ReloadAsync();
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.
I have read mythz's post here about how ORMLite can read anything up from SQL and fit it into a POCO of the same shape. That is great.
On the other hand, how does ORMLite handle these "View POCOs" when saving them back into the database? Since they are not tables, they may be views or they may be just any sql select queries like that:
var rows = dbCmd.Select<ShipperTypeCount>(
"SELECT ShipperTypeId, COUNT(*) AS Total FROM Shippers GROUP BY ShipperTypeId ORDER BY COUNT(*)");
There's nothing special about the POCOs you use with OrmLite, they're not tied or related back to any underlying tables and there's no hidden magic state that OrmLite caches in between calls so it knows what fields map to.
With every DB call OrmLite just uses the POCO to create the appropriate SELECT, INSERT, UPDATE or DELETE statement based on the schema definition of the type. The INSERT Apis shows some examples of this.
It's best to think of OrmLite as just turning your POCO into an SQL statement, which is what it does. So trying to insert a ShipperTypeCount will attempt to insert a record into a table called ShipperTypeCount unless it has an [Alias("UseTableNameInstead")] attribute which it will use instead.
I have the following scenario: there are a database that generates a new logTable every year. It started on 2001 and now has 11 tables. They all have the same structure, thus the same fields, indexes,pk's, etc.
I have some classes called managers that - as the name says - manages every operation on this DB. For each different table i have a manager, except for this logTable which i have only one manager.
I've read a lot and tried different things like using ITable to get tables dynamically or an interface that all my tables implements. Unfortunately, i lose strong-typed properties and with that i can't do any searches or updates or anything, since i can't use logTable.Where(q=> q.ID == paramId).
Considering that those tables have the same structure, a query that searches logs from 2010 can be the exact one that searches logs from 2011 and on.
I'm only asking this because i wouldn't like to rewrite the same code for each table, since they are equal on it's structure.
EDIT
I'm using Linq to SQL as my ORM. And these tables uses all DB operations, not just select.
Consider putting all your logs in one table and using partitioning to maintain performance. If that is not feasible you could create a view that unions all the log tables together and use that when selecting log data. That way when you added a new log table you just update the view to include the new table.
EDIT Further to the most recent comment:
Sounds like you need a new DBA if he won't let you create new SPs. Yes I think could define an ILogTable interface and then make your log table classes implement it, but that would not allow you do GetTable<ILogTable>(). You would have to have some kind of DAL class with a method that created a union query, e.g.
public IEnumerable<ILogTable> GetLogs()
{
var Log2010 = from log in DBContext.2010Logs
select (ILogTable)log;
var Log2011 = from log in DBContext.2011Logs
select (ILogTable)log;
return Log2010.Concat(Log2011);
}
Above code is completely untested and may fail horribly ;-)
Edited to keep #AS-CII happy ;-)
You might want to look into the Codeplex Fluent Linq to SQL project. I've never used it, but I'm familiar with the ideas from using similar mapping techniques in EF4. YOu could create a single object and map it dynamically to different tables using syntax such as:
public class LogMapping : Mapping<Log> {
public LogMapping(int year) {
Named("Logs" + year);
//Column mappings...
}
}
As long as each of your queries return the same shape, you can use ExecuteQuery<Log>("Select cols From LogTable" + instance). Just be aware that ExecuteQuery is one case where LINQ to SQL allows for SQL Injection. I discuss how to parameterize ExecuteQuery at http://www.thinqlinq.com/Post.aspx/Title/Does-LINQ-to-SQL-eliminate-the-possibility-of-SQL-Injection.
I have a standard self referencing table of Categories. In my entity model I have made associations Children and Parent. Is it possible to load the whole Category object without lazy loading?
if I use the code below, it loads only to the second level.
db.Categories.MergeOption = System.Data.Objects.MergeOption.NoTracking;
var query = from c in db.Categories.Include("Children")
where c.IsVisible == true
orderby c.SortOrder, c.Id
select c;
Is it possible to load references if I have all the category objects already loaded?
One method to load it is to add the Children property multiple times
db.Categories.Include("Children.Children.Children.Children.Children")
but this generates a very long insane T-SQL code and also it doesn't do what I want.
No, it isn't possible. Consider: All LINQ to Entities queries are translated into SQL. Which SQL statement includes an unlimited depth in a self-referencing hierarchy? In standard SQL, there isn't one. If there's an extension for this in T-SQL, I don't know what it is, and I don't think EF providers do, either.
Ok, you might consider using Load method.
if (!category.Children.IsLoaded)
category.Children.Load();
Of course, category entity need to be tracked by ObjectContext.
There is better explanation here how-does-entity-framework-work-with-recursive-hierarchies-include-seems-not-to.
One way I have used to implement that if I have several entities I want to get all children in self-referencing table is to use recursive cte and stored procedure to get their ids using Entity FrameWork :
Here is the code using Entity Framework and code first approach