How to correctly use Data Annotations to select which Items should be returned by the Web API? - data-annotations

I'm trying to specify a subset of data to be returned from a database query by Web API 2.
In particular, for this query, I first turn lazy loading on:
db.Configuration.LazyLoadingEnabled = true;
This is because there are potentially infinite levels of children. For example:
Parent: {"name":"Jon","children":[{"name":"Dave","children":["name":"Ed"...
Each person in the above sequence can also have a biography. In the database, the books also have related tables for, let's say, authors, reviewers etc.
As far as I know I can add data annotations to the model to specify which fields to return:
[Key]
= specifies the key which will be returned
[DataMember]
= specifies a property which will be returned
[JsonIgnore]
[IgnoreDataMember]
= specifies a property which will not be returned
[JsonObject(IsReference = true)]
= specifies that the object is being references from another object and therefore related objects should not be loaded
I'm struggling to load the related biographies. The id for the biography is returning, but the biography objects are null. From the parent object, I have annotated both the nullable int and the virtual object references to biography with [DataMember]. In the biography object, I have then specified the id with [Key] and the name with [DataMember] and all other properties with [JsonIgnore]
[IgnoreDataMember]. However the biographies are not being loaded. The db query is returning the items loaded, but they are then being nulled by web api, I assume because of some circular reference in the chain somewhere.
There are about 50 tables linked in some way, do I need to go through them all and add data annotations to everyone - even if I have used an ignore annotation to break the chain? Hoping for a simple solution, but any solution appreciated!
It seems to be working fine that [DataMember] will load a HashSet of related data, which gets instantiated in the constructor, but related databases objects (which are not instantiated in the constructor) do not get loaded.

It seems that the update statement doesn't turn lazy loading on:
db.Configuration.LazyLoadingEnabled = true;
The related items would only be returned if I went into debug mode and loaded the related data when hovering over the object (seems strange), but basically it was staying in lazy loading = false mode.
My solution has been to turn lazy loading on globally and to use data annotations as described above to avoid circular references.

Related

use ToListAsync() with navigation property

I am using ASP.NET MVC with Entity Framework.
I have an entity called "AllUserData".
I have a second entity called "Genres". Each row in the table is a genre.
These two entities have a one-to-many relationship. So in the class definition of the "AllUserData" class, I have
public virtual ICollection PreferredGenres { get; set; }
I am able to successfully read the genres preferred by each user using
AllUserData aud = db.AllUserData.Single(b => b.UserId == currentUserId);
var chosengenres = aud.PreferredGenres.ToList()
However I cannot use
AllUserData aud = db.AllUserData.Single(b => b.UserId == currentUserId);
var chosengenres = await aud.PreferredGenres.ToListAsync()
Visual Studio says "ICollection does not contain a definition for 'ToListAsync' and the best extension method overload 'QueryableExtensions.ToListAsync(IQueryable)' requires a receiver of type 'IQueryable'.
Why is this happening? The only difference between the two was that in one case I used ToList() and in the other I used ToListAsync().
Can async methods not be used with navigation properties? In a real life application there are many cases where there are relationships between various entities; can asynchronous methods not be used when accessing properties using these relationships? Is there some way around this? I'd rather do things asynchronously if possible.
Your navigation property would have been declared as an ICollection, standard for Entity properties -- the compiler won't be pleased.
However, you can get an IQueryable with AsQueryable or with another Select() (if that makes sense for your needs) between the navigation collection and the ToListAsync().
ToList() can operate on IEnumerable (which includes ICollection and IQueryable). ToListAsync() has only been made to work with IQueryable, hence the message from VS. The "why" may lie in the implementation details.
Ok, it seems entity framework doesn't allow for calling the navigation property asynchronously in this manner.
What worked for me is earger loading the navigation property in the beginning. (Thanks to user daf for sending me in the correct direction).
So instead of
AllUserData aud = await db.AllUserData.SingleAsync(b => b.UserId == currentUserId);
var usergenres = await aud.PreferredGenres.ToListAsync();
which does not work, I can instead do this :
AllUserData aud = await db.AllUserData.Include(p=>p.PreferredGenres).SingleAsync(b => b.UserId == currentUserId);
var usergenres = aud.PreferredGenres.ToList();
This way the first query asks to pull all necessary information from the database through eager loading with .Include(), and does so asynchronously due to the user of .SingleAsync(). The next statement uses .ToList(), but shouldn't make a second trip to the database since the data is already eager loaded, so it doesn't matter that it is async. The whole operation is now async.
I also realized this same question has been asked previously on StackOverflow; it is a little difficult to find depending on what search terms you used: Entity Framework Designer First get navigation property as Tasks. The selected answer provides 4 different solutions, out of which the first one is similar to what I am using now.
It also appears possible to designate navigation properties as async when defining the entity class- see the section titled "Async Lazy Loading" on this page: here The sample snippet demonstrates how to do this for a single navigation property; I don't know if it can be done for an ICollection navigation property as well, which is what I need, but I didn't do any further digging either.

Setting a collection of related entities in the correct way in EF4 using POCO's (src is the DB)

I have a POCO entity Report with a collection of a related POCO entity Reference. When creating a Report I get an ICollection<int> of ids. I use this collection to query the reference repository to get an ICollection<Reference> like so:
from r in referencesRepository.References
where viewModel.ReferenceIds.Contains(r.Id)
select r
I would like to connect the collection straight to Report like so:
report.References = from r in referencesRepository.References
where viewModel.ReferenceIds.Contains(r.Id)
select r;
This doesn't work because References is an ICollection and the result is an IEnumerable. I can do ToList(), but I think I will then load all of the references into memory. There also is no AddRange() function.
I would like to be able to do this without loading them into memory.
My question is very similar to this one. There, the only solution was to loop through the items and add them one by one. Except in this question the list of references does not come from the database (which seemed to matter). In my case, the collection does come from the database. So I hope that it is somehow possible.
Thanks in advance.
When working with entity framework you must load objects into memory if you want to work with them so basically you can do something like this:
report.References = (from r in referencesRepository.References
where viewModel.ReferenceIds.Contains(r.Id)
select r).ToList();
Other approach is using dummy objects but it can cause other problems. Dummy object is new instance of Reference object which have only Id set to PK of existing object in DB - it will act like that existing object. The problem is that when you add Report object to context you must manually set each instance of Reference in ObjectStateManager to Unchanged state otherwise it will insert it to DB.
report.References = viewModel.ReferenceIds.Select(i => new Reference { Id = i }).ToList();
// later in Report repository
context.Reports.AddObject(report);
foreach (var reference in report.References)
{
context.ObjectStateManager.ChangeObjectState(reference, EntityState.Unchanged);
}

System.InvalidOperationException when trying to iteratively add objects using EF 4

This question is very similiar to this one. However, the resolution to that question:
Does not seem to apply, or
Are somewhat suspect, and don't seem like a good approach to resolving the problem.
Basically, I'm iterating over a generic list of objects, and inserting them. Using MVC 2, EF 4 with the default code generation.
foreach(Requirement r in requirements)
{
var car = new CustomerAgreementRequirement();
car.CustomerAgreementId = viewModel.Agreement.CustomerAgreementId;
car.RequirementId = r.RequirementId;
_carRepo.Add(car); //Save new record
}
And the Repository.Add() method:
public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
private TxRPEntities txDB;
private ObjectSet<TEntity> _objectSet;
public void Add(TEntity entity)
{
SetUpdateParams(entity);
_objectSet.AddObject(entity);
txDB.SaveChanges();
}
I should note that I've been successfully using the Add() method throughout my code for single inserts; this is the first time I've tried to use it to iteratively insert a group of objects.
The error:
System.InvalidOperationException: The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: AcceptChanges cannot continue because the object's key values conflict with another object in the ObjectStateManager. Make sure that the key values are unique before calling AcceptChanges.
As stated in the prior question, the EntityKey is set to True, StoreGeneratedPattern = Identity. The actual table that is being inserted into is a relationship table, in that it is comprised of an identity field and two foreign key fields. The error always occurs on the second insert, regardless of whether that specific entity has been inserted before or not, and I can confirm that the values are always different, no key conflicts as far as the database is concerned. My suspicion is that it has something to do with the temporary entitykey that gets set prior to the actual insert, but I don't know how to confirm that, nor do I know how to resolve it.
My gut feeling is that the solution in the prior question, to set the SaveOptions to None, would not be the best solution. (See prior discussion here)
I've had this issue with my repository using a loop as well and thought that it might be caused by some weird race-like condition. What I've done is refactor out a UnitOfWork class, so that the repository.add() method is strictly adding to the database, but not storing the context. Thus, the repository is only responsible for the collection itself, and every operation on that collection happens in the scope of the unit of work.
The issue there is that: In a loop, you run out of memory damn fast with EF4. So you do need to store the changes periodically, I just don't store after every save.
public class BaseRepository : IRepository where TEntity : class
{
private TxRPEntities txDB;
private ObjectSet _objectSet;
public void Add(TEntity entity)
{
SetUpdateParams(entity);
_objectSet.AddObject(entity);
}
public void Save()
{
txDB.SaveChanges();
}
Then you can do something like
foreach(Requirement r in requirements)
{
var car = new CustomerAgreementRequirement();
car.CustomerAgreementId = viewModel.Agreement.CustomerAgreementId;
car.RequirementId = r.RequirementId;
_carRepo.Add(car); //Save new record
if (some number limiting condition if you have thousands)
_carRepo.Save(); // To save periodically and clear memory
}
_carRepo.Save();
Note: I don't really like this solution, but I hunted around to try to find why things break in a loop when they work elsewhere, and that's the best I came up with.
We have had some odd collision issues if the entity is not added to the context directly after being created (before doing any assignments). The only time I've noticed the issue is when adding objects in a loop.
Try adding the newed up entity to the context, do the assignments, then save the context. Also, you don't need to save the context each time you add a new entity unless you absolutely need the primary key.

Lazy fetching of objects using FindAllBy , for the first time

When I use criteria queries, the result contains array list of lazy initialized objects. that is, the list has values with handler org.codehaus.groovy.grails.orm.hibernate.proxy.GroovyAwareJavassistLazyInitializer.
This prevent me from doing any array operation (minus, remove etc) in it. When I use, GORM methods, I get array list of actual object types. How can I get the actual objects in criteria query?
The code is listed below.
availableTypes = Type.withCriteria() {
'in'("roleFrom", from)
'in'("roleTo", to)
}
availableTypes (an array list) has one value , but not actual object but value with a handler of GroovyAwareJavassistLazyInitializer
availableTypes (an array list) has values with type Type
availableTypes = Type.findByRoleFrom(from)
---------- Update ----------
I did further troubleshooting, and this is what I found. Probably the above description might be misleading, but I kept it in case it helps.
When using findAllBy for the first time, I get proxy objects rather than the actual instance. Then, I invoke the method through an ajax call, the actual instance is loaded (anything to do with cache loading??). When I refresh the page, it again loads the proxy
def typeFrom = Type.findAllByParty(partyFrom)
there is another use of findAllBy in the same method, which always returns actual instances.
def relFrom = Relation.findAllByParty(partyFrom)
When compared the two classes, the attribute 'party' of class Roles is part of a 1-m relation. like
class Role {
RoleType roleType
LocalDate validFrom
LocalDate validTo
static belongsTo = [party : Party ]
...
}
I know if I do statement like Party.findAll(), the role instances would be proxy till they access. But, when using gorm directly on the class (Role), why I am getting the proxy objects ???
thanks for the help.
thanks.
Turns out are a couple of possible solutions which I came across but didn't try, such as
Overloading the equals method so that the proxy and the domain
object use a primary key instead of the hashCode for equality
Using a join query so that you get actual instances back and not proxies
GrailsHibernateUtil.unwrapProxy(o)
HibernateProxyHelper.getClassWithoutInitializingProxy(object)
One solution that worked for me was to specify lazy loading to be false in the domain object mapping.
History of this problem seems to be discussed here: GRAILS-4614
See also: eager load

How can I use nHibernate to get a small subset of object properties for list view

I am using ASP.NET-MVC and nHibernate for the first time. Great tools, but big learning curve!
I have a list view of objects that are quite large (each has about 60 properties). In the list view I am only using about 10 of these properties. Performance is not too bad, but it seems a waste to fully hydrate these objects. What is the recommended practice?
I tried using HQL to select fewer properties, but it seems like it won't partially hydrate an object. I also tried making property on the main object that was a header class with the list view properties, but I couldn't seem to get it mapped properly. I think this should be easy but I've really been struggling with it.
EDIT:
I kept coming back to this because I knew Anton had given me the answer and I just couldn't see it.
There are three things you need to do:
Make an object with the properties you want.
Make a mapping file to import this object.
<hibernate-mapping
xmlns="urn:nhibernate-mapping-2.2"
namespace="Core.Entities"
assembly="Core"
default-access="property">
<import class="RequestHeader" />
</hibernate-mapping>
If you are using HQL, your object must contain a constructor with all the properties, in the same order, as your select new statement. If you use the Criteria API, you don't need to do this.
public IList<RequestHeader> ListAll()
{
using (ISession session = GetSession())
{
using (ITransaction tx = session.BeginTransaction())
{
IList<RequestHeader> results = session.CreateCriteria(typeof (Request), "r")
.CreateCriteria("Requestor", "req", JoinType.InnerJoin)
.CreateCriteria("r.Grant", "g", JoinType.InnerJoin)
.SetProjection(Projections.ProjectionList()
.Add(Projections.Property("r.Id"), "Id")
.Add(Projections.Property("r.Status"), "Status")
.Add(Projections.Property("r.SubmissionDate"), "SubmissionDate")
.Add(Projections.Property("req.Name"), "Requestor")
.Add(Projections.Property("g.Number"), "Number"))
.SetResultTransformer(Transformers.AliasToBean(typeof (RequestHeader)))
.SetMaxResults(10000)
.List<RequestHeader>();
tx.Commit();
return results;
}
}
}
60 properties is too much. See Component mapping.
As for selecting a subset of properties, see this: you need a select new HQL construct. Be aware, though, that you need an appropriate constructor and that an object you'll get cannot be saved back to the DB.

Resources