breezejs v1.3.6 breaks my application - javascript

I've updated my client library and server web api dll to latest version.
Now whenever I do an expand on a query, I get that kind of error:
unable to locate property: Mandate on type: MandateHistory:#Dom.DirectDebit
with the query being :
var query = breeze.EntityQuery.from("MandatesHistory")
.where("Mandate.Id", "==", mandatId).expand("Mandate");
return manager.executeQuery(query.using(service));
If I downgrade to 1.3.3 (the client library only), everything works fine.
I would have like to try 1.3.4 or 1.3.5 but I can't find them on the website....
What has changed between 1.3.3 and 1.3.6 that could break my application ?
EDIT
THIS IS THE CODE CAUSING ISSUES :
In 1.3.6, in the function parseCsdNavProperty, the following code was added:
var constraint = association.referentialConstraint;
if (!constraint) {
// TODO: Revisit this later - right now we just ignore many-many and assocs with missing constraints.
return;
// Think about adding this back later.
//if (association.end[0].multiplicity == "*" && association.end[1].multiplicity == "*") {
// // many to many relation
// ???
//} else {
// throw new Error("Foreign Key Associations must be turned on for this model");
//}
}
Basically, for the navigation property MandateHistory.Mandate, there is no contraint found, so the code just return. This is the cause of my issue.
In version 1.3.3, there was no check on constraint because first there was the following check which returns false in my case (isScalar is false):
if (toEnd && isScalar) {
var constraint = association.referentialConstraint;
if (constraint) {
var principal = constraint.principal;
var dependent = constraint.dependent;
var propRefs;
if (csdlProperty.fromRole === principal.role) {
propRefs = toArray(principal.propertyRef);
} else {
propRefs = toArray(dependent.propertyRef);
}
// will be used later by np._update
fkNamesOnServer = propRefs.map(__pluck("name"));
}
}
Can the breeze team look into this ?
SOLUTION
Following Jay's suggestion, the .net model had to be changed in order to explicitly set the foreign key association between MandateHistory and Mandate:
public class MandateHistory
{
[ForeignKey("Mandate")]
public int Mandate_Id { get; set; }
public virtual Mandate Mandate { get; set; }
}

My guess is that you are missing referential constraints in your model. i.e. the Entity Framework thinks that you are not exposing foreign keys. See Foreign keys in the Entity Framework.
Breeze requires the foreign keys in order to perform it's automatic object linking logic.
This is also described here: Breeze navigation properties

Related

Filtering and only display certain information using Web API

I am developing a website that is mostly written in asp.net and Javascript and I am using a lot of ajax on it to retreive and display information from a SQL database.
To perform all these operations I am using Web API as the communication path to perform server related tasks.
There are quite a few places on my website I will only want to display certain information. For example, I have the following route of which I may consume: api/customers/orders/order/5
This would retreive data for Order #5. However, what if some places on my website only need to display the Order # or Order Description or something? and what happens if I want to filter the database a bit more, to perhaps only display orders with specific information. By using the above URL it would return everything about the order to the web browser and seems a bit unnecessary.
If I have several needs for different filters then I don't understand how the routing would work for as some bits would be optional.
Any recommendations would be greatly appreciated! Or perhaps i'm missing something!
Thanks
You can do something like following. You can create request filter like specified in below link.
public class RequestFilterAttribute : Attribute, IHasRequestFilter
{
#region IHasRequestFilter Members
public IHasRequestFilter Copy()
{
return this;
}
public int Priority
{
get { return -100; }
}
public void RequestFilter(IHttpRequest req, IHttpResponse res, object requestDto)
{
var query = req.QueryString["q"] ?? req.QueryString["query"];
var limit = req.QueryString["limit"];
var offset = req.QueryString["offset"];
var user = requestDto as QueryBase;
if (user == null) { return; }
user.Query = query;
user.Limit = limit.IsEmpty() ? int.MaxValue : int.Parse(limit);
user.Offset = offset.IsEmpty() ? 0 : int.Parse(offset);
}
#endregion
}
.Net WebAPI URI convention for advanced searching /filtering

Loading Breeze Navigation Properties offline

