I have a POCO entity on which I have defined a custom constructor. I have also implemented the default constructor so that Entity Framework can successfully hydrate the object when I request a copy from the database.
This seems to work well but when I set the default constructor to private (to force my code to use the custom version) and request an entity from the database, I don't seem to be able to navigate over related entities as they are all null.
This seems to be a lazy loading problem so I could change my repository to eager load the related objects I need but am wondering if there is a better way to hide the default constructor from the client code whilst allowing Entity Framework to lazy load?
If you define private constructor you violate requirements for creating POCO proxy responsible for lazy loading:
A custom data class must have a public or protected constructor that
does not have parameters.
So the best option for you is using protected constructor or not using lazy loading.
Related
Here is my set up thus far:
Database-First Entity Framework (generates a EDMX)
Separated POCOs into a different project
Left Custom DbContext under EDMX to be used as a bounded Countext
Created a UOW
Created a generic Repository (IRepository)
Mapped each entity into the UOW through concrete implementations of IRepository
UOW instantiates the Custom DbContext created by the EDMX and passes it along to each Repository
So far all is well.
Now, the problem stems out of that last part. The UOW instantiates the Custom DbContext, injects it into the Repositories and everything works... for the most part.
Each Repository implementation takes the DbContext, creates a DbSet and exposes the usual CRUD operations. All Entities are accessible through generics, so if I want to implement say GetAll() I will simply return DbSet and it will already be mapped to the Entity .
BUT, when I try to access a Function Import from DbContext within the Repository... I CAN'T.
It makes sense that I can't of course: the generic repository takes a DbContext as input, but it knows nothing of the Custom DbContext created by the EDMX, thus, all such functions added into the Custom DbContext are not known to the DbContext within the repository.
In other words:
I can access the Function Imports from the Custom DbContext while in the UOW
I pass the Custom DbContext to the constructor of each Entity Repository
The Repository expects any class that derives from DbContext, so the Custom DbContext does the trick
When the Repository tries to access the Function Import through the DbContext is has access to, it can't, because it has no knowledge of them
Obviously I can't just use the Custom DbContext everywhere, because I would be marrying the Repository to a specific Custom DbContext, and I'll be needing more than one since I'm created several Bounded Context.
Alas, THE QUESTION:
How could I call the Function Import from within the Repository without marrying it to a specific Custom DbContext?
Workarounds:
I know I could use reflection, but I'm trying to stir away from it for performance reasons (I know it is not that terrible, but still... the present question is about finding out a better way).
I have managed to get what I needed using DbContext.SqlQuery() to execute the Stored Procedure (which was mapped to the Function Import in the EDMX). Yet again, since I can easily swap Function Imports in the EDMX, I would like to find a way to access it within the Repository.
Hope it all makes sense. I appreciate any light anyone can shed onto the matter.
It's not clear to me how would you know what function to invoke or what parameters it takes. However you can take a look at the code generated for function imports and it basically looks like this:
((IObjectContextAdapter)this).ObjectContext
.ExecuteFunction<Customer>("Customers_With_Recent_Orders", customerId)
Therefore (instead of using reflection) you can just do the same thing dynamically - given that even with reflection you would have to know what function to invoke, you should know the type of the entity you expect and you can use params to pass any number of parameters it should be doable.
** EDIT **
You can also execute the function as no tracking by using the other overload:
((IObjectContextAdapter)this).ObjectContext
.ExecuteFunction<Customer>(
"Customers_With_Recent_Orders",
MergeOption.NoTracking,
customerId)
I have been using an AutoMapper and it seems that it gets me all the child entities (even if I don't specify them in "Include()" clause). Is there any way how to make lazy loading possible and get child properties only if I specify them.
Thank you,
Jakub
After mapping you will have mapped object without any references to source entity (which holds database context for lazy loading). Only property values are copied to destination entity. So you will not be able to do any lazy-loading without source entity.
Actually lazy loading works just fine for you - and it's occur during mapping process. You specified mappings for lazy-loaded properties of your entity, and mapper tries to get those values. That results in lazy-loading all navigation properties which you have configured for mapping. This is very inefficient. To disable lazy-loading during mapping you can ignore navigation properties in mapping configuration. E.g. if you have customer with lazy-loaded orders:
Mapper.CreateMap<Customer, CustomerDto>()
.ForMember(s => s.Orders, m => m.Ignore());
Or remove Orders property from your destination entity CustomerDto. If you need to have CustomerDto instance with orders inside, then best option is to do eager loading of orders, to avoid additional queries.
I think the best way is to define your mapping objects according to your need. In the mapping object define only the needed child entities.Let's say some DTOs like this mapping in to the Person entity in the domain.
class PersonDto
{
public string Name{get;set;}
public PersonLiteDto Parent{get; set;}
}
class PersonLiteDto
{
public string Name{get;set;}
//no navigation properties here..
}
I have extended Entity Framework autogenerated classes with custom partial classes and added several new properties and business login into it. I need to populate those properties any time objects are either materialized from the database or created from scratch without a database contact.
Can I use or is it advised to use the New() method inside the class for that? (I know there is an event ObjectContext.ObjectMaterialized as well).
As an example, in my partial class i have a property
Public Property Employees As List(Of Employee)
and I want to instantiate that list somewhere (where?).
You could just write a default constructor for those entities where you set default property values. Notice Entity Framework doesn't create constructors for you, so they can easily be added in partial classes.
I have a REST-style web application that uses EF code first. I'm using AutoMapper to map my classes to DTO classes for passing across the wire, and (hopefully) back.
When I map from my POCO classes to DTOs, I'm actually starting with an EF proxy object, since the objects I'm dealing with were the result of executing some sort of query against my DataContext. This seems to work fine, however.
When I get back a DTO class as part of a POST request, I can use AutoMapper to map it onto my POCO class, and this works fine too.
However because AutoMapper is just new()-ing the POCO objects rather than using the Create method on the EntitySet, I now have a POCO class rather than the corresponding EF proxy class. This makes it harder for me to add the data to my database etc.
How can I persuade AutoMapper to use EntitySet.Create? Or is there another way to achieve the same result?
Map.CreateMap creates an IMappingExpression object which has a method ConstructUsing that accepts a function that can be used as factory method for new objects. The mapped properties are used to set values. (This can be overriden by ConvertUsing, by the way).
For details, see Automapper - how to map to constructor parameters instead of property setters, AutoMapper using the wrong constructor, or How to use Automapper to construct object without default constructor.
In your case this could be something like:
Mapper.CreateMap<TDto, TPoco>()
.ConstructUsing((Func<TDto, TPoco>) (c => context.CreateObject<TPoco>()))
May be you can do like this,
First create the required object and then use that instance to map the DTO object,
var poco=EntitySet.Create()
Mapper.Map<DTOtype, POCOtype>(dto, poco);
Suppose you are accepting POCO object in your post method instead of DTO like
[HttpPost]
public ActionResult Save(Student std)
{
//do the stuff
}
suppose that Student is EF proxy class but when its bound to the form values using Modelbinder, it creates the new objects not the ones associated with data context. So first thing is, there is no difference if you accept DTO's in post and then convert them to proxy classes or you accept proxy classes in the first place.
Second thing is that if object already exist in database and you have just created it using automapper, you can associate with datacontext using attach method. and if its new object you will need to call the Add method to save it in the database.
I am facing a problem with EF 4.1. I am trying to Add a detached object to the DbContext. Problem is it is not the emd mapped object, but derived from it. Changing the mapping is not an option as some teams are using the model with the regular mapped BL-classes, but my project created a derived model for UI stuff. Even with casting I always receive a
InvalidOperationException ("Mapping and metadata information could not be found for EntityType ...").
What I want is EF to treat this as the base class and put the object into the DbSet of the BaseClass. The current EF code is:
Context.Entry(object).State = EntityState.Added
But I am open for other suggestions, even
via IObjectContextAdapter, as long as it can save the Base and the Supertype. This should be simple, right?! Mapping to a new Base-class instance is not good idea as the main objects temporary Id would not be updated after saving...
Thanks!
As I know this is not possible. You cannot use derived class from the entity instead of the entity. You must either map derived class as well or create new instance of the entity for persistence and copy all fields from your derived class instance to the entity instance.