I have published a WebAPI service which returns a list of items. I am implementing Breeze and have managed to get it basically working with filtering/sorting. However, the Expand is not working.
http://www.ftter.com/desktopmodules/framework/api/dare/dares?$expand=ToUser
You can see the ToUserId ForeignKey in the response above, but the ToUser properties are NULL (the user definitely exists)
You can see the ToUser EF navigation property in the metadata.
When I use .Include on the server side I can populate it with EF, but I don't want to do this.
I checked the Breeze Tutorial 2 here on Expand: http://learn.breezejs.com/
Here is it without expand: http://learn.breezejs.com/api/northwind/Products
and here it is with Expand (And you can see the additional Category info): http://learn.breezejs.com/api/northwind/Products?$expand=Category
This is what I am trying to do but mine does not fill it...
UPDATE:
I downloaded the Breeze 1.3.6 Samples and loaded the DocCode solution in VS2011.
I ran it and saw that the client-side expand works;
e.g.
http://localhost:47595/breeze/Northwind/Orders?$top=1 (no expand)
http://localhost:47595/breeze/Northwind/Orders?$top=1&$expand=Customer (expands customer correctly).
I checked the WebAPI controller code and it looks the same, except they use EF Code First instead of Model First. The Foreign key is decorated with a property:
Breeze Sample that Works
[ForeignKey("CustomerID")]
[InverseProperty("Orders")]
public Customer Customer {get; set;}
It just doesn't make sense... it is something to do with my WebAPI controller or EntityFramework relationship...
UPDATE 2
I downloaded the most basic ToDo Knockout Breeze sample and added this line to the ToDoItem class: public User ToUser { get; set; }
I am then able to Expand the WebAPI call with http://localhost:63030/breeze/todos/Todos?$expand=ToUser
So I have come to the conclusion that it is something to do with the fact that I am using EntityFramework DB First and not Code First. It definitely does seem possible to do in the current version of the WebAPI with Breeze and EF.
UPDATE 3
I have narrowed it down to my database, EF Database First and Code First differences, but still not identified the issue. I have changed from a Model to a Code First approach with the exact same result (ie. no expand).
For example: if you look at this Expand on the Breeze site that works, http://learn.breezejs.com/api/northwind/Products?%24expand=Category, try change the last param to an invalid field and it throws an error, e.g. :
http://learn.breezejs.com/api/northwind/Products?%24expand=Category1
However, in my code, it always ignores this param and returns ALL the records, and never throws an exception if the Expand param is incorrect:
http://www.ftter.com/desktopmodules/framework/api/dare/dares?$expand=To4657657User
Hence I am stumped.. I have no idea why this is not working.
My Code
[HttpGet]
[Queryable(AllowedQueryOptions = AllowedQueryOptions.All)]
public HttpResponseMessage Dares()
{
var response = Request.CreateResponse(HttpStatusCode.OK, (IQueryable<Dare>)contextProvider.Context.Dares);
return ControllerUtilities.GetResponseWithCorsHeader(response);
}
and here is the generated class from my EF model (using Database First)
public partial class Dare
{
public int DareId { get; set; }
public int ToUserId { get; set; }
public virtual User ToUser { get; set; }
}
Your URL seems to be missing the $ for the expand query option...should be $expand.
I think I have found the problem - the IQueryable with the HttpResponseMessage return type does not behave the same as a pure IQueryable return type. expand seems to work when I do not wrap it.
I have raised a new question here:
How to use Breeze IQueryable with CORS?
Related
I am having trouble when trying to delete a field of an Entity using Entity Framework (version 6.1.3).
Let's say I have two Entities: Person and Work.
I can change the work of a person without any issue, but when I try to express that the person is unemployed it does not work properly:
person.Work = null;
db.SaveChanges();
After running this code the person still will have the previous work, but if I use the debugger and check the Work property of person before running
person.Work = null;, everything will behave as expected.
Can someone please explain why reading the value first makes the code work properly and how to correctly delete the field?
var work = person.Work; \\ with this line here everything works as expected
person.Work = null;
db.SaveChanges();
Two things that are contributing to your issue:
Entity Framework determines what needs to updated during SaveChanges by tracking changes to property values.
You probably have lazy loading enabled (both in general and for the Work property), which means that if the person has an associated Work, that associated entity doesn't get loaded until the first time you access that property.
Putting those together, when you set person.Work = null without accessing person.Work (which would trigger a load), the context thinks nothing has changed. But if you load the property first, setting the property to null tells EF to remove that association. Edit: According to the page that octavioccl linked, this is true for .NET 4.0., but for .NET 4.5+ (and EF 5+), loading first is unneeded.
Possible solutions
If you want to remove the association without loading the related entity, you'll need to add a foreign key property to your Person entity, then you can set that to null instead of setting the navigation property to null. For example:
public class Person
{
// other properties...
public int? WorkId { get; set; }
public virtual Work { get; set; }
}
person.WorkId = null;
db.SaveChanges();
octavioccl's answer quoted another option:
context.Entry(person).Reference(p => p.Work).CurrentValue = null;
From this msdn page:
To delete the relationship, set the navigation property to null. If
you are working with the Entity Framework that is based on .NET 4.0,
then the related end needs to be loaded before you set it to null. For
example:
context.Entry(person).Reference(p => p.Work).Load();
person.Work = null;
Starting with the Entity Framework 5.0, that is based on .NET 4.5, you
can set the relationship to null without loading the related end. You
can also set the current value to null using the following method:
context.Entry(person).Reference(p => p.Work).CurrentValue = null;
The question could also look like "Why is my initialization code of object in server not working?".
For example,
public class Order
{
public int Id { get; set; }
public int Quantity { get; set; }
public Order()
{
Quantity = 10;
}
}
From debugger, I can see the contructor is called and Quantity is set, however, it is not taking effect. I have to set Quantity on client side after the entity is created to make it work.
Is there a way to make the initialization on server work? By the way, my project is in Angular/Breeze/EF.
UPDATE: As I dig a little further, I believe, this is the general "issue" with Breeze that the server side change must be added to so-called entityInfo.OriginalValueMap, otherwise, its change is not saved. If true, how can work around this limitation because I have a lot default values I'd like to set on server?
This is how I create my entity:
var manager = new breeze.EntityManager("breeze/breeze");
manager.enableSaveQueuing(true);
function _createEntity(entityName) {
return manager.createEntity(entityName);
}
Setting any initialization code on the server in the model constructor won't work simply because the JavaScript client doesn't know anything about the C# constructor code on the server.
The DefaultValueAttribute is only honored when you're constructing a Model-First metadata. It is unfortunately ignored by EF when constructing a Code-First model metadata.
I suggest that you see Breeze - Create Entity on Server side for how another user solves a similar situation by creating a "create Endpoint" on the server that basically returns a new entity with default values set.
You don't have to create a constructor to set default values.
Just add the default value data annotation to any property you wish to set its default value:
[DefaultValue(10)]
public int Quantity { get; set; }
Also, consider not to initialize the Quantity when creating an entity at the client side.
In play framework, I use following code to fetch values from a table called "Report" which has other relationship tables like "Project","Build" etc.
List<Report> rpts = Report.find.where()
.eq("publish","1")
.eq("functionality_id", Integer.toString(fun.id))
.eq("project_id", currentProject.id)
.eq("prod_build", prod_build)
.eq("loadType_id", loadType_id)
.in("build_id", buildId)
.orderBy("id desc")
.findList();
I get list of values from "Report" table, but all related table values are not populated. They are populated with null.
#Entity
#Table(name="report")
public class Report {
#Id
public int id;
#Constraints.Required
#ManyToOne
#JoinColumn(name="build_id")
public Build build;
#Constraints.Required
#ManyToOne
#JoinColumn(name="project_id")
public Project project;
.
.
}
It was loaded with those values when I tested couple of days ago, but not working today. When I do rpts.get(i).build.release , it gives nullPointerException because build is null here. This code has not been changed in recent days. Wondering why this is happening. Can someone suggest me whether there is any other setting file (like application.conf) that does this lazy loading. Please help.
I've resolved it.
The problem is that I created an Ebean transaction using following code that caused the trouble.
Ebean.beginTransaction();
try{
group.role = "A";
Ebean.update(group);
Ebean.commitTransaction();
} finally {
Ebean.endTransaction();
}
I never suspected that this could would have caused the problem as I put this code in another class file that is not related to this page. Then I changed to following code and everything worked as expected.
#Transactional
public void saveGroup(Group group){
group.role = "A";
Ebean.save(group);
.
.
}
Following documentation in play framework site helped me to identify the culprit. :)
Enhancement of direct Ebean field access (enabling lazy loading) is
only applied to Java classes, not to Scala. Thus, direct field access
from Scala source files (including standard Play 2 templates) does not
invoke lazy loading, often resulting in empty (unpopulated) entity
fields. To ensure the fields get populated, either (a) manually create
getter/setters and call them instead, or (b) ensure the entity is
fully populated before accessing the fields.
this is really annoying
I have something like this:
class Person {
..properties id, name etc..
}
class Task {
..properties id, name etc..
Person Moderator {get;set}
}
public class DataModel : DbContext {
public DbSet<Task> Tasks { get; set; }
public DbSet<Person> People { get; set; }
}
I can then create new tasks and add People objects to the task and save, and I can see the data correctly saved in the sql backend - each Task saved has the correct Person id saved
with it and the Person with that id is saved back as well.
But when I try and get back a task, the person object is always null.
using (DataModel db = new DataModel()) {
Task t = db.Tasks.SingleOrDefault(p => p.Id == 22);
assert(t.Name.Lenght>0)
assert(t.Moderator != null) // always null!!!!!!
....
}
What do I have to do to get the whole object graph bought back? Do I have to do a join in the SingleorDefault call? seems a bit wrong somehow.
Did I mention this is really annoying.
TIA,
Two options for you. By default the code first / dbContext model returns a proxy object that derives from your model (this is important to understand when you run into JSON serialization issues). The proxy object uses lazy loading of associations but only under certain circumstances. The Moderator property has to be declared as virtual so that the proxy can override it and do the lazy loading for you.
However lazy loading can create a problem called Select N+1. If in most cases you only need the Task and not the Moderator, this won't be a problem. However if you frequently display a list of tasks and their associated moderators, you will effectively have to run an extra round trip to the database for every task in that list in addition to the 1 for the original list(e.g. for a list of 100 tasks you would do 101 queries to display the tasks and their moderators).
To get around this, EF provides the Include operator, this forces the relation to load. Use it as such
Task t = db.Tasks.Include(t=>t.Moderator).SingleOrDefault(p => p.Id ==
22);
Hope this helps.
You have lazy loading turned off for your Moderator property, so it will only be loaded if you explicitly do so using Load().
You can force EF to eagerly load your related Person entity by using the Include() method in your query like this:
Task t = db.Tasks.Include(x => x.Moderator).SingleOrDefault(p => p.Id == 22)
There is a pretty good overview in this article.
I am using DB First EF 4.1 and I am adding DbContextGenerator tt template to my model. This is all great, but I end up with classes like this:
public partial class t_city
{
public t_city()
{
this.t_neighborhood = new HashSet<t_neighborhood>();
}
public int city_id { get; set; }
public string city_name { get; set; }
public virtual ICollection<t_neighborhood> t_neighborhood { get; set; }
}
This is super ugly. I modified the template to generate properties in camelcase, but that breaks the mapping onto tables and columns. Is there way to get clean class names and still preserve the mapping?
EDIT
Looks like it's possible by renaming the objects inside the Entity Model file. The only question remains, is it possible to automate the renaming using a function, or does it have to be done manually each time?
Thanks!
You need to do it manually but that's needed only once for each entity / property. These changes are not deleted when you update your model from the database.
The only automation can be implemented as some processing of EDMX file. It is XML with defined schema so you can process that XML in your custom tool or XSLT transformation and automatically change property and entity names in CSDL and MSL.