I am using the latest versions of Angular, breeze, EF.
I am constructing a complex object on a client called a Quote which is added to a job. This has a QuoteMeasure added to it. One of the properties of QuoteMeasure is a navigation property called measure:
var quote = em.createEntity("Quote", { id: breeze.core.getUuid() }),
quoteMeasure,
measure;
measure = _getMeasureFromLookups(4);
quoteMeasure = em.createEntity("QuoteMeasure", { id: breeze.core.getUuid(), quoteId: quote.id });
I have tried the following which executes a query to the server
quoteMeasure.measureId = measure.id;
quoteMeasure.entityAspect.loadNavigationProperty("measure").then(function () {
console.log(quoteMeasure.measure);
});
quote.quoteMeasures.push(quoteMeasure);
job.quotes.push(quote);
to url /Breeze/Data/Measure?$filter=Id%20eq%204&
which does not exist. I would ideally like to set the navigation property manually as it is static data and previously obtained from a breeze query lookups on the server:
[HttpGet]
public object Lookups()
{
var measures = UnitOfWork.MeasureRepository.Get(null, q => q.OrderBy(m => m.Ordinal)).ToList();
return new { measures = measures };
}
This is what the function _getMeasureFromLookups does, it looks up the previously stored measure. I would like to do assign it this way:
quoteMeasure.measure = measure;
But I get the following meaningless error on the client:
Error: A is undefined M#//llhst/X/Scripts/breeze.min.js:1 d/f.set#//llhst/X/Scripts/breeze.min.js:5 _createNewQuote#//llhst/X/Scripts/app/services/jobService.js:76
This I assume is because a full tree of objects has been downloaded via the lookup rather than an individual measure entity. In http://www.breezejs.com/documentation/navigation-properties there is a section on 'Omitting navigation properties' but then it neglects to tell you how to do this.
So my question is what is best practise for loading navigation property data offline? How can I modify the sample above so that it works?
If I understand your requirement correctly, you should be able to construct your quote and quoteMeasure entities as follows:
var quote = em.createEntity("Quote", { id: breeze.core.getUuid() });
//the assignment quoteId: quote.id is the same as quote.quoteMeasures.push(quoteMeasure)
//you don't need to add it again to the collection
var quoteMeasure = em.createEntity("QuoteMeasure", { id: breeze.core.getUuid(), quoteId: quote.id });
var measure = _getMeasureFromLookups(4);
quoteMeasure.measure = measure;
//or
//quoteMeasure.measureId = measure.id
//your _getMeasureFromLookups should look something like this
function _getMeasureFromLookups(measureId) {
//getEntityByKey will look up Measure from client cache
return em.getEntityByKey('Measure', measureId);
}
Calling loadNavigationProperty will initiate a query to the server.
The 'Omitting navigation properties' section actually tells you how you can omit the principal side of the association. So for example, to apply it to your EF model, if you don't want a Quote to be able to navigate to all QuoteMeasures, you can do the following:
//EF Model on Server
public class Quote {
//Simply remove or comment this collection navigation property
//public virtual ICollection<QuoteMeasure> QuoteMeasures { get; set; }
}
Hope this helps.
Seems the problem was the ommission of these statements:
Configuration.ProxyCreationEnabled = false;
Configuration.LazyLoadingEnabled = false;
Not having this caused the preloading of not only the navigation properties but all of theirs as well which caused the obscure error I noted above. No other modifications were required to get the code working.
EntityAspect.loadNavigationProperty() always makes a request to the server. If you want properties be loaded without making a separate request, do Eager loading with EF.
If you have several properties which are null when breeze fetches them, and you don't want to make several loadNavigationProperty calls, use EntityQuery.expand() method. You can list any properties you need to be loaded

Using DataAnnotations/IValidatable object on a model passed to server via AJAX call

