In Breeze (entityframework, knockout) how can I access the original Entity after a query has completed - breeze

I am wanting to pass down some pseudo information about an Entity to the client and have that available with my entity. From what I can tell only mapped properties of the entity are available after query execution. Is there a hook I am missing or some other way I can get at the raw results of the API call that fetched a given entity from the server?
e.g:
server:
class Patient
{
[NotMapped]
public string Name => First + " " + Last;
public string First {get;set;}
public string Last {get;set;}
}
client:
this._executeQuery(breeze.EntityQuery.from('api/Patient/1'))
.then(function (data) {
var data = data[0];
// data = PatientCtor with first and last properties only
});
I would like data.name to be available in the client. It is sent down with the query to the client but does not come out on the other end of the query.

You can add the properties to the client-side definition of the entity, and Breeze will populate them for you. You can do this by registering a custom constructor function for the entity:
function Patient() {
this.name = "";
}
var em = new breeze.EntityManager();
em.metadataStore.registerEntityTypeCtor("Patient", Patient);
Note that you need to do this before your first query.
Then, when breeze creates Patient entities as a result of a query, it will populate the unmapped name property.
See the Breeze documentation on Extending Entities.

Related

Parallel calls to a SQL Server database from view model

I have simple view model, which is populating itself but the problem is that a webpage contains a lot of select lists and for every one select list I am calling database procedure for getting data list.
Is it possible, for performance, to execute database calls asynchronous or in parallel?
I have this kind of code :
// controller
public ActionResult Index()
{
model = new SampleViewModel();
model.Populate(database);
return View(model);
}
// view model
public class SampleViewModel
{
public SampleViewModel(DbContext db)
{
_list1 = context.Db.SqlQuery<SelectList1>("SELECT Id, Value FROM dbo.Table1").ToList();
_list2 = context.Db.SqlQuery<SelectList2>("SELECT Id, Value FROM dbo.Table2").ToList();
_list3 = context.Db.SqlQuery<SelectList3>("SELECT Id, Value FROM dbo.Table3").ToList();
_list4 = context.Db.SqlQuery<SelectList4>("SELECT Id, Value FROM dbo.Table4").ToList();
_list5 = context.Db.SqlQuery<SelectList5>("SELECT Id, Value FROM dbo.Table5").ToList();
}
private readonly List<SelectList1> _list1;
public int SelectedList1Id { get; set; }
public IEnumerable<SelectListItem> List1 { get { return new SelectList(_list1, "Id", "Value");} }
-//- _list2
-//- _list3
-//- _list4
-//- _list5
}
As you can see, _list3 is waiting for _list2 and _list2 is waiting for _list1 and this can slow request a lot. The reason why view model is populating itself is because in real scenario these select lists are related to each other and the model contains information about selected Ids and with these Ids I can rebuild the select lists for example if model validation failed.
Any idea? Can I use some async await approach and will it help me in this case against SQL Server 2008 ?
You may use Task Parallel Libraries Paralle.Invoke method to execute many tasks in parallel.
Parallel.Invoke(() =>{
// Execute some code here
}, () =>
{
// Execute some other code here
});
I personally do not pass a concrete DbContext object to my view model. View models should be simple POCO. It should not have any knowledge of your data access technology. So my personal preference is keeping the data access code seperate from my view model. So i never read values from database in a view model constructor with a concrete object like you did.
Assuming you have a simply POCO view model like this
public class CreateViewModel
{
public List<SelectListItem> States {set;get;}
public List<SelectListItem> UserTypes {set;get;}
}
In your GET action, you can use Parallel.Invoke to load the 2 properties data.
var vm = new CreateViewModel();
Parallel.Invoke(() =>{
vm.States = db.States.Select(s=>new SelectListItem { Value=s.Id.ToString(),
Text=s.Name }).ToList();
}, () =>
{
vm.UserTypes= db.UserTypes.Select(s=>new SelectListItem { Value=s.Id.ToString(),
Text=s.Name }).ToList();
});
return View(vm);
Caching
If these are frequently accessed items for your dropdown, I suggest you cache this data instead of querying the db table every time. You may consider using the default MemoryCache.

CreateEntity failure with KeyProperties not defined

