I insert an Entity with this request and it is successful.
Request
{
"Title": "Book 1"
}
Response
{
"id": 1,
"title": "Book 1",
"serialId": "00000000-0000-0000-0000-000000000000",
"borrows": null,
"votes": null
}
I checked the database and there is a Book row with id = 1. But now when I try to fetch a list it returns an error because the DbSet is empty after the insert.
var books = db.Books; // count = 0
The response for /books is the exception:
"exceptionMessage": "The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'.",
And the inner exception includes Error getting value from 'Borrows' so I thought it might be to do with how the relationship is defined, also there are no Borrow entities in the database yet.
"exceptionMessage": "Error getting value from 'Borrows' on 'System.Data.Entity.DynamicProxies.Book_6E27A1F717202EA02AE923CCC6405EF9A501FE9A54A71841CEB43E942224D88A'.",
"exceptionType": "Newtonsoft.Json.JsonSerializationException",
The Book entity has a navigation property of List on it with a defined relationship:
modelBuilder.Entity<Book>().HasMany<Borrow>(b => b.Borrows);
Book's entity defined with a list of Borrow entities:
public virtual List<Borrow> Borrows { get; set; }
The migration of Book only has it's Id for the primary key:
CreateTable(
"dbo.Books",
c => new
{
Id = c.Int(nullable: false, identity: true),
Title = c.String(),
SerialId = c.Guid(nullable: false),
})
.PrimaryKey(t => t.Id);
Question: What is causing this exception, is it something to do with how I defined the relationship between Book and Borrow? I've seen examples of relationships having to be defined in both directions, do I need to define the relationship to not require a borrow somehow? I've noticed DbSet Count = 0 occur in the past when there have been results so I think it could also be an issue with NewtonSoft. I'm not sure what is going on here.
Full exception: https://pastebin.com/raw/BvUfBnKU
I got it working by using this answer: https://stackoverflow.com/a/14317598/11419029, returning a List rather than IQueryably.
I changed it to this:
// GET: api/Books
public List<Book> GetBooks()
{
var books = db.Books.ToList();
return books;
}
From this:
// GET: api/Books
public IQueryable<Book> GetBooks()
{
var books = db.Books;
return books;
}
But I'm still not sure what the problem was, another answer said it was to do with a query being executed while iterating results of another query. Triggering lazy loading. Now I'm concerned it is executing nested queries, querying borrows for every instance of a book.
Related
So I have two Database Tables
Club
- ClubId
- ClubName
ClubMembers
ClubMemberId
ClubId
FirstName
LastName
in my api controller I have
private ClubsEntities db = new ClubsEntities();
// GET: api/Clubs
public IQueryable<Club> GetClub()
{
return db.Club;
}
But when I hit it i get data returned from both tables
[{"ClubMember":[{"ClubMemberId":1,"ClubId":1,"FirstName":"John","LastName":"Smith"}],"ClubId":1,"ClubName":"Test"},{"ClubMember":[],"ClubId":2,"ClubName":"Test 2"}]
How can I get it to just return from club this is baffling me
Ok so ive figured out that if I just specify a new class just with the values I want returned and select those values, it will keep my returned data cleaner.
return db.Club.Select(c => new myClubs { ClubId = c.ClubId, ClubName = c.ClubName }).ToList();
I have two tables in my database that I am filling via Web API:
Orders
________
OrderID
OrderDate
OrderStatusID
and
OrderItems
___________
OrderID
ItemID
ItemNote
ItemSortOrder
and below is the method from my controller that I use to post Order to the database which works fine, I unit tested it previously.
[ResponseType(typeof(myClass.Order))]
[HttpPost]
public IHttpActionResult PostNewOrder(myClass.Order ord1)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
using (MyContext ctx = new MyContext())
{
ctx.Orders.Add(ord1);
ctx.SaveChanges();
return CreatedAtRoute("DefaultApi", new { id = ord1.OrderID }, ord1);
}
}
My question is - how can I return the OrderID of the newly inserted order from the database? OrderID field is an identity field in the database
No need to add any new code.
As soon as you call ctx.SaveChanges(); the object ord1 will then have the new id in the property OrderID.
You can do that by getting the Database values:
var dbObject = ctx.Entry(ord1).GetDatabaseValues();
return CreatedAtRoute("DefaultApi", new { id = dbObject["OrderID"] }, ord1);
ord1.OrderID will have the correct order id but you have to make sure that Entity Framework is mapped so that it knows the database will generate the value for the id. If you do that then the id is read back when you create your new order (or any entity that is added with an id column with that same mapping configuration applied).
You can map this on the id using either fluent mapping or an attribute. Here is the syntax for fluent:
modelBuilder.Entity<Order>()
.Property(x => x.OrderID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
And for attributes
[DatabaseGenerated(DatabaseGenerationOption.Identity)]
public int OrderID { get; set; }
If you are using the designer (.EDMX file) for mapping see the the image below as an example of how you can set the store generation pattern.
Image was copied from a previous SO answer. https://stackoverflow.com/a/6306817/1260204
Note - you should pick one or the other, not both.
I'm guessing on Entity Framework based on the name used in the context.
I am trying to figure out how to seed a complex entity framework code first object. Currently the code I have allows me to insert simple objects but there has to be a way do more complex items, i.e. Address with an AdressType field.
context.AddressTypes.AddOrUpdate(
p => p.Name,
new AddressType { Name = "Original" },
new AddressType { Name = "Shipping" },
new AddressType { Name = "Billing" }
);
context.Addresses.AddOrUpdate(
a => a.Address1,
new Address
{
Address1 = "1234 West Main",
City = "Hannibal",
Region = "MO",
PostalCode = "12345",
Country = "USA",
Type = context.AddressTypes.Where(a=>a.Name=="Original")
});
But while I can "Find" an addresstype by id I can't do a "Where" name equals. Find would work except that I can not guarantee what the id will be with each seed and want to base it on name instead.
Any suggestions on how to do this?
TIA
Solution:
Added a reference System.Linq to the seed class. Then was able to the where clause.
I'm trying to get data from my Webapi2 Breeze controller with Entity Framework 6 and .NET 4.5.1. And get the error "unable to locate property" when I use the Where clause on a navigation property. The call is not even made to the Webapi2 Controller
If I leave out the where clause, the data is returned correctly.
The relevant part of the c# class:
public class NotificationRule {
public Guid NotificationRuleId { get; set; }
public virtual NotificationRuleSet NotificationRuleSet { get; set; }
}
The relevant part of the C# class in the navigational property NotificationRuleSet:
public class NotificationRuleSet{
public Guid NotificationRuleSetId { get; set; }
public virtual List<NotificationRule> NotificationRules { get; set; }
}
The relevant part of the C# Breeze controller:
public IQueryable<NotificationRuleSet> NotificationRuleSets()
{
return _contextProvider.Context.NotificationRuleSets;
}
public IQueryable<NotificationRule> NotificationRules()
{
return _contextProvider.Context.NotificationRules;
}
The relevant part of the Query (Typescript):
var query = breeze.EntityQuery.from("NotificationRules")
.where ("NotificationRuleSet.NotificationRuleSetId","==", this.NotificationRuleSetId)
.expand("NotificationRuleSet");
var Result = this.BreezeEntityManager
.executeQuery(query)
.then((data) => this.RefreshViewModelCallback(data))
.fail((data) => alert("Fail to retrieve data"));
If I leave the Where clause out, the data is transferred correctly as you can see in this Fiddler dump:
{
"$id": "1",
"$type": "Imp.Classes.NotificationRule, Imp",
"NotificationRuleId": "11111111-be1e-423c-ac5b-f2c689093aca",
"NotificationRuleSet": {
"$id": "2",
"$type": "Imp.Classes.NotificationRuleSet, Imp",
"NotificationRuleSetId": "11111111-1bd6-4520-9f69-381504b8e2b2",
"NotificationRules": [
{
"$ref": "1"
}
],
},
}
So I get an error that a property does not exists, but it seems to exists.
Using a Where on a non navigational property works fine.
I've read something about camelCasing but replacing NotificationRuleSet with notificationRuleSet gives the same error.
EDIT:
The solutions seems to be that NotificationRules in the Viewmodels query should start with a lowercase character, regardless wether the first character of the controllers method name is upper or lowercase .
camelCasing is most likely your issue provided both the entity and property do exist -
.where('notificationRuleSet.notificationRuleSetId', "==", this.NotificationRuleSetId)
Remember that when you are camelCasing your property names it is for the navigation property as well.
I thought I had an explanation after reviewing you interaction with PW Kad.
My guess was that the ACTUAL defaultResourceName for your NotificationRule type is "notificationRules".
Can you tell me what it is? The following expression will reveal it:
manager.metadataStore.getEntityType('NotificationRule').defaultResourceName;
Another question. You say it fails. What is the failure exception (check the payload of the response). Is it something like this?
$id: "1",
$type: "System.Web.Http.HttpError, System.Web.Http",
Message: "The query specified in the URI is not valid.",
ExceptionMessage: "A binary operator with incompatible types was detected. Found operand types 'Edm.Guid' and 'Edm.String' for operator kind 'Equal'.",
ExceptionType: "Microsoft.Data.OData.ODataException",
Here is what I was thinking. Most of the time, Breeze doesn't need to know the root type of a query when that query is sent to the server. It can simply wait for the query results and reason over the JSON to determine the type (or types) involved.
But the occasional query involves a filter comparison that is ambiguous in its data type. GUIDs are a good example. Unless breeze knows the query root type it can't know for sure if the "11111111-be1e-423c-ac5b-f2c689093aca" in "foo == '11111111-be1e-423c-ac5b-f2c689093aca'" should be a string or a GUID. A human would guess it's a GUID; Breeze is not so sure. You can be sure only if you know the datatype of the "foo" property.
Breeze will compose the query anyway. If it guesses string if produces a URL that looks like "...foo eq '11111111-be1e-423c-ac5b-f2c689093aca'..." and that will fail (for me anyway).
I thought this could be your issue.
I tried an experiment in DocCode that I thought would demonstrate it. I changed the endpoint name for an Order query to something that is NOT the Order type's defaultResourceName(which is "Orders").
As it happens, Web API doesn't care if the URL says 'orders' or 'Orders' so I can achieve my goal of confusing Breeze about the root type by pointing the query to "orders" and the query should still be routed by Web API to the proper controller GET method.
I was expecting that Breeze would compose the GUID query as a string and thus I could duplicate your issue. Here is my attempt
/*********************************************************
* Orders of Customers with Alfred's ID
* Customer is the related parent of Order
* CustomerID is a GUID
* Demonstrates "nested query", filtering on a related entity
* where the filter criteria is ambiguous (string or GUID)
* and only knowing the root query type (Order) can disambiguate.
* The 'defaultResourceName' for Order is "Orders", not "orders"
* so I expect this test to fail ... but it doesn't ...
* because Breeze disambiguated anyway.
*********************************************************/
test("orders of Customers w/ Alfred's ID (resource name is lowercased)", 2, function () {
var query = EntityQuery.from("orders")
.where("Customer.CustomerID", "==", testFns.wellKnownData.alfredsID)
.expand("Customer");
verifyQuery(newEm, query, "orders query", showOrdersToAlfred);
});
Unfortunately, the test passes!
This is the URL that Breeze sent to the server:
http://localhost:56337/breeze/Northwind/orders?$filter=Customer.CustomerID eq guid'785efa04-cbf2-4dd7-a7de-083ee17b6ad2'&$expand=Customer
DAMN Breeze (v.1.4.12) was too smart for me. It somehow figured out that my comparison value is a GUID ... despite not knowing the root type of the query.
That means I do not have an explanation for why, in your example, breeze.EntityQuery.from("notificationRules") works but breeze.EntityQuery.from("NotificationRules") does not.
Maybe I'll have another idea once you tell us the defaultResourceName AND show us the URLs that are generated (a) when it works and (b) when it does not work.
I want to hydrate a collection of entities by passing in a comma delimited list of Ids using EF5 Code First.
I would previously have created a table function in t-sql, passed in the comma delimited list of Ids, I'd then join this table to the target table and return my collection of records.
What is the most performant way of using EF5 Code First to achieve the same?
Update: I want to avoid having the full set of entities in memory first.
Update2: I'd ideally like the order of the entities to match the delimited list.
I'd say to start out by converting the comma delimited list into a List<int> that contains all of the IDs that you would be going for. At that point, using your particular DbContext class you would do the following:
var entities = db.MyEntities.Where(e => myListOfIds.Contains(e.ID)).ToList();
Note: I only put the ToList at the end there because you were talking about hydrating the collection. Otherwise, with IEnumerable, there will be deferred execution of the query, and so it will not populate right away.
You could do it like this, where you restrict the set of Entity objects by checking if their IDs belong to your list of IDs:
// Dummy list of POCO 'entity' objects (i.e. the Code first objects) just for the sake of this snippet
var entities = new List<Entity>();
entities.Add(new Entity() { ID = 1, Title = "Ent1" });
entities.Add(new Entity() { ID = 2, Title = "Ent2" });
entities.Add(new Entity() { ID = 3, Title = "Ent3" });
// List of ids to match
var ids = new List<int>();
ids.Add(1);
ids.Add(2);
// LINQ:
var selected = (from e in entities where ids.Contains(e.ID) select e).ToList();
Just for completeness, this is the dummy class used above:
// POCO (Code first) object
private class Entity
{
public int ID { get; set; }
public string Title { get; set; }
}