My questions is probably very simple, how do you load children/subclasses. There is no "load" or anything like it that I can find to have the context load the children.
the context class is of ObjectContext type, see below:
public partial class RTIPricingEntities : global::System.Data.Objects.ObjectContext
Product
Product.ModifiedByUser (how to load this class, when loading product??)
Product.Category (how to load the categories when loading product?)
You can eager load:
var q = from p in Context.Products
.Include("ModifiedByUser")
.Include("Category")
select p;
...or project:
var q = from p in Context.Products
select new
{
Id = p.Id,
Name = p.Name
ModifiedByUserName = p.ModifiedByUser.Name,
CategoryName = p.Category.Name
}
The advantage of projection is you get only the data you need, not the whole of every referenced entity. Advantage of eager loading is that returned entities do change tracking. Pick the right technique for the problem at hand.
Update
Yes, it is important to mention that you are using RIA Services. I presume you're also working within the client. This makes things completely different.
In RIA Services, it is very important to make sure that you return the entire graph of entities you require in the initial load. You don't want to call anything like .Load() on an entity, because that would be another hot to the server, which is bad for performance. If you are in, for example, a Silverlight client and request a list of instances from the server and their related properties are not already materialized, it is already too late. Also, Include will not work within a Silverlight client. Therefore, RIA Services has server-side tools you can use to ensure that you return the correct, fully materialized object graph initially.
What you need to do instead is used IncludeAttribute inside your RIA Services server. You can create a "buddy" metadata class to decorate your entity model with [Include]. There are examples in the RIA Services overview document, section 4.8.
Using the .Include() as many others have suggested is a great way to achieve what you need.
However, sometimes you might need to "re-load" something later on that you didn't "include", or that you only need sometimes, so putting an Include statement might be a waste of computing cycles in many cases.
In case of a singular relationship like "Product.Category" (where Product.Category is your navigation property from product to category), you most likely also have a "Product.CategoryReference" element. You can check that to see if it's loaded or not, and if not, you can load it "on demand":
if(!Product.CategoryReference.IsLoaded)
{
Product.CategoryReference.Load();
}
Now your referenced "Category" should be in memory and ready to use.
If you have a navigation property which references a collection of things (e.g. "Parts" for a product), you can do the same thing, directly on the navigation property:
if(!Product.Parts.IsLoaded)
{
Product.Parts.Load();
}
That can be a useful technique for "loading on demand" of single or collection type navigation properties if you haven't "included" them into your EF query.
Marc
You can use the Include() method of the System.Data.Objects.ObjectQuery. This method specifies the related objects to include in the query results and calls to Include() can be chained together to load multiple related objects.
For example to load ModifiedByUser and Category you would use a query like this:
var q = from p in context.Products.Include("ModifiedByUser").Include("Category")
select p;
If your Category entity also had a ModifiedByUser entity that you wanted to load you would use a query like this:
var q = from p in context.Products
.Include("ModifiedByUser")
.Include("Category.ModifiedByUser")
select p;
See Shaping Query Results on MSDN for further examples.
I have noticed that the above mentioned solution by Craig does not load both the ModifiedByUser and Category. It only loads the last object collection which in this case is "Category".
var q = from p in Context.Products
.Include("ModifiedByUser")
.Include("Category")
select p;
However, if you swap the order, to make it .Include("Category").Include("ModifiedByUser"), then ModifiedByUser is loaded. The wierd thing is that the IsLoaded property of both the object collection will show "true", however the Count for the first object collection will always be zero. Not sure why this is the case.
Related
Using Durandal, I have two view models within my app, let's say vmCompanies & vmEmployees. I have two views, one for each view model and within each, you can see all companies & all employees.
However, when I'm loading my employees, in the DB they have an ID for which company they are employed by. What I'd like to do is the following pseudo:
From within the vmEmployees, get a reference to the vmCompanies
If the vmCompanies has already been initialized (which I know it is 99% of the time), get a reference to it so I can use something like linq.js to find the specific company this employee works for
If the vmCompanies has not been initialized (aka: activated), do so
This way I can avoid the requirement of the vmEmployees having it's own internal cache of companies. So far I've been unable to figure out how in Durandal to query and ask "give me this view model that you've already loaded." It seems like it has that internally because when I navigate between views, they are cached and not reloaded (same with the VMs)... I've just so far been unable to see how I can do it.
You can manually require() a view model by its ____moduleId____. As long as your view model module returns an object and not a function, you'll be dealing with a singleton, so you can be sure you'll get the correct instance.
If you're not sure what the __moduleId__ is, you can use this chrome extension to view your view model's properties, including __moduleId__.
However, instead of manually instantianting VMs, a better alternative may be to create a separate module that you use to store the cached companies. I have a data module with its own internal cache that I use generically for this purpose, but you could create one specifically for companies, and store that information in it. It could even be responsible for loading its own data, if that's appropriate.
Here's some simple code to help explain:
Note this uses the sugar syntax for require JS - if you're using the array-based syntax, you'll need to translate accordingly. I can help if needed.
//companies-cache.js
define(function(require){
//some auto-loading logic here, if you wish
var companies = ko.observableArray([]);
return {
companies: companies
};
});
//vmCompanies, vmEmployees
define(function(require){
var companiesCache = require("viewModels/companies-cache");
//additional properties for this specific VM
...
return {
companies: companiesCache.companies
};
});
Using Entity Framework 5, Visual Studio 2010 with the Entity Framework Power Tools (Beta 2) extension.
Here is my database table structure:
I used the Reverse Engineer Code First function of the aforementioned extension, which generated the POCO classes and some 'mapping' files (not sure if that's the formal parlance) and a single DbContext-derived class. Other than the change I describe next, all of these generated classes are as-generated by the power tool.
In the Category.cs file, I added the following code to help flatten the object graph a bit:
private ICollection<Product> m_Products = null;
public ICollection<Product> Products
{
get
{
if (m_Products == null)
{
m_Products = new List<Product>();
foreach (var categoryProduct in CategoryProducts)
{
m_Products.Add(categoryProduct.Product);
}
}
return m_Products;
}
set { m_Products = value; }
}
I get the following exception, which I know must have something to do with the mappings, but I just can't quite figure this out.
Unhandled Exception: System.Data.EntityCommandExecutionException: An error occurred while
executing the command definition. See the inner exception for details.
---> System.Data.SqlClient.SqlException:
Invalid column name 'Category_CategoryId'.
If I need to post more information, such as the specifics of the mappings, just let me know and I'll do it. I wanted to keep this as short as possible, but I realize I've omitted some things that, for those unfamiliar with the code generated by the tool, may leave one wanting for more details.
You've added a navigation property to your model and so EF is trying to map that to your database. "Code First" means your code model defines your database schema.
Try adding the [NotMapped] attribute to your helper properties to tell EF to ignore them.
In case you've created DB scheme automatically and you are not using strategies like (DropDatabaseAlways/DropDatabaseIfModelChanges) - other words: you are really in Reverse Engineering, it seems that you have to manually add column "CategoryId" on "Category" table.
In case, you don't want to work with the property (I mean in DB), you can use Data Annotation [NotMapped] or Fluent API modelBuilder.Entity<Category>().Ignore(x=> x.CategoryId)
Finally it is possible that problem can be in mapping. I don't know whether you are using data annotations or Fluent API but EF may automatically looks for some db column (logical behavior derived from the model) and can not find it. In this case I recommend you make a revision of the mapping.
The OP already solved their problem, but I've had a similar error with a different solution. So here it is in case others need it:
I was missing a navigation property on one side of a 0..1 relationship between entities. Once I added an appropriate navigation property to the entity that was missing it, the problem was solved.
A bit more details: I had two entities with a 0..1 relationship using a FK. Entity A (parent) had a FK to Entity B (child). The child entity B had a navigation property to entity A, but A did not have a navigation property to B. After adding this, the problem was solved.
I have encountered a LINQ issue and hope that you can help me to figure it out.
Here is what is happening.
I get an IQueryable<LicenseEntity> of entities from the repository.
I look at the fields in these entities and see that they contain valid data. There is a field for a related entity Customer in the LicenseEntity. It contains valid data, too, because I loaded it with the LicenseEntity.
I use .Select to project each LicenseEntity to a LicenseViewModel.
For each LicenseEntity, a LicenseEntity is passed into AutoMapper.Mapper.Map and is loaded into a LicenceViewModel entity.
After all of the entities have been processed, when I look at the list of LicenseViewModels in the debugger, it reports a null reference exception and there are no items to view.
To determine whether AutoMapper what causing my problem, I replaced it with a MapMe(). When I stopped at the return statement in MapMe and looked at the s parameter, which is the original entity, I found that the data in it is okay except that the customer field is now null. I assume that Select has done something that I don't know about.
How I can make Select retain all of the information in the original entity when it is doing its projection? Our solution cannot materialize the list because it may be very, very large. I've included my test code below and would really appreciate your help.
// Get the IQueryable<LicenseEntity> list of licenses from the repository.
var list = LicenseRepository.List();
// Convert the IQueryable<LicenseEntity> to an IQueryable<LicenseViewModel>
var vmlist = list.Select(x => MapMe(x, new LicenseViewModel()));
//var vmlist = list.Select(x => AutoMapper.Mapper.Map(x, new LicenseViewModel()));
// This function was used to see the LicenseEntity that was passing into Map().
// I discovered that the entity has all the correct data except for a related
// entity field, which was present in the original LicenseEntity before
public LicenseViewModel MapMe(LicenseEntity s, LicenseViewModel d)
{
return d;
}
The following code works properly however it materializes the entities, which we cannot do.
List<LicenseViewModel> vms = new List<LicenseViewModel>();
foreach (var item in list)
{
var vm = AutoMapper.Mapper.Map(item, new LicenseViewModel());
vms.Add(vm);
}
You've tagged this LINQ-to-Entities but there's no mention of the underlying technology in the text. But it's very likely that the problem is caused by lazy loading of associated objects.
This is a design choice that applies to most ORMs that I've worked with. When you load an object, connected objects are not loaded by default. If they were loaded by default it's quite clear you'd quickly break everything
when you load a Licence, the related Customer is automatically loaded
when the Customer is loaded all related objects are loaded - Company, Address, all other Licences, etc
for each of those objects, every related object is loaded...
The answer is that you need to specify which related objects to load. In the Entity Framework you do this using the Include method.
Because you are using a repository abstraction you might find this more difficult than it needs to be, but without knowing more I can't give any advice. This type of functionality - pretty basic stuff - is always a difficulty with repositories and 'unit-of-work' patterns.
I think your mapping should be more like:
var vms = Mapper.Map<List<LicenseEntity>, List<LicenseViewModel>>(list);
(ie - you don't need the foreach loop).
But unfortunately I doubt very much that that'll fix your issue as I suspect that'll also materialize your entities.
I've found the solution for projecting domain entities to viewmodels. If you are struggling with the same kind of issue as I had, please review the following links:
http://lostechies.com/jimmybogard/2011/02/09/autoprojecting-linq-queries/
http://www.devtrends.co.uk/blog/stop-using-automapper-in-your-data-access-code
By the way, in one one my domain entities, I had a partial class with some "calculated" properties... Properties whose values were generated from other fields in the database record. These cannot be in the domain entity because they will interfere with the aforementioned solutions. I moved them to my ViewModel class, which is where they were really required, and all is well.
Hope that this info helps...
Mike
I've asked the question a few different times in a few different ways and I haven't yet gotten any responses. I'm trying again because I feel like my solution is too convoluted and I must be missing something simpler to do.
Using EF 4.1, POCO, DbContext API, AutoMapper, and Razor in an MVC 3 application.
I have a many-to-many relationship between two of my entities: Proposals and CategoryTags. I can successfully map (Automapper) a Proposal to my ProposalViewModel including the collection of CategoryTags.
In my View, I use javascript to allow the user to add, update, and remove tags by dynamically creating elements, each one that stores the ID of the chosen tag.
I can successfully post my ViewModel back to my controller with it's CategoryTags collection populated (although only with the ID property for each CategoryTag).
When that ViewModel is posted back to my controller, I don't know how to get those tags from the ViewModel and add them to my Model in such a way that db.SaveChanges() updates the database properly.
The only way I've had any success is to disconnect the CategoryTags collection in mapping (by namig them differently), iterate through each tag and manually look it up in my context and then call the .add() method. This is sloppy for a number of reasons which leads me to believe I'm doing it wrong.
Can anyone offer any direction at all?
UPDATE:
For anyone who is interested, my functional code:
Dim p As New Proposal
Dim tempTag As CategoryTag
p = AutoMapper.Mapper.Map(Of ProposalViewModel, Proposal)(pvm)
db.Proposals.Attach(p)
db.Entry(p).Collection("CategoryTags").Load()
For Each ct In pvm.Tags
tempTag = db.CategoryTags.Find(ct.Id)
If tempTag Is Nothing Then
Continue For
End If
If ct.Tag = "removeMe" Then
p.CategoryTags.Remove(tempTag)
Continue For
End If
p.CategoryTags.Add(tempTag)
Next
db.Entry(p).State = EntityState.Modified
db.SaveChanges()
Return RedirectToAction("Index")
The only working way is doing this manually - you can read full description of the problem if you want. The description is related to ObjectContext API but DbContext API is just wrapper suffering same issues (actually DbContext API suffers even more issues in this scenario and because of that I will skip solution with manually setting relationships).
In short. Once you post your data back to the controller you must create new context instance and attach your Proposal and realated CategoryTags. But after that you must inform the context about changes you did. It means you must say context which tags have been added to proposal and which have been removed. Otherwise context cannot process your changes because it doesn't do any automatic merge with data in database.
The easiest way to solve this is loading current Proposal with related CategoryTags from database (= you will have attached instances) and merge incoming data into attached object graph. It means you will manually remove and add tags based on posted values.
I have asked this question on more than one forums and it seems no one would even want to take a crak at it.
My problem is simple and i would guess everyone has run into it when using LINQ to SQL.
If your have a LINQ object called: Person and you would like to poulate a list box based on all of the people you have in your DB the task is simple:
BindingListCollectionView view;
view = (BindingListCollectionView)CollectionViewSource.GetDefault (dataContext.Persons);
Now say you wish to have a text box over the list to filter the results. that
would not work since IBindingList interface implemented by the LINQ to SQL objects returns false on "CanFilter" property.
What most people do is create an ObservebleCollection, the folloing is an example
im sure most of you use.
ObservebleCollection<Person> col = new ObservebleCollection<Person>(dataContext.Persons.ToList());
ListCollectionView view = (ListCollectionView)CollectionViewSource.GetDefault(col);
Since this will return a ListCollectionView and not a BindingListCollectionView
it will be filterbale and all is well with the world.
Here comes the problem, say you have Multi levels of Forign key relations:
Person<---Alias<---Tickets
and now you wish to have 3 list boxes binded when a person is selected the second list box will diplay only his Alias's and when an Alias is selected only it's Ticket's are shown, this is very simple with binding and syncronizing. the problem is if i want to add a textbox filter on tob of all of the listboxes ( say a person has over 1000 Aliases and i want to be able to filter them to choose 1 ).
The prvious solution of ObservebleCollection will not work since all the Person objects returned will have EntitySet objects for the forgin relation, and this will again return a none filterbale BindingListCollectionView and not a ListCollectionView.
The only way i found around this is to manually bulid an ObserverbleCollection based on the retunred Query this is tedious work and causes me to tie the BusnessObjects layer and the application Layer. also its very slow since you need to make many trips to the database...
Does anyone have a solution to this?
Thanks,
Eric.
I think the Model View View-Model pattern (MVVM) will help you out here.
Create a view for your first listbox and ensure it exposes its collection as something that implements INotifyCollectionChanged. Same with your second and third listboxes.
You can also make any of them include properties for binding to your textbox for filtering. When the value changes, you simple adjust the in-memory collection that the list is bound to.
Have a google for MVVM since it works quite well. Most examples will relate to WPF but are still applicable for what you are doing.
Also check out a product on codeplex called 'Bindable Linq' which allows you to do things like:
var q = from p in Persons.AsBindable() select p;