The Client is retrieving the metadata from a service. That call is succeeding.
But the call entityManager.CreateEntity(); is failing.
The error is:
"There are no KeyProperties yet defined on EntityType:
'Customer:#MyCommerceServer.Models'. Please insure that the metadata
for this type is complete either by calling FetchMetadata or by
explicitly updating the KeyProperties before creating an EntityKey for
this type."}
But the following passes with an exception says the customer is detached.
var customerType = entityManager.MetadataStore.GetEntityType(typeof(Customer));
var customer = customerType .CreateEntity();
Here is my set up. The Customer entity has a key named Id. The Customer entity on the client also has the same key. The entities on client and server exist in the same namespace.
Is there any setup I have to add to have the Customer entity KeyProperties? I see the same problem in the ToDo sample project also.
******** Update on 8/12/2014
On the server:
namespace MyCommerceServer.Models
{
public class Customer
{
public int Id { get; set; }
}
}
On the client:
namespace MyCommerceServer.Models
{
public class Customer : BaseEntity
{
public int Id
{
get { return GetValue<int>(); }
set { SetValue(value); }
}
}
}
The difference between the Breeze.sharp EntityManager.CreateEntity method and the EntityType.CreateEntity method is that the first, by default, adds the newly created entity to the EntityManager whereas the 2nd does not. The error you are getting occurs when an entity is added to an EntityManager and that entity either has no key defined or the key is set to just the default values for all of the key properties. The key is needed before the entity can be attached because the entity is cached in the EntityManager by its key.
So you have several options,
You can set the key properties in the EntityManager.createEntity call using an anon object like so:
var newCust = (Customer) myEntityManager.CreateEntity(typeof(Customer),
new { Id = 999 }));
or you can use the EntityType.CreateEntity method and set the Id before adding the entity to the entityManager
var customerType = myEntityManager.MetadataStore.GetEntityType(typeof(Customer));
var customer = customerType.CreateEntity();
customer.Id = 999;
myEntityManager.AddEntity(customer);
or you can change your metadata for the customer type to use Identity keys. This will mean that the AutoGeneratedKeyType property of the customer is set to either Identity or KeyGenerator. Note that either of these will require changes to your server side model to accomodate the change.
var customerType = entityManager.MetadataStore.GetEntityType(typeof(Customer));
Assert.IsTrue(customerType.AutoGeneratedKeyType == AutoGeneratedKeyType.Identity);

Send records count from server to breeze when custom query is used

return Repository.Clients.OrderByDescending(cl => cl.ID).AsQueryable().Skip(startIndex).Take(pageSize);
I am executing a custom breeze query in web api and want the total no of records fetched on client side. How can I send the records count from server to client. Since its a custom query, data.inlineCount will not work here that is usually used with the breeze query.
Just create a complex object on the server to pass back with the results and an inlineCount property on it so that Breeze interprets it correctly -
public class returnResult
{
public returnResult()
{
Results = new List<MyObject>();
}
public int InlineCount{ get; set; }
public List<MyObject> Results { get; set; }
}
And stuff your results into the results property and the count in the inlineCount property.
In fact you could do this with any property name and just pull the property out in your query results -
return manager.executeQuery(query).then(querySucceeded);
function querySucceeded(data) {
var inlineCount = data.totalRecords;
}

Insert to multiple foreign key tables in MVC with super-type/sub-type database-first model