Warning: Long Question! There's a lot of setup to get to what I need to ask.
So, I'm working on a small experiment project, because I'm trying to figure out if I can trigger server-side MVC validation on a model that was sent to the server via AJAX. The experiment app takes a note name, sign, and octave, to determine the frequency of the note.
My view is pretty standard:
<div id='frequency-line-container'>
// By default, nothing...
</div>
<div id='new-line-interface'>
<p>
What note is sounding? <input type='text' id='note-name' maxlength='1' />
</p>
<p>
What sign is by the note?
<span>
#Html.RadioButton("note-sign", "natural", true, new {id="note-natural"})Natural
#Html.RadioButton("note-sign", "sharp", false, new {id="note-sharp"})Sharp
#Html.RadioButton("note-sign", "flat", false, new {id="note-flat"})Flat
</span>
<p>
<p>
What octave is the note sounding at? <input type='number' id='octave' maxlength='1' min='0' max='9' />
</p>
<a href='#' id='save-button'>Save</a>
</div>
On the client, I have the following JavaScript set up:
$(document).ready(function () {
var $frequencyLineContainer = $('#frequency-line-container'),
$saveLineButton = $('#save-line'),
$noteName = $('#note-name'),
$octave = $('#octave');
$saveLineButton.click(function (e) {
e.preventDefault();
var data = getLineData();
var $promise = getNewLinePromise(data, true);
$.when($promise)
.then(addLine);
});
function getLineData() {
var $noteSign = $('#note-sign-section input[type="radio"][name="note-sign"]:checked');
var model = {
'NoteName': $noteName.val(),
'NoteSign': $noteSign.val(),
'Octave': parseInt($octave.val())
};
return {
'model': model
};
}
function getNewLinePromise(data, async) {
return $.ajax({
type: 'POST',
url: '/Home/AddFrequencyLine/',
contentType: 'application/json',
data: data,
async: async
});
}
function addLine(result) {
$frequencyLineContainer.append(result);
}
});
...and on the server, my action is pretty simple:
[HttpPost]
public void AddFrequencyLine(FrequencyLineViewModel model)
{
model.CalculateFrequency(); // Determines the frequency of a note by sign, name, and octave...
return Partial("_FrequencyLine", model);
}
So far par for the course. Now, things get interesting.
As you can see, while our note name field is limited to only a single character, they can enter an invalid note name; musical notes range from A to G. If a user enters anything from H-Z, that isn't a valid musical note, and thus should not generate a frequency! In fact, a red error message should appear next to the note name field.
My FrequencyLineViewModel looks like this...
public enum NoteSign
{
Natural,
Sharp,
Flat
}
public class FrequencyLineViewModel : IValidatableObject
{
[Required]
public string NoteName { get; set; }
[Required]
public NoteSign NoteSign { get; set; }
[Required]
public int Octave { get; set; }
public Dictionary<string, float> FrequencyLookup = new Dictionary<string, float>{
// Each note from A0 to G#0 has an associated frequency. This lists 'em.
// A full listing of the frequency table is not salient to this question.
}
public IEnumerable<ValidationResult> Validate(ValidationContext context)
{
List<ValidationResult> results = new List<ValidationResult>();
byte asciiCode = (byte) (NoteName.ToUpper())[0];
if(asciiCode >= 72 // Between H and Z
&& asciiCode <= 90)
{
results.Add(new ValidationResult("(Invalid)", new []{"NoteName"}));
}
// Could add other validations here
return results;
}
}
So far in my tests, I have modified my action:
public void AddFrequencyLine(FrequencyLineViewModel model)
{
model.IsValid = ModelState.IsValid;
if (ModelState.IsValid)
{
model.CalculateFrequency();
return PartialView("_NoteRecord", model);
}
// Need to change this...but to what?
model.ErrorMessage = "Something is wrong!";
return model;
}
...But I'm lost on how best to implement the fruits of a validation. I set a breakpoint in my modified action on the model.IsValid check, and found that, in invalid cases such as an empty form, or a note from H-Z, that it is properly flagging as invalid; conversely, when entering a valid form, it works as intended. But, I'm not interested in the 'works right' part; the invalid state has me stymied.
I could simply return the error message as I'm doing and use JavaScript in the success handler to figure out where to put it, if the IsValid property of the result is false (only happens on the invalid code path.) The problem is, this defeats the purpose of the success handler; what's really happening is an error state.
So, at long last, to my Question: I'm trying to show some Validation Messages, but this setup dosen't lend itself to doing that. What can I do to make this AJAX-based model validation present validation messages, without using my JavaScript success handler?
EDIT: The reason this is a question, is because our team wants to keep as much of our business logic on the server as possible; we're striving for a 'thin-client' setup. This means that the JavaScript will be only responsible 1) for GUI state, 2) for funneling data back to the server for processing, and 3) for receiving data from the server for presentation.
I don't have a precise answer for you but I wanted to ask: since your app is reliant on javascript already, why don't you validate the input client-side before making the ajax call? Best practices would be to validate both client-side before ajax and server-side just in case.
Perhaps you need to create a separate validation class that contains all of your rules (logic) and associated error messages which you could then reference in your model there.
Food for thought, sorry for a lack of real answer!
edit: I think I'm way off as far as addressing the actual question. You could consider always returning an object with an error property which is null when the input is valid but contains a relevant error message when the input is invalid. Then in your success handler you could just look for error != null and grab the error data there?

Error using dynamic keyword in asp.net mvc 4

