Loading related objects without Include() - entity-framework

Briefly, I'm loading objects that descend from a base class using a repository defined against the base class. Although my objects are created with the correct descendant classes, any descendant classes that add navigation properties not present in the base class do not have those related objects loaded, and I have no way to explicitly request them.
Here is a simple method in a repository class that loads a given calendar event assuming you know its ID value:
public CalendarEvent GetEvent(int eventId)
{
using (var context = new CalendarEventDbContext(ConnectionString))
{
var result = (from evt in context.CalendarEvents
where eventId.Equals((int)evt.EventId)
select evt).ToList();
return result.ToList()[0];
}
}
CalendarEvent is a base class from which a large number of more specific classes descend. Entity Framework correctly determines the actual class of the calendar event specified by eventId and constructs and returns that derived class. This works perfectly.
Now, however, I have a descendant of CalendarEvent called ReportIssued. This object has a reference to another object called ReportRequest (another descendant of CalendarEvent, although I don't think that's important).
My problem is that when Entity Framework creates an instance of ReportIssued on my behalf I always want it to create and load the related instance of ReportRequested, but because I am creating the event in the context of generic calendar events, although I correctly get back a ReportIssued event, I cannot specify the .Include() to get the related object. I want to do it through this generically-expressed search because I won't necessarily know the type of eventId's event and also I have several other "Get" methods that return collections of CalendarEvent descendants.
I create my mappings using the Fluent API. I guess what I'm looking for is some way to express, in the mapping, that the related object is always wanted or, failing that, some kind of decorator that expresses the same concept.
I find it odd that when saving objects Entity Framework always walks the entire graph whereas it does not do the equivalent when loading objects.

Related

Doctrine - mapping an entity from an external library

i'm using an external library (the awesome nicmart/Tree to build trees) that returns me an object that is the extension of the original object produced by the library
class originalObject
{
//some properties
// this is the object produced by the library
// i dont want to modify the external library so no mapping here
}
class myObject extends originalObject
{
//this is the entity i want to persist
// but it hasnt got any property ??
}
i want to persist myObject with Doctrine\MongoDB, therefore i need to map it.
i (obviously) dont want to modify the library itself so my question is:
where do i put the mapping ?
I thought i could override the properties (like i would do with methods) by re-declaring them and adding the mapping to the re-declaration, but they are not overriden but duplicated.
IMHO you can't do it this way. You'll have to modify originalObject (add annotations there) or you'll have to declare whole myObject and can't inherit origObj.

Approach for binding on a MVVM application using Portable Class Libraries

I'm trying to port the core of an application across to Portable Class Libraries and don't appear to have binding support.
I'm trying to bind a property on my ViewModel to my Model, which consists of an ObservableDictionary (INotifyPropertyChanged, INotifyCollectionChanged, IDictionary<string, string>). I do this usually (with WP7) by using the following code when initialising the view model:
SetBinding(MyProperty, new Binding(string.Format("MyDictionary[{0}]", "thekey")) { Source = MyModel });
How would I approach this when using Portable Class Libraries, where it seems like the Binding class is unavailable?
I've implemented this by having the base class for the ViewModels wire up to the PropertyChanged event of the ViewModel and the NotifyCollectionChanged event of the ObservableDictionary. I then have a method (with a set of overloads for additionally supplying an implementation of an IPclValueConverter which is a copy of the IValueConverter) which adds to a collection of PclBinding objects which is a set of PropertyInfo, dictionary key, IPclValueConverter and a converter parameter.
Within the PropertyChanged/NotifyCollectionChanged I check to see if the binding should be updated, and if so perform the update passing the value through a converter if present.
This means that from my original example, I now write the following inside my ViewModel which creates the binding as required:
SetBinding(() => MyProperty, "theKey");
If anyone is actually interested in this code I'd be happy to post it up. :)

How to include all child entities when querying a context with a Where expression using Entity Framework 4.1?

I created a generic repository that handles querying my entities.
When I call this:
public IQueryable<TEntity> GetQuery()
{
return _context.Set<TEntity>().AsQueryable();
}
I get back the entire entity including all child entities.
When I call this:
public TEntity GetById(Guid id)
{
return GetQuery().Where(e => e.Id == id).FirstOrDefault();
}
I have to specify what child entities to include.
Is there a way to get back ALL child entities without having to write includes for each entity?
Lazy loading is enabled by default. This means that the collections will be loaded when you access them, not when you retrieve the parent object e.g.
foreach (var parent in repo.GetQuery()) {
foreach (var child in parent.Children) {
// do something
}
}
If you wish to eagerly load your entities you could subclass your generic repository and override the methods where you wish to use the Include lambda. Alternatively there is an Include method that accepts a string list of associations to include, which you could expose on your generic repository.
Update:
Not quite sure why you gave my answer -1 but as further clarification.
You stated regarding your GetQuery() method:
I get back the entire entity including
all child entities.
The child entities are lazily loaded, whether you access the collections in debug or output them on your page.
The single query should work, with lazy loading enabled.
And AFAIK, with lazy loading disabled, this doesn't mean that all the collections are loaded automatically, quite the opposite, you have to explicitly load them by calling Include.