I have two sub-types of a super-type "Entity", namely "Household" and "Involved Body".
I've modeled them as shown below in my database and they were auto-generated to the EF Model (again shown below).
database
edmx model
Using the default scaffolding for MVC I am able to add a new Household without any problems. However, when I try to add a new Involved Body I hit an error when it tries to add the Entity Type.
There only relevant (as far as I can tell) difference between the two sub-types is that the EntityType for a Household is hard-coded as "Household" whereas the EntityType for an Involved Body can be any EntityType except "Household" - this is selected from a list by the user.
The Create Action on the HTTP POST for the Involved Body throws an error relating to the foreign key between tEntity and tEntityType with the tEntityType being null. Code as follows:
[HttpPost]
public ActionResult Create([Bind(Exclude = "entityID")]tEntity tentity
, tInvolvedBody tinvolvedbody
, tAddress taddress
, tAddressEntity taddressentity
//, tEntityType tentitytype
, int entityTypeID
)
{
#region entity type
//find entity type from id
var tentitytype = db.tEntityTypes.Find(entityTypeID);
#endregion
#region address
//assume start date of involved body not needed for reporting
taddressentity.startDate = DateTime.Now.Date;
#endregion
if (ModelState.IsValid)
{
db.tEntities.Add(tentity);
db.tInvolvedBodies.Add(tinvolvedbody);
db.tAddresses.Add(taddress);
db.tAddressEntities.Add(taddressentity);
db.SaveChanges();
return RedirectToAction("Index");
}
//recreate viewbag for entityType dropdown
var q = (
from e in db.tEntityTypes
where e.entityType != "Household"
select e
);
ViewBag.entityTypeID = new SelectList(q, "entityTypeID", "entityType");
return View(tinvolvedbody);
}
I've tried adding the tEntityType to the parameters list for the create but this results in the ModelState.IsValid returning false due to the entityType being null on all the objects.
I've also tried actively linking the entity type to each of the other objects using:
tentity.tEntityType = tentitytype;
tinvolvedbody.tEntity.tEntityType = tentitytype;
taddressentity.tEntity.tEntityType = tentitytype;
The above ends up working but it creates a new Entity for each of the other objects i.e. I get three new rows in my tEntity table, one is the Entity, one links to tInvolvedBody and one links to tAddressEntities. This makes no sense...
How can I insert a new InvolvedBody that creates an Entity, picks up the Entity Type and then links to the AddressEntity junction table?
Finally worked through this. Not sure if the answer is 'perfect' from a developer perspective but it works.
After intense debugging I realised that the navigation properties for the involved body and address entity were both looking for an entitytypeID which I had assumed would be provided by the entity object.
If I passed these in directly with the code shown:
tinvolvedbody.tEntity.tEntityType = tentitytype;
taddressentity.tEntity.tEntityType = tentitytype;
...I ended up with three new entitites and no relational data existing between all of {entity, involved body, address}
The code that works removes the explicit addition of a new entity and relies on EF to create an entity from the Involved Body. I then used the newly created entityID to map the address via addressentity as follows:
[HttpPost]
public ActionResult Create([Bind(Exclude="entityID")]tEntity tentity
,tInvolvedBody tinvolvedbody
,tAddress taddress
,tAddressEntity taddressentity
,int entityTypeID
)
{
#region entity type
var t =
(
from e in db.tEntityTypes
where (e.entityTypeID == entityTypeID)
select e
);
tinvolvedbody.tEntity.tEntityType = t.First();
#endregion
#region address
//assume start date of involved body not needed for reporting
taddressentity.startDate = DateTime.Now.Date;
#endregion
if (ModelState.IsValid)
{
db.tInvolvedBodies.Add(tinvolvedbody);
db.tAddresses.Add(taddress);
taddressentity.tEntity = db.tEntities.Find(tinvolvedbody.bodyID);
db.tAddressEntities.Add(taddressentity);
db.SaveChanges();
return RedirectToAction("Index");
}
Have you tried setting the typeID specifically? Also, from what I gathered from your model, the taddress is a child of taddressentity? As such, should it not be inserted first in order for the foreign key?
if (ModelState.IsValid)
{
tentity.entityTypeID = entityTypeID;
db.tEntities.Add(tentity);
tinvolvebody.bodyID= tentity.entityID
db.tInvolvedBodies.Add(tinvolvedbody);
taddressentity.entityID = tentity.entityID;
db.tAddressEntities.Add(taddressentity);
taddress.UPRN = taddressentity.UPRN;
db.tAddresses.Add(taddress);
db.SaveChanges();
return RedirectToAction("Index");
}

Json and Circular Reference Exception

I have an object which has a circular reference to another object. Given the relationship between these objects this is the right design.
To Illustrate
Machine => Customer => Machine
As is expected I run into an issue when I try to use Json to serialize a machine or customer object. What I am unsure of is how to resolve this issue as I don't want to break the relationship between the Machine and Customer objects. What are the options for resolving this issue?
Edit
Presently I am using Json method provided by the Controller base class. So the serialization I am doing is as basic as:
Json(machineForm);
Update:
Do not try to use NonSerializedAttribute, as the JavaScriptSerializer apparently ignores it.
Instead, use the ScriptIgnoreAttribute in System.Web.Script.Serialization.
public class Machine
{
public string Customer { get; set; }
// Other members
// ...
}
public class Customer
{
[ScriptIgnore]
public Machine Machine { get; set; } // Parent reference?
// Other members
// ...
}
This way, when you toss a Machine into the Json method, it will traverse the relationship from Machine to Customer but will not try to go back from Customer to Machine.
The relationship is still there for your code to do as it pleases with, but the JavaScriptSerializer (used by the Json method) will ignore it.
I'm answering this despite its age because it is the 3rd result (currently) from Google for "json.encode circular reference" and although I don't agree with the answers (completely) above, in that using the ScriptIgnoreAttribute assumes that you won't anywhere in your code want to traverse the relationship in the other direction for some JSON. I don't believe in locking down your model because of one use case.
It did inspire me to use this simple solution.
Since you're working in a View in MVC, you have the Model and you want to simply assign the Model to the ViewData.Model within your controller, go ahead and use a LINQ query within your View to flatten the data nicely removing the offending circular reference for the particular JSON you want like this:
var jsonMachines = from m in machineForm
select new { m.X, m.Y, // other Machine properties you desire
Customer = new { m.Customer.Id, m.Customer.Name, // other Customer properties you desire
}};
return Json(jsonMachines);
Or if the Machine -> Customer relationship is 1..* -> * then try:
var jsonMachines = from m in machineForm
select new { m.X, m.Y, // other machine properties you desire
Customers = new List<Customer>(
(from c in m.Customers
select new Customer()
{
Id = c.Id,
Name = c.Name,
// Other Customer properties you desire
}).Cast<Customer>())
};
return Json(jsonMachines);
Based on txl's answer you have to
disable lazy loading and proxy creation and you can use the normal methods to get your data.
Example:
//Retrieve Items with Json:
public JsonResult Search(string id = "")
{
db.Configuration.LazyLoadingEnabled = false;
db.Configuration.ProxyCreationEnabled = false;
var res = db.Table.Where(a => a.Name.Contains(id)).Take(8);
return Json(res, JsonRequestBehavior.AllowGet);
}
Use to have the same problem. I have created a simple extension method, that "flattens" L2E objects into an IDictionary. An IDictionary is serialized correctly by the JavaScriptSerializer. The resulting Json is the same as directly serializing the object.
Since I limit the level of serialization, circular references are avoided. It also will not include 1->n linked tables (Entitysets).
private static IDictionary<string, object> JsonFlatten(object data, int maxLevel, int currLevel) {
var result = new Dictionary<string, object>();
var myType = data.GetType();
var myAssembly = myType.Assembly;
var props = myType.GetProperties();
foreach (var prop in props) {
// Remove EntityKey etc.
if (prop.Name.StartsWith("Entity")) {
continue;
}
if (prop.Name.EndsWith("Reference")) {
continue;
}
// Do not include lookups to linked tables
Type typeOfProp = prop.PropertyType;
if (typeOfProp.Name.StartsWith("EntityCollection")) {
continue;
}
// If the type is from my assembly == custom type
// include it, but flattened
if (typeOfProp.Assembly == myAssembly) {
if (currLevel < maxLevel) {
result.Add(prop.Name, JsonFlatten(prop.GetValue(data, null), maxLevel, currLevel + 1));
}
} else {
result.Add(prop.Name, prop.GetValue(data, null));
}
}
return result;
}
public static IDictionary<string, object> JsonFlatten(this Controller controller, object data, int maxLevel = 2) {
return JsonFlatten(data, maxLevel, 1);
}
My Action method looks like this:
public JsonResult AsJson(int id) {
var data = Find(id);
var result = this.JsonFlatten(data);
return Json(result, JsonRequestBehavior.AllowGet);
}
In the Entity Framework version 4, there is an option available: ObjectContextOptions.LazyLoadingEnabled
Setting it to false should avoid the 'circular reference' issue. However, you will have to explicitly load the navigation properties that you want to include.
see: http://msdn.microsoft.com/en-us/library/bb896272.aspx
Since, to my knowledge, you cannot serialize object references, but only copies you could try employing a bit of a dirty hack that goes something like this:
Customer should serialize its Machine reference as the machine's id
When you deserialize the json code you can then run a simple function on top of it that transforms those id's into proper references.
You need to decide which is the "root" object. Say the machine is the root, then the customer is a sub-object of machine. When you serialise machine, it will serialise the customer as a sub-object in the JSON, and when the customer is serialised, it will NOT serialise it's back-reference to the machine. When your code deserialises the machine, it will deserialise the machine's customer sub-object and reinstate the back-reference from the customer to the machine.
Most serialisation libraries provide some kind of hook to modify how deserialisation is performed for each class. You'd need to use that hook to modify deserialisation for the machine class to reinstate the backreference in the machine's customer. Exactly what that hook is depends on the JSON library you are using.
I've had the same problem this week as well, and could not use anonymous types because I needed to implement an interface asking for a List<MyType>. After making a diagram showing all relationships with navigability, I found out that MyType had a bidirectional relationship with MyObject which caused this circular reference, since they both saved each other.
After deciding that MyObject did not really need to know MyType, and thereby making it a unidirectional relationship this problem was solved.
What I have done is a bit radical, but I don't need the property, which makes the nasty circular-reference-causing error, so I have set it to null before serializing.
SessionTickets result = GetTicketsSession();
foreach(var r in result.Tickets)
{
r.TicketTypes = null; //those two were creating the problem
r.SelectedTicketType = null;
}
return Json(result);
If you really need your properties, you can create a viewmodel which does not hold circular references, but maybe keeps some Id of the important element, that you could use later for restoring the original value.

Resources