I am getting this long error when i accpet the parameter as dynamic on my server side action method in mvc 4.
{"Message":"An error has
occurred.","ExceptionMessage":"'Newtonsoft.Json.Linq.JObject' does not
contain a definition for
'TournamentId'","ExceptionType":"Microsoft.CSharp.RuntimeBinder.RuntimeBinderException","StackTrace":"
at CallSite.Target(Closure , CallSite , Object )\r\n at
System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite
site, T0 arg0)\r\n at
ManagerDeTorneos.Web.Controllers.TournamentDateController.Create(Object
data) in
F:\Prince\Projects\Juan\trunk\ManagerDeTorneos.Web\Controllers\TournamentDateController.cs:line
133\r\n at lambda_method(Closure , Object , Object[] )\r\n at
System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c_DisplayClass13.b_c(Object
instance, Object[] methodParameters)\r\n at
System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object
instance, Object[] arguments)\r\n at
System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1
func, CancellationToken cancellationToken)"}
[HttpPost]
public HttpResponseMessage AddMatch(dynamic data)
{
int tournamentDateId = (int)data.TournamentDateId.Value;
var tournamentDate = Catalog.TournamentDateRepository.GetById(tournamentDateId);
if (tournamentDate == null)
{
throw ExceptionHelper.NotFound("Fecha no encontrada!");
}
In The above method data Contains tournamentId as sent from ajax call as JSON.Stringify({'TournamentId':'5'}).
Can anybody tell me what is the cause of error. I even replaced the dll of Newtonsoft.Json as well
You are right dan but i fixed my issue by removing that dll from GAC. May be in GAC it was using old assembly
The error is caused by the fact that you typed your parameter as dynamic, which means that the model binder doesn't know what to make it. It's the same as if you were to declare it as an object. Since you are providing JSON, it serializes the object as a Json.Net JObject. Just because you define it as a dynamic doesn't mean that it's going to magically take whatever shape you need it to.
Change it to a concrete type - something that matches the structure of the provided JSON:
public class TournamentInfo
{
public int TournamentId { get; set; }
}
[HttpPost]
public HttpResponseMessage AddMatch(TournamentInfo data)
{
int tournamentDateId = data.TournamentId;
var tournamentDate = Catalog.TournamentDateRepository.GetById(tournamentDateId);
if (tournamentDate == null)
{
throw ExceptionHelper.NotFound("Fecha no encontrada!");
}
This way, the binder knows what it's supposed to turn the JSON into, and since TournamentInfo matches the structure of the JSON, it won't have any trouble serializing it.
Don't misuse dynamic. It was not introduced into C# so developers could stop defining classes.

Assign new values in a Breeze entity

I'm developing a spa web application with BreezeJS and the DurandalJS Framework. I came accross a problem which I can't fix.
I have a entity called: Car, this entity contains name, number, owner, type en manufacturer. In this entity the name and number are filled in as the entity is created AND saved in the database. The other properties are allowed to be NULL.
This because the other values are filled in during a modal/ dialog screen. Here a user can select a owner from a list and also a type and manufacturer from a list. When the user selects one from a dropdown the selected value should be assigned to the value of the Car entity. How can I get this to work?
Car().Owner = newOwner;
Car.Owner() = newOwner;
This won't work. I tried a lot of combinations. Remember that the value was null first and that I can't insert a new value;S
Edit 1
Here the Entity Framework model of Car
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Estimate_App.Models
{
public class tblCar
{
[Key]
public int CarID { get; set; }
public string CarNumber { get; set; }
private DateTime _CreationDate;
public DateTime CreationDate
{
get { return this._CreationDate; }
set { this._CreationDate = DateTime.Now; }
}
//This is the Owner
[ForeignKey("Owner")]
public int? OwnerID { get; set; }
public tblOwner Owner { get; set; }
}
}
Here is what I put in my Car().Owner(), consider Container should be Car (this is an other project with the same problem)
I hover my mouse over newValue.
Edit 2
By a answer of Julián Yuste I also tried this but it didn't work. Here is the error:
When I do Container().Owner(newValue);
Edit 3
The code that fetches the owners
breeze.EntityQuery.from("Customers")
.using(dataservice.manager)
.execute().then(function (data) {
data.results.forEach(function (item) {
tempCustomerList.push(item); //ko.observableArray([]);
});
}).fail(function (data) {
});
Are you using the EntityManager from your dataservice object in order to create the newOwner object?
In other words, you probably shouldn't be doing this*:
var newOwner = new Owner();
newOwner.OwnerID = 123;
You should do this:
var newOwner = dataservice.manager.createEntity('Owner', { OwnerID: 123 });
*Note that can actually use new Owner(), but that requires you to define entity constructors in your code. :-)
For more information, check out the Breeze documentation: http://www.breezejs.com/documentation/creating-entities
Also, note that you can read the Breeze JavaScript code to help you understand the issue. If you search breeze.debug.js for your error message (An Entity cannot be attached to an entity in another EntityManager. One of the two entities must be detached first.), you will find the line of code that is causing the exception. It may be helpful to backtrack from there.
Edit
The answer to the question was to make sure that the EntityManager object is a Singleton (in this case, the dataservices.manager object).
In other words, use the same EntityManager object to update objects as you use to query for objects.
I think we need more information. Is the 'Owner' property an instance of another entity or is it a primitive type, i.e. string, number etc?
If it is an entity then I would first check that your 'newOwner' variable was also in fact an entity.
If owner is an observable, you need to asign the new value as: owner(newOwner).
Greetings.

Categories

Resources