On previous efforts with old versions of Entity Framework, I'm used to using Eager Loading - so you get your root entity and then gather related entities as required by the use of "Include".
On my current project we've implemented the latest version of EF on a new database, using database-first. Take this class, for example:
public partial class Zone
{
public Zone()
{
this.OverrideCharges = new HashSet<OverrideCharge>();
}
public System.Guid RowId { get; set; }
public string Name { get; set; }
public virtual ICollection<OverrideCharge> OverrideCharges { get; set; }
}
The OverrideCharges object also has a number of sub-properties underneath it, with related entities under those.
We have two contexts, the actual DB context and a set of DTO contexts. The objects for the latter are mostly copies of the former - the Zone_dto object is pretty much a clone of the original. In both, I have turned off Lazy Loading by using:
public CContext(): base("BreezeMetaData")
{
this.Configuration.LazyLoadingEnabled = false;
}
public UDBEntities()
: base("name=UDBEntities")
{
this.Configuration.LazyLoadingEnabled = false;
}
Now, I query my Zone objects by doing this:
public List<Zone_dto> GetZones()
{
List<Zone> zones = _cilContext.Zones.ToList();
List<Zone_dto> zone_dtos = new List<Zone_dto>();
foreach (Zone zn in zones)
{
zone_dtos.Add(Mapper.Map<Zone, Zone_dto>(zn));
}
return zone_dtos;
}
So - no includes. And Lazy Loading is disabled. I would expect to get back a list of Zone objects and their direct properties, but not any related entities.
But what I get back are the Zone objects, plus all their OverrideCharges, plus all the related entities to those OverrideCharges and so on, all the way down the tree.
These data objects are not huge, and it's not a massive problem. But I'm frustrated that I don't understand why I'm getting back all this data that I haven't asked for. Can someone explain?
What you're describing is exactly what I'd expect - you seem to have Lazy and Eager Loading backwards.
Lazy loading means that the context won't load everything - it's lazy, because it does less work, and loads only what you ask for specifically. .Include() is needed when there is lazy loading, because you're telling it not to be lazy about the thing you're Include()ing.
Eager loading means that the context will load all the things you might need, before you ask for them, by following links from the item you requested and loading whatever they lead to. With lazy loading turned off, it doesn't need to be told to Include() things because it will load everything by default. Use of .Include() is referred to as eager loading because you're telling it to use eager-loading behaviour for that property; with eager loading on everything by default, you don't need .Include().
Set LazyLoadingEnabled = true; and see what happens.
It may be because of the virtual keyword. A virtual ICollection will be lazy-loaded.
See this SO link.
Related
I stumbled upon unexpected behaviour when using Entity Framework with PostgreSql.
When I query context with navigation property inside where clause, its always null and fails. But If I add there Include method pointing to navigational propery it's working
this will work
context.Garages.Include("PostalCode").Where(f=>f.PostalCode.RegionId == regionId)
this will not (PostalCode is null and fails on NullReference)
context.Garages.Where(f=>f.PostalCode.RegionId == regionId)
I don't think I had to add this to query when using MSSQL. Can anybdoy explain this to me.
If you want that your navigation properties be lazy loaded, then you need to declare them as virtual:
public Garage
{
//...
public virtual PostalCode PostalCode {get;set;}
}
In this link you will find the conditions that must follow your entities if you want to enable lazy loading for your entities and to have the Entity Framework track changes in your classes as the changes occur.
If that navigation property is already virtual, the other option that I think could cause that behavior is if you turn off Lazy Loading on your context:
public class YourContext : DbContext
{
public YourContext()
{
this.Configuration.LazyLoadingEnabled = false;
}
}
If you call the Include method, you are going to load the related entity as part of the query. This load behavior is called Eager Loading. On the other hand, if you use Lazy Loading, the related entity is going to be loaded the first time that is accessed, that is the behavior that you looking for.
Im writing an web application with MVC using Entity Framework for my backend logic. My problem is that I have an entity that has certain fields that should never be changed on an update. I am not really sure what the best way to solve this problem would be. There is going to be a lot of data processed in my application, so I cant afford to just hack up a solution.
Is it possible to just define the fields as readonly in the POCO entities ? Or should I write and entity framework extension class that validates all updates. Could it be done in the mapping files between EF and the actual database?
I am relatively new with EF, so I hope some of you might be able to give me some pointers!
Thanks!
If you are using .NET 4.5 and EF 5 (i.e. MVC 4), you can simply set IsModified = false on the individual properties in question. This has the benefit of sticking close to the default out-of-the-box MVC conventions.
For example, if you have a CreatedBy field that shouldn't be touched when the record is updated, use the following in your controller:
[HttpPost]
public ActionResult Edit(Response response)
{
if (ModelState.IsValid)
{
db.Entry(response).State = EntityState.Modified;
db.Entry(response).Property(p => p.CreatedBy).IsModified = false;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(response);
}
Note that the IsModified line is the only change from the default controller action.
You MUST put this line AFTER setting .State = EntityState.Modified (which applies to the record as a whole and adds the record into the db context).
The effect is that EF will not include this column in the SQL UPDATE statement.
I am still (very) shocked that there are no [InsertOnly] or [UpdateOnly] attributes similar to [ReadOnly]. This seems like a major oversight by the MVC team. Am I missing something?
I'm not fully satisfied with this solution because it's a hack: You're telling EF that no change was made when what you really mean to say is "HANDS OFF". It also means that you have to use this code anyplace where the field could be updated. It would be better to have an attribute on the class property.
(Apologies for posting to an older thread, but I've not see this solution anywhere else. ViewModels are robust but a lot of work, and EF was supposed to make things easier, not harder...)
Well I would advice against ever using the EF classes in the View. You're best bet is to construct ViewModel classes and use Automapper to map them from the EF classes.
When you are updating records in the database though, you can control which fields in the ViewModel are used to update the existing fields in the EF class.
The normal process would be:
Use the Id to get the latest version of the existing object out of the database.
If you are using optimistic concurrency control then check that the object has not been updated since the ViewModel was created (so check timestamp for example).
Update this object with the required fields from your ViewModel object.
Persist the updated object back to the database.
Update to include Automapper examples:
Let's say your POCO is
public class MyObject
{
public int Id {get;set;}
public string Field1 {get;set;}
public string Field2 {get;set;}
}
and Field1 is the field you don't want updating.
You should declare a view model with the same properties:
public class MyObjectModel
{
public int Id {get;set;}
public string Field1 {get;set;}
public string Field2 {get;set;}
}
and Automap between them in the constructor of your Controller.
Mapper.CreateMap<MyObject, MyObjectModel>();
you can if you wish (although I prefer to do this manually, automap the other way too:
Mapper.CreateMap<MyObjectModel, MyObject>().ForMember(dest=>dest.Field1, opt=>opt.Ignore());
When you are sending date to your website you would use:
var myObjectModelInstance = Mapper.Map<MyObject, MyObjectModel>(myObjectInstance);
to create the viewModel.
When saving the data, you'd probably want something like:
public JsonResult SaveMyObject(MyObjectModel myModel)
{
var poco = Mapper.Map<MyObjectModel, MyObject>(myModel);
if(myModel.Id == 0 )
{
//New object
poco.Field1 = myModel.Field1 //set Field1 for new creates only
}
}
although I'd probably remove the exclusion of Field1 above and do something like:
public JsonResult SaveMyObject(MyObjectModel myModel)
{
var poco;
if(myModel.Id == 0)
{
poco = Mapper.Map<MyObjectModel, MyObject>(myModel);
}
else
{
poco = myDataLayer.GetMyObjectById(myModel.Id);
poco.Field2 = myModel.Field2;
}
myDataLayer.SaveMyObject(poco);
}
note I believe that best-practise would have you never Automap FROM the ViewModel, but to always do this manually, including for new items.
I just asked a very similar question, and I believe the answer to that one may help out a lot of folks who stumble across this one as well. The OP mentions that these are fields that should never change, and using PropertySaveBehavior.Ignore ensures this. With the existing answers to this question, you need to make custom save methods or introduce mapping where it might not make sense. By setting the AfterSave property behavior instead, you can prevent this from being possible in EF altogether.
In my project, I am generically accessing a property that is on an abstract class so I have to set it like this:
MyProperty.SetAfterSaveBehavior(PropertySaveBehavior.Ignore);
If you're accessing it directly on a known class, you'd use this:
...
.Property(e => e.YourProperty)
.Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Ignore);
While working with EF code first I get error given below at different times:
The entity type SomeType is not part of the model for the current context.
What are the possible causes of this error?
It may occur because:
DbContext configured with an incorrect connection string
The entity specified is actually not mapped in configuration
I got this when my class that inherited from DbContext did not declare the model as a property. For example, I neglected to add a property for FooModel in the code below:
public class MyDBContext : DbContext
{
public DbSet<FooModel> FooModels{ get; set; }
// etc. ...
}
This message also appears if you try to do somthing such a set an EntityState on a child collection in a one-to-many association.
For example; if a one-to-many association exists between ParentEnt and ChildEnt in the code snippet below, the error message:
The entity type Hash1Type is not part of the model for the current context.
MyDbContext.Entry(ParentEnt.ChildEnt).State = EntityState.Unchanged;
The following change does not produce an error:
MyDbContext.Entry(ParentEnd.ChildEnt.First).State = EntityState.Unchanged;
Note that the use of First() in this case may indicate t
This can also be caused by properties on your POCO not named EXACTLY as they are in the EDMX/modelbuilder. Please see my post here for details on how I trouble shot the issue.
The entity type <class> is not part of the model for the current context
I had this error.
It turned out I had added a new field to a db View a few hours before. I updated the context (as part of something else I was doing) and got this error.
When I updated the POCO's all was well: EF threw this error because it could not map a field in the View to a property in the POCO of the View.
Not the most helpful error message in this situation IMO.
It may happen when your model is not mapped correctly to your Class. In my case I got this error when I used EF Model First and when I updated my EDMX model from DB but didn't update my Entity class. Specifically a property in Entity was in lower case while in DB and EDMX diagram was in Upper case.
And another issue I had was a model property in EDMX diagram was not converted to my app Enum So that EF couldn't recognize that Entity.
I've been doing database first and using the built in template generation for my models (EF 4.1)
I copied the generated code into a new file and removed the navigation properties. That's when I started to see this error. I have lazy loading turned off, but it appears the navigation properties are still necessary in the POCO's.
I suppose the error might indicate that your model is missing something.
namespace TestApp.BLL
{
using System;
using System.Collections.Generic;
public partial class User
{
public User()
{
//this.Roles = new HashSet<Role>();
}
public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
//public virtual ICollection<Role> Roles { get; set; }
}
}
The above code shows the navigation properties commented out. If I uncomment them on all POCO's (that means the Role POCO too) the exception goes away.
UPDATE
This error kept attacking me with various updates I made to the database. Eventually I deleted the edmx file and just created it again with the same tables and stored procs.
Old
I got this when the generated entity was missing a nullable column:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MyProgram.Models
{
using System;
using System.Collections.Generic;
public partial class Question
{
public int id { get; set; }
public string title { get; set; }
public string body { get; set; }
public string tags { get; set; }
public int votes { get; set; }//I had to manually add this property which is nullable int in the database
}
}
I added the property after generating the initial model. However, I even tried deleting the table and recreating it. That didn't fix it. Only adding the property manually fixed it for me.
Answering the question is "What are the possible causes of this error?":
This error seems to occur any time the internal/EDMX model is not successfully built, or is not completely built. And there are a large potential number of causes for this problem. It's unfortunate that there seems to be insufficient error reporting or error detection when building the model, so solving it seems to involve trying a bunch of things to see what makes the problem go away.
I ran into another instance of this error over the past few days, using EF 6.0 (currently pre-release code), code-first, and custom conventions. It turns out that the root cause is that I had a custom convention which renames the ID EdmProperty (eg ID -> MyTypeId). If I disabled my custom convention this problem went away; if I enabled my convention the problem occurs. There is no logging or exceptions or other errors to indicate that a problem occurred when the building the model. This exception doesn't rear its head until I try to add an entity to a DbSet. The convention didn't cause any problem when generating the database (via Migrations).
In my scenario I was using EF6 to migrate a MySQL database to MSSQL. I had 2 separate models and contexts, each with their own connection string. The classes had the same name, but the MySQL one was all lowercase and the MSSQL one Pascal casing. I had copied in both connection strings from the relevant assemblies containing my EDMX models. But when I ran my application I got an error about one of the 2 connection strings having been already added to the dictionary list.
So I removed the conflicted entry, foolishly thinking it somehow had access to the connection string in the assembly's own app.config (it's late!). But no, the error was actually happening because both connection strings also had the same name - one all lowercase and one in Pascal casing - i.e. the Dictionary key ignores the casing. So when I removed the MySQL one, it then attempted to use the MSSQL connection string for BOTH models. So I had to re-add, rename and manually set the connection string for the second model in code.
I am pretty new to entity framework, but given a simple object like this:
public class Country
{
public string Name { get; set; }
[Key]
public string Code { get; set; }
public bool IsPostalCodeRequired { get; set; }
public ICollection<Province> Provinces { get; set; }
}
returned by a DbContext, the Provinces property is null. If I use the Include method on my linq statement to include provinces, then it works. But I was wondering if there's a way to load them when I actually access the property? I know there's performance concerns to think about here, but I just want to know how to do it.
Thanks,
Make sure ObjectContext.ContextOptions.LazyLoadingEnabled is true. This is the default for a new project.
If using pure POCO entities, lazy loading can't work (think about it). So you can use POCO proxies. Again, this is the default, but making lazy loading work with POCO proxies requires that all the relationship properties are declared virtual, and yours aren't.
Craig said it all. Just wanted to say by default ObjectContext has LazyLoading turned off . Although when you create new .net 4.0 project, the model designer explicitly turns it on for u for .net 4.0 projects. Its turned off because EF needs to preserver legacy behavior of .net 3.5 when lazy loading was not available. However as you may notice that you are using Dbcontext which is new and has no dependency to .net 3.5. Hence LazyLoading would be enabled by default to give you seamless experience.
This questions doesn't let me sleep as it's since one year I'm trying to find a solution but... still nothing happened in my mind. Probably you can help me, because I think this is a very common issue.
I've a n-layered application: presentation layer, business logic layer, model layer. Suppose for simplicity that my application contains, in the presentation layer, a form that allows a user to search for a customer. Now the user fills the filters through the UI and clicks a button. Something happens and the request arrives to presentation layer to a method like CustomerSearch(CustomerFilter myFilter). This business logic layer now keeps it simple: creates a query on the model and gets back results.
Now the question: how do you face the problem of loading data? I mean business logic layer doesn't know that that particular method will be invoked just by that form. So I think that it doesn't know if the requesting form needs just the Customer objects back or the Customer objects with the linked Order entities.
I try to explain better:
our form just wants to list Customers searching by surname. It has nothing to do with orders. So the business logic query will be something like:
(from c in ctx.CustomerSet
where c.Name.Contains(strQry) select c).ToList();
now this is working correctly. Two days later your boss asks you to add a form that let you search for customers like the other and you need to show the total count of orders created by each customer. Now I'd like to reuse that query and add the piece of logic that attach (includes) orders and gets back that.
How would you front this request?
Here is the best (I think) idea I had since now. I'd like to hear from you:
my CustomerSearch method in BLL doesn't create the query directly but passes through private extension methods that compose the ObjectQuery like:
private ObjectQuery<Customer> SearchCustomers(this ObjectQuery<Customer> qry, CustomerFilter myFilter)
and
private ObjectQuery<Customer> IncludeOrders(this ObjectQuery<Customer> qry)
but this doesn't convince me as it seems too complex.
Thanks,
Marco
Consider moving to DTO's for the interface between the presentation layer and the business layer, see for example:- http://msdn.microsoft.com/en-us/magazine/ee236638.aspx
Something like Automapper can relieve much of the pain associated with moving to DTOs and the move will make explicit what you can and cannot do with the results of a query, i.e. if it's on the DTO it's loaded, if it's not you need a different DTO.
Your current plan sounds a rather too tightly coupled between presentation layer and data layer.
I would agree with the comment from Hightechrider in reference to using DTOs, however you have a valid question with regard to business entities.
One possible solution (I'm using something along these lines on a project I'm developing) is to use DTOs that are read-only (at least from the presentation layer perspective. Your query/get operations would only return DTOs, this would give you the lazy loading capability.
You could setup your business layer to return an Editable object that wraps the DTO when an object/entity is updated/created. Your editable object could enforce any business rules and then when it was saved/passed to the business layer the DTO it wrapped (with the updated values) could be passed to the data layer.
public class Editable
{
//.......initialize this, other properties/methods....
public bool CanEdit<TRet>(Expression<Func<Dto, TRet>> property)
{
//do something to determine can edit
return true;
}
public bool Update<TRet>(Expression<Func<Dto, TRet>> property, TRet updatedValue)
{
if (CanEdit(property))
{
//set the value on the property of the DTO (somehow)
return true;
}
return false;
}
public Dto ValueOf { get; private set;}
}
This gives you the ability to enforce if the user can get editable objects from the business layer as well as allowing the business object to enforce if the user has permission to edit specific properties of an object. A common problem I run into with the domain I work in is that some users can edit all of the properties and others can not, while anyone can view the values of the properties. Additionally the presentation layer gains the ability to determine what to expose as editable to the user as dictated and enforced by the business layer.
Other thought I had is can't your Business Layer expose IQueryable or take standard expressions as arguments that you pass to your data layer. For example I have a page building query something like this:
public class PageData
{
public int PageNum;
public int TotalNumberPages;
public IEnumerable<Dto> DataSet;
}
public class BL
{
public PageData GetPagedData(int pageNum, int itemsPerPage, Expression<Func<Dto, bool>> whereClause)
{
var dataCt = dataContext.Dtos.Where(whereClause).Count();
var dataSet = dataContext.Dtos.Where(whereClause).Skip(pageNum * itemsPerPage).Take(itemsPerPage);
var ret = new PageData
{
//init this
};
return ret;
}
}