Entity Framework Generic Repository Context

I am building an ASP.NET 4.0 MVC 2 app with a generic repository based on this blog post.
I'm not sure how to deal with the lifetime of ObjectContext -- here is a typical method from my repository class:
public T GetSingle<T>(Func<T, bool> predicate) where T : class
{
using (MyDbEntities dbEntities = new MyDbEntities())
{
return dbEntities.CreateObjectSet<T>().Single(predicate);
}
}
MyDbEntities is the ObjectContext generated by Entity Framework 4.
Is it ok to call .CreateObjectSet() and create/dispose MyDbEntities per every HTTP request? If not, how can I preserve this object?
If another method returns an IEnumerable<MyObject> using similar code, will this cause undefined behavior if I try to perform CRUD operations outside the scope of that method?
Yes, it is ok to create a new object context on each request (and in turn a call to CreateObjectSet). In fact, it's preferred. And like any object that implements IDisposable, you should be a good citizen and dispose it (which you're code above is doing). Some people use IoC to control the lifetime of their object context scoped to the http request but either way, it's short lived.
For the second part of your question, I think you're asking if another method performs a CRUD operation with a different instance of the data context (let me know if I'm misinterpreting). If that's the case, you'll need to attach it to the new data context that will perform the actual database update. This is a fine thing to do. Also, acceptable would be the use the Unit of Work pattern as well.

RIAServices unsupported types on hand-built DomainService

My EF model was generated from my SQL Server database. I then generated a DomainService for RIAServices against the EF model. One of the entities is called "EntryCategories". The DomainService created this method:
public IQueryable<EntryCategories> GetEntryCategoriesSet()
{
return this.Context.EntryCategoriesSet;
}
Since my user interface display model looks quite different from the physical model, I decided to write my own DomainService for that and related entities. Yes, I know we are meant to modify the generated one but it has so much stuff in there and I wanted to focus on a small thing.
I removed the EnableClientAccess attribute from the generated DomainService and added a new class called ClientDomainService, and encapsulated in it the generated DomainService:
[EnableClientAccess()]
public class ClientDomainService : DomainService
{
// the generated domain service encapsulated in my new one.
private DataDomainService _dcds = new DataDomainService();
// reimplement one of the DataDomainService methods
public IQueryable<EntryCategories> GetEntryCategories()
{
return (from t in _dcds.GetEntryCategoriesSet() where t.EntryCategoriesVersions.EntryCategoriesVersionId == datahead.EntryCategoriesVersions.EntryCategoriesVersionId orderby t.DisplayOrder select t);
}
}
The very fist thing I tried is to reimplement the GetCateogoriesSet method but with the underlying data filtered based on another entity in my class (not shown). But when I build this, an error shows up:
Entity 'DataProject.Web.EntryCategories' has a property 'EntryCategoriesVersionsReference' with an unsupported type
If I comment out my CientDomainService, replace the EnableClientAccess attribute on the generated DomainService, and place the analagous linq filtering in the original GetEntryCategoriesSet method, the project compiles with no errors.
What is so special about the generated DomainService that my new one doesn't have? Is it that metadata.cs file?
What's special about the generated domain service is not the .metadata.cs file (you can keep it, and use it, but it doesn't solve your problem).
The problem appears somehow because RIA services (?) needs a 'domain service description provider' for the exposed Linq to EF entities. The LinqToEntitiesDomainService class has the LinqToEntitiesDomainServiceDescriptionProviderAttribute, already applied, so the generated domain services which inherit from it also inherit the provider.
When you build your own custom domain service, derived from DomainService, and expose entities through it, you need to apply this attribute yourself. Furthermore, since the provider cannot infer the object context type from the domain service base class (which it can and does if the base class is LinqToEntitiesDomainService), you need to specify the object context type in the attribute constructor, like this:
[EnableClientAccess()]
[LinqToEntitiesDomainServiceDescriptionProvider(
typeof(YourObjectContextType))]
public class ClientDomainService : DomainService
{
...
}
That should fix it.
Note that this means if you had hoped to abstract your object context away from your domain service, you'll be disappointed. I had opted for the seemingly popular repository model where all code that operates on the object context goes into a provider used by the domain service. This facilitates unit testing, but evidently doesn't remove the domain service's dependency on the object context. The context is required for RIA Services to make sense of your entites, or at least those referenced by the domain entity (such as EntryCategoriesVersions in your case).
If you want to expose a specific entity on a domain service you will have to provde at least one query method for it. This is also required when the entity is only accessed as a child of another entity.
In this case you need to add the EntryCategoriesVersions entityset to the domain service, to get the scenario working correctly.
What type is EntryCategoriesVersionsReference ? Try adding a [DataContract] annotation against the type, and appropriate [Key] and [DataMember]. It should help with marshalling.
For me, the fix for this error was to add a default constructor to the return type.
In OP's example, the property 'EntryCategories.EntryCategoriesVersionsReference' needs to be of a type with a default constructor.