I've this -simplified- model class:
public class Transaction
{
public string Id { get; set; }
public virtual UserResource Resource { get; set; }
}
And an Mvc Web Api Controller:
// POST api/values
[HttpPost]
public void Post([FromBody]Transaction transaction)
{
}
And my request body is like this:
{"Id":"5520AEB9-DAD8-4C85-80A7-3257931B9790","ResourceId":"118547FC-0B3A-4816-820C-93BF2BA1BF14"}
In the post method, transaction.Resource is null. And I don't know how to get it to be populated using the Resource Id I passed...
What I tried so far:
Add public string ResourceId {get; set;} and mark it as a ForeignKey in Transaction... -> no change
In the request change ResourceId param to "Resource":{Id:".."} -> transaction.Resource is not null any more but it hasn't queried Resource table to fill in anything...
Mark Resource as virtual or not (Lazy loading or Eager) -> No change
Ideas?
You are not passing the Resource object to your API, you must either pass it in or have the API figure out what it is, I don't suggest the latter.
So as it's Entity Framework 7, you can't use lazy loading at this point in time as it's not (yet) in the framework.
The current roadmap lists it as a high priority and they state:
These features are high priority but we think EF7 would be a compelling release for the vast majority of applications without them.
So instead, in your query you need to manually include all of your navigation properties, for example:
var data = context.Transactions
.Include(t => t.Resource)
.Where(t => t.Id == 1);
Related
I wrote a query which is pretty simple:
var locations = await _context.Locations
.Include(x => x.LocationsOfTheUsers)
.Include(x => x.Address)
.ThenInclude(x => x.County)
.Where(CalculateFilters(searchObj))
.ToListAsync(cancellationToken);
And everytime LocationsOfTheUsers were null so I decided to .Include(x => x.LocationsOfTheUsers) and I received results as expected but I'm not sure why do I have to include this collections since it's defined like this:
public class Location
{
public string Title { get; set; }
public long? RegionId { get; set; }
public Region Region { get; set; }
public long? AddressId { get; set; }
public Address Address { get; set; }
public long? CountyId { get; set; }
public County County { get; set; }
public ICollection<LocationsOfTheUsers> LocationsOfTheUsers { get; set; }
}
I thought this will be automatically included since it exist as ICollection in Location class.
So why is .Include() on LocationsOfTheUsers needed here?
Thanks guys
Cheers
In entity framework the non-virtual properties represent the columns of the tables, the virtual properties represent the relations between the tables (one-to-many, many-to-many, ...)
So your property should have been defined as:
public virtual ICollection<LocationsOfTheUsers> LocationsOfTheUsers { get; set; }
One of the slower parts of a database query is the transfer of the selected data from the database management system to your local process. Hence it is wise to limit the selected data to the values you actually plan to use.
If you have a one-to-many relation between Schools and Students, and you ask for School [10] you don't want automatically to fetch its 2000 Students.
Even if you would like to have "School [10] with all its Students" it would not be efficient to use Include to also fetch the Students. Every Student will have a foreign key SchoolId with a Value of [10]. If you would use Include you would transfer this foreign key 2000 times. What a waste!
When using entity framework always use Select to fetch data and select only the properties that you actually plan to use. Only use Include if you plan to change the included items.
This way you can separate your database table structure from the actual query. If your database structure changes, only the query changes, users of your query don't notice the internal changes.
Apart from better performance and more robustness against changes, readers of your code can more easily see what values are in their query.
Certainly don't use Include to save you some typing. Having to debug one error after future changes will take way more time than you will ever save by typeing include instead of Select
Finally: limit your data early in your process, so put the Where in front.
So your query should be:
var predicate = CalculateFilters(searchObj)
var queryLocations = dbContext.Locations
.Where(predicate)
.Select(location => new
{
// Select only the location properties that you plan to use
Id = location.Id,
Name = location.Name,
// Locations Of the users:
UserLocations = location.LocationsOfTheUsers
.Select(userLocation => new
{
// again: only the properties that you plan to use
Id = userLocation.Id,
...
// Not needed, you already know the value
// LocationId = userLocation.LocationId
})
.ToList(),
Address = new
{
Street = location.Address.Street,
PostCode = location.Addrress.PostCode,
...
County = location.Address.County.Name // if you only want one property
// or if you want more properties:
County = new
{
Name = location.Address.County.Name,
Abbr = location.Address.Count.Abbr,
...
}),
},
});
I thought this will be automatically included since it exist as ICollection in Location class.
Well, it's not automatically included, probably for performance reasons as the graph of related entities and their recursive child entities may be rather deep.
That's why you use eager loading to explicitly include the related entities that you want using the Include method.
The other option is to use lazy loading which means that the related entities are loaded as soon as you access the navigation property in your code, assuming some prerequisites are fulfilled and that the context is still around when this happens.
Please refer to the docs for more information.
I believe you are using EntityFrameworkCore. In EntityFramework (EF6), lazy loading is enabled by default, However, in EntityFrameworkCore, lazy loading related entities is handled by a separate package Microsoft.EntityFrameworkCore.Proxies.
To enable the behaviour you are seeking, install the above package and add the following code
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseLazyLoadingProxies();
}
After this, the related entities will be loaded without the Include call.
I have an issue using EF7 in a web application with which I could use some help. I'm currently using EF7 RC1.
Here are some models that illustrate my problem.
Contact
public class Contact
{
public Guid Id { get; set; }
public string Desc { get; set; }
public ContactType ContactType { get; set; }
}
ContactType
public class ContactType
{
public Guid Id { get; set; }
public string Desc { get; set; }
public ICollection<Contact> Contacts { get; set; }
}
These models are related via Fluent API like this:
modelBuilder.Entity<Contact>(entity => {
// abridged for clarity
entity
.HasOne(c => c.ContactType)
.WithMany(ct => ct.Contacts)
.IsRequired();
});
My needs are to be able to retrieve a collection of Contact entities from the database with their ContactType property loaded. EF makes this quite easy:
using(var context = new MyDbContext()) {
var contacts = await context
.Contacts
.Include(c => c.ContactTypes)
.Where(/* some search criteria */)
.ToListAsync();
}
The issue is that in loading the ContactType properties of the Contact entities (which happens due to the call to .Include() in the query), EF also helpfully loads the Contacts property of each ContactType entity, resulting in an infinite chain of Contacts pointing at ContactTypes and ContactTypes pointing at Contacts. I understand why this is the default behavior and that it's helpful in many cases, but my needs are to serialize these entities to JSON and send them down to the client - it's a read-only situation.
My desired behavior is for EF to return a collection of Contacts with loaded (non-null) ContactType properties that have their Contacts property set to null. Is this something EF can do? Is there any way to end up with the object graph I want short of manually nulling out properties I don't want populated?
Things I've tried:
Appending .AsNoTracking() to the EF query (which doesn't seem to stop
the Contacts property of the ContactType entity from being loaded)
Telling Json.NET not to serialize infinite reference loops (which is
required to avoid infinite recursion during serialization, but still
results in a lot of extra data being serialized)
You can't avoid EF to load ContactType.Contacts collection, as it's not actually loading it but filling the collection with the loaded Contact instances.
This is why using AsNoTracking has no efect, because is not a problem of lazy loading nor ChangeTracker.
You have three possible solutions:
Use Json.NET ReferenceLoopHandling = ReferenceLoopHandling.Ignore, but as you stated it will generate lot of unnecesary data, as you will get the collection of Contacts for every ContactType
Use [JsonIgnore] attribute on ContactType.Contacts so it will be ignored by the serializer. But it will ignore it always, I don't know if you need it in other situations
Define a DTO, use something like Automapper to map your data in it (without Contacts collection) and serialize it
I would prefer the 3rd option as I don't like sending domain model objects to the client, and it avoid adding attributes to domain model not related with domain.
I have same question Entity Framework 7 Core disable auto loading
I add AsNoTracking()
IQueryable<ScheduleModel> q = _db.Schedules;
q = q.AsNoTracking();
q = q.Include(x => x.ElementItem);
q = q.Include(x => x.ScheduleHours);
Properties not populate automatic now.
I have a model that has some related data in navigation properties, like so:
public class Document
{
[Key]
public int DocumentId { get; set; }
public string DocumentName { get; set; }
public virtual ICollection<DocumentBeneficiary> DocumentBeneficiaries { get; set; }
public virtual ICollection<DocumentExecutor> DocumentExecutors { get; set; }
public virtual ICollection<DocumentSuccessor> DocumentSuccessors { get; set; }
}
I understand how to do eager loading of this related data from a controller method, like so:
var doc = context.Documents.Include(x => x.DocumentBeneficiaries)
However, what I want to do is write a member method inside the model that takes the data related to an entity, does some processing, and outputs a string. Something like this:
public class Document
{
...
public string ProcessStuff() {
//use data in navigation properties here like so:
foreach (var d in DocumentBeneficiaries) { ... }
}
}
Is this allowable? I can't seem to find anything about it on google. Will it load the related data lazy vs. eager depending on how I load the entity in the controller prior to calling the method in the model?
I realize that some schools of thought hold that models should have no methods, but others say it's ok to put business logic in the model. If I have to I suppose I can make this a controller method, but this way makes more sense to my design if possible. Sorry if this is a somewhat speculative question, but I can't seem to find any info on this pattern.
Yes it will load the DocumentBeneficiaries when you invoke ProcessStuff method as long as Lazyloading is enabled, thou it may not be a good design (my opinion) to add business logic directly into the model, but as you stated, there are some who like it and some who don't.
If you don't load the child collection ahead of time using Include and Lazyloading is enabled, then you will end up making extra database trips while executing ProcessStuff(). Using Include pre loads the data you need with less number of database round trips. It is always better to make less database trips whenever possible.
If Lazyloading is disabled, you have to use Include before invoking ProcessStuff()
I'm trying to develop a messeging system to my mvc application using mvc 5. I have tables called Event, EventUser, EventObject. Each of those tables have following;
Event
ID
CreatedBy
StartTime
IsShared
Budget
EventUser
EventID
UserID
IsAccepted
EventObject
EventID
ObjectID
in my messageController i have the index method which receive the parameter of the user id.i need to display every event that user has invited using this method..
namespace MvcApp.Controllers
{
public class MessageController : Controller
{
private EPlannerDatabaseEntities db = new EPlannerDatabaseEntities();
// GET: /Message/
public ActionResult Index(int UId)
{
/* linq expressions */
return View();
}
}
}
when the parameter has passed in, i want to;
*Select from EventUser table where UID=UserID and join the result with Event and EventObject tables by using EventID attribute.
*Finally by using the final result i need to display every event's infomation that user has invited; like CreatedBy , StartTime, Budget,other users,objects etc..
i'm new to mvc and viewmodel concept.I heard that viewmodel concept can help with these situations.can i overcome this problem by using viewmodel concept.if yes what are the things i need to add in view model?? otherwise what are the other ways to do this?
one way i can see of doing this is creating a custom return object and using EF to join all the tables together. Example
public class MyObject{
public DateTime DateCreated{get;set}
// add remaining properties here
// properties to get back
}
then in code you would use Entity Framework to create a joined data set into a nice list of objects. Example:
var results = (from b in bla join bla2 in (Some Second Query Here)
from SomeSecondQueryHere
where cond1 and cond2 Select new MyObject{
// add properties in here})
where you would replace the bla and bla2,etc with respective table names needed. Then all you need to do is
return View(results);
And the changes will be accessible in the View
If you question is regarding querying with an ORM like Entity Framework, you need to post your entities, not your table schemas. The whole purpose of an ORM is to abstract away the underlying database structure, so while the schema will often be similar to the entity class, it can also be quite different. As a result, I'll have to make assumptions about your entity classes.
To query everything, you just need something like the following:
var events = db.Events.Where(m =>
m.EventUsers.Any(u => u.UserID == UId && u.IsAccepted)
).Include(m => m.EventObjects);
That assumes entity classes along the lines of:
public class Event
{
...
public virtual ICollection<EventObject> EventObjects { get; set; }
public virtual ICollection<EventUser> EventUsers { get; set; }
}
public class EventUser
{
...
public int UserID { get; set; }
public bool IsAccepted { get; set; }
}
You end up with an enumerable of Event. If you need to access the EventObjects for an individual event, you have to use the appropriate collection property. For example:
foreach (var item in events)
{
foreach (var obj in item.EventObjects)
{
// do something with `obj` (an invidual `EventObject` instance)
}
}
If you need the actual User object, you're better object querying that first and including related Events and EventObjects:
var user = db.Users.Include("EventUsers.Event.EventObjects").SingleOrDefault(m => m.UserID == UId);
That assumes entities like:
public class User
{
...
public virtual ICollection<EventUser> EventUsers { get; set; }
}
public class EventUser
{
...
public virtual Event Event { get; set; }
}
public class Event
{
...
public virtual ICollection<EventObject> EventObjects { get; set; }
}
With that method, however, there's no way to filter the included Events by whether they're accepted or not. There's a potential way around that, but it requires disabling lazy-loading of EventUsers entirely and complicates querying the information you need. If you need to go that route, see: https://msdn.microsoft.com/en-us/data/jj574232.aspx#explicitFilter.
Otherwise, you can just exclude non-accepted events before iterating over the collection:
var events = user.EventUsers.Where(m => m.IsAccepted).Select(m => m.Event);
Really you don't need a view model, per se, for any of this. As you can either pass the lists of events (which will include any related EventObjects) or the the single user instance (which includes related events and related EventObjects) directly to your view.
A very high level description of how to solve your scenario using Entity Framework would be something like this:
First you've got to create a series of entity data objects that will represent your tables in the EF data model using EF Code first techniques.
Then you create DbContext objects with DbSets for your previously created entities.
Then you create at least one Service class that will have a property representing DbContext and a set of methods encapsulating Linq queries to your entities.
In the MVC controller you call an instance of Service that you previously create and assign it to a property ant Controller's construction time. Finally, in the Action method you should call the correct Service method and pass any result to the view.
( I am assuming this is a small Ad-Hoc system with a handful of tables , an elaborate System with production quality would require using IoC techniques).
Right now I have proxy creation disabled:
context.Configuration.ProxyCreationEnabled = false;
I have a data model like so (removed non-relevant fields):
public partial class Video
{
public int VideoID { get; set; }
public string Title { get; set; }
public int UserID { get; set; }
public virtual User User { get; set; }
}
public partial class User
{
public User()
{
this.Videos = new HashSet<Video>();
}
public int UserID { get; set; }
public string Username { get; set; }
public virtual ICollection<Video> Videos { get; set; }
}
I am using Unit of Work and Repository patterns to load my data like so,
Get all video's, including the user object:
var videos = videoService
.Include(v => v.User)
.Get()
I am using automapper to map from data model to domain model (hence the UI namespace in the screenshot below). When I inspect the video enumeration I get back, and look at the first item in the enumeration, I go to check the user object:
What I expect here is the VideoModel to be filled with data(ok), with only it's single UserModel entity to be filled with data(ok), and all collections in the UserModel to be empty(this is broke). As you can see in the second red box above, the Videos collection is populated with 6 videos. And on those video's, the user's are filled in. So this basically creates a very large object graph.
1) Can I make it so when using an include that it ONLY goes 1 level deep (IE doesn't fill in Video.User.Videos)?
2) Why doesn't ProxyCreationEnabled = false take care of this? Am I expecting too much?
p.s. I want to avoid creating a customer mapper for this with automapper.
p.p.s. I am doing db first, not model first
By default, EntityFramework uses lazy loading for virtual properties (such as User and Videos in your example). If you want these properties to be filled prior to them actually being accessed, you can use Include() or, to go another level deep, an Include() with a nested Select().
This default behavior, however, relies on the creation of a proxy class, which you have apparently turned off.
Not knowing all the things you're trying to do, this may not work, but it seems like you would get the behavior you wanted by simply removing ProxyCreationEnabled = false and using Include() as you have.
Also, viewing properties in the debugger may be misleading because you are in fact accessing the property when you try to view it in the debugger (which could cause the lazy loaded entity or collection to be filled right then, making you think it had been eagerly loaded).