Using Entity Framework one often writes queries such as
var orders = from o in context.Orders.Include("Customer")
where o.OrderDate.HasValue && o.OrderDate.Value.Year == 1997
orderby o.Freight
select o;
What really makes my stomach churn is the "Customer" string argument. I have a hard time believing that EF does not generate table names as constants somewhere. Does anyone know a better approach than to using a string? for the Include fetch option?
EF 4.1 has strongly typed version of Include usable for IQueryable, ObjectQuery and DbQuery. Once you add reference to EntityFramework.dll (EF 4.1) you can add using System.Data.Entity and use eager loading with lambda expressions
// get Orders with related Customers
var orders = from o in context.Orders.Include(o => o.Customer) ...
Edit:
If you don't want to use EF 4.1 check this article. I already used in my project and I'm happy with it.
IMO GetType might help you other than .edmx file where all the definitions is stored,
context.Orders.Include(CustomerEntity.GetType.Name or full name )
How about
Include(context.Customers.EntitySet.Name)
?
You can create a Text Template which will allow you to generate the code in addition to EF's default code. You can do this by right click and clicking "Add Code Generation Item".
In this text template, you can create your constants as you need in "CustomerProperties" and create constant name for each navigation property.
http://msdn.microsoft.com/en-us/data/gg558520
Related
I am using EF4, and I using the System.Data.Entity dll in my class. However, I can't see the load method of my navigation property of my entity.
How can I access to this method?
What I am doing is create e 4.0 project of .NET, create my edmx from a database, right click in the model edmx and I generate a DBContext.
However, I can access to the local property of my context, that I think that is a feature of EF4.
Thanks.
Daimroc.
For the DbContext approach, the IsLoaded property and Load method have been moved:
context.Entry(yourEntity)
.Collection(entity => entity.NavigationProperty)
.IsLoaded;
context.Entry(yourEntity)
.Collection(entity => entity.NavigationProperty)
.Load();
Entry method: http://msdn.microsoft.com/en-us/library/gg696578(v=vs.103).aspx
Collection method: http://msdn.microsoft.com/en-us/library/gg671324(v=vs.103).aspx
IsLoaded property: http://msdn.microsoft.com/en-us/library/gg696146(v=vs.103).aspx
Load method: http://msdn.microsoft.com/en-us/library/gg696220(v=vs.103).aspx
When you write the query (all this also works with lambda syntax as well btw)
From a in context.EntitySet.Include("Navigation Property NAme")
select a
and all will be there ;) .. or more to the point the navigation properties will be populated.
can be wierd with lots of joins though
you can also
From a in context.EntitySet.Include("Navigation1.Navigation2.Navigation2")
.Include("SomeOtherNavigation")
select a
where navigation1 and SomeOtherNavigation are navigation properties on the entity set
Failing that you can turn on lazy loading in the context. But its evil imo.
Also it doesnt really scale well when you go for an SOA approach as you need to include prior to knowing about the lazy load in your repository.
Ie you run the query in your repository, send stuff over the service and then want the navigation properties in the front end ... by which time its too late to lazy load ... EG you are using a wcf service that doesn't use data services.
Important to note that you ahve to use Include on the EntitySet, it is not on an IQueryable<>.
A real example:
var csg = from ucsg in c.UserCarShareGroups.Include("UserCarShareGroups.User.UserVehicles")
where ucsg.User.UserName.ToLower() == userName.ToLower()
&& ucsg.CarShareGroup.Carpark.Name.ToLower().Equals(carparkName.ToLower())
&& ucsg.DeletedBy == null
select ucsg.CarShareGroup;
(the database has case sensitive collation for some reason)
An alternate approach (and maybe more relevant is here)
Entity Framework - Trouble with .Load()
However I like doing it the way I said because it is explicit and I know exactly what is being pulled out. Especially when dealing with large datasets.
An answer to a question that combines my concerns with Load() is here:
Entity Framework 4 load and include combine
Apologies if this isnt phrased very well, but after upgrading to VS2012/.NET 4.5, I know Table Valued Functions are possible in Entity Framework 5.
We use a custom datalayer / orm, and I cant find any code examples that dont use the EDMX model generator as this is of no use.
As a very wild guess I would say some code that defines the table value function will need adding in OnModelCreating(DbModelBuilder modelBuilder) .
Any help appreciated.
Table valued function are not supported for Code-First strategy, only for Database-First with EDMX: http://msdn.microsoft.com/en-us/hh859577. Quote:
TVFs are currently only supported in the Database First workflow.
I was able to easily execute a Table Valued Function using EF 5 as follows:
int orderID = 100;
var query = context.Database.SqlQuery<Product>("Select * from [dbo].[tfn_GetOrderProducts](#p0)", orderID);
var results = query.ToList();
where Product can be any POCO class whose member names match up with the results of the table valued function.
This isn't a perfect solution -- it's not returning an IQueryable, so you can't use this as part of a larger LINQ query; however, in this case, it was all I needed.
the following suggestion was removed from this link http://blogs.msdn.com/b/adonet/archive/2011/06/30/walkthrough-table-valued-functions-june-ctp.aspx. I've found this information that may be usefull to you.
Code First Approach
Entity Framework June 2011 CTP does not include Code First support for
TVFs. However, you can use DbContext against your TVFs. You can do
this by adding the DbContext template to your model. The steps to add
the template are the following:
Open NorthwindModel.edmx and right click on the canvas
Click on Add Code Generation Item…
Select ADO.NET DbContext Generator V4.2, enter a name for your template, and click Add
I have an entity called File, I created a partial class with a property on it.
I was able to use this property in regular constructors and other statements. But this property is not accepted in any linq statement.
I am getting the following exception.
Exception
The specified type member 'FileStatus' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
Code
var fileEntry = from entry in context.ICFiles
where entry.FileName == FileName &&
entry.FileStatus == FileStatus.InProgress
select entry;
fileEntry.First().FileStatus = FileStatus.Completed;
// This is where I get the exception
Property Definition
public partial class ICFile
{
[DataMemberAttribute()]
public FileStatus FileStatus
{
get
{
return (FileStatus)this.Status;
}
set
{
this.Status = (int)value;
}
}
}
I think the problem is that the entity framework is trying to convert your FileStatus property to a column in the database, where it does not exist and there is no mapping information for it to go on as you have defined it in a partial class.
I see that you are trying to cast to an enumeration, because presumably you are using an Entity Framework version before the version 5 beta that apparently has enum support (I may be wrong on that, perhaps it is still missing).
Sadly enumerations do not really work very well in the current Entity Framework version (4.3.1). There are various kludges you can do but none are very satisfactory and you often end up with accidentally loading the entire table into memory or something equally horrendous (see Using ADO.net Entity Framework 4 with Enumerations? How do I do it?).
Personally I would just not bother with the partial class and do your query like this, to keep things simple:
var fileEntry = from entry in context.ICFiles
where entry.FileName == FileName &&
entry.Status == (int)FileStatus.InProgress
select entry;
fileEntry.First().Status = (int)FileStatus.Completed;
Not ideal, but it works and it shows what is happening and you know that the where clause will be correctly translated to an equivalent bit of SQL to run against the database (I think that is called "pragmatic").
I don't think the way you use Enums in your code example is causing any issues. The problem is simply that your class extension property is not recognized by EF as a member of ICFiles since it is not really in your edmx model nor a column in your database table. Use the code example user1039947 gave you and you should be fine.
You can however use your original query when doing a LINQ to Object search on an ICFiles IEnumerable instance. This is because LINQ to Entities is translated into SQL before fetching data from the database, while LINQ to objects is done in memory. Your FileStatus property does not exist in your database, only in memory.
Right when I asked the question, I was left with the answer that #user1039947 has posted. However, that's not the one I was expecting as an answer for this question.
I was looking for the ability of Entity framework to utilize the Enum completely. But eventually I figured out that Linq to Entity can't recognize the Enums (it usually recognizes the entities through an attribute starting with Edm, like EdmTypeAttribute, EntityPropertyAttribute etc.)
Linq to Entity of Framework 4.0, has no way to accept the enums as entity object. However, the partial method serves us 80% what we might need from enum.
I have posted a blog with more detail on this.
Please forgive me if I don't explain this very well. First off, I am using NHibernate 2.0 with .NET 3.5. Basically I have an entity "EntityA" (for simplicity) with one or more children of type EntityB. Each EntityB has a number indicating how recently it was created. I would like to delete all but the x most recent EntityB. This forms part of a purge operation.
I am struggling to see an efficient way of doing this, The problem is that the EntityB instances are actually quite complex and could have hundreds of child objects themselves. The list of EntityB on EntityA is lazily loaded and I would ideally like to avoid loading it in memory if possible.
I tried passing an HQL query to Session.Delete. Unfortunately HQL doesn't seem to support the top statement so I cannot do a subselect to choose which ones not to delete.
The cascades are set up in NHibernate and not in the database. I'm not sure but I wonder if NHibernate will load the whole object graph even if the delete is done via HQL.
Any advice would be appreciated.
[Edit]Unfortunately any query must be HQL not SQL since it needs to be database independent[/Edit]
Cheers,
James
From my experience with nHibernate I don't think you're going to find a clean solution for this. But, it doesn't matter. Stepping outside the framework is only a bad thing if the framework offers a viable alternative. Use a parameterized SQL statement, make sure it's clear in the code where and why you're doing this and it'll be a great solution.
EDIT:
I'm fairly certain you could come up with a database independent SQL query for this but anyway... Instead of trying to use a top statement try using MAX() (assuming there is such a function in HQL) to grab the top item id and then structure a delete statement with a conditional that the id is not the MAX id.
You should be able to use HQL delete Child c where c in (:list) where list is a copy of the list of children with only the to-be-removed elements included. list may be obtained through an HQL query such as from Child c where c.Parent = :parent - HQL queries apparently do not obey the mapping's fetch strategy (lazy vs eager) and will only fetch children eagerly when instructed to fetch children eagerly (so just don't put in the left join fetch c.SubChildren) - and then filtered to include only the elments to be removed.
If nothing else you could set up the cascades in the database and just do
Session.CreateSQLQuery([SqlStatement]).SetParameter([ParameterStuff]).ExecuteUpdate();
I always try to keep cascades, default values, etc set up in both the database and nhibernate for cases like this when you need to do something directly to the database.
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