breeze: creating client-side entities with data coming from third-party service - breeze

I'm getting data from a third-party service and while I had no issues converting to breeze entities, I have one particular scenario that puzzles me:
the data structure I receive is this one (simplified for the sake of clarity)
{
TotalRecords: 72,
Contractors: [ { name: 'test} , {name: 'test2'}]
}
in my jsonResultAdpater, I have created an extractResults method, which returns data.results.Contractors.
And in my visitNode method, I can convert objects of the Contactors array to breeze entities.
But I've lost the TotalRecords property on the way.... This should be passed somehow to the controller that initiated the call to the third-party webservice.
How would I do that ?
adapter:
extractResults: function (data) {
var results = data.results;
return results && results.Contractors
},
visitNode: function (node, parseContext, nodeContext) {
if (node && node.Type === 'ContractorFrameworkDTO') {
return { entityType: "Freelancer" };
}
}

actually it was as simple as filling the inlineCount property in the extractResults method:
data.inlineCount = results.TotalRecords;

Related

breezejs how to using as datasource

i'm using breezejs for some time now, and i'm happy about it and it's rich functionality , but the problem with breezejs is that i cant use it as datasource almost for anything.
there is no grid that you could show data and not losing functionality, and you cant use your array of entities as normal array. (so you cant use it as datasource for dropdown ...)
so for showing data in UI i end up converting data to normal array and losing Breeze functionality (like track change) and before save converting them back.
some code like this one for converting to normal array:
if(data.length>0)
{
var names = Object.getOwnPropertyNames(data[0]._backingStore);
var columns: string[] = [];
for (var i = 0; i < names.length; i++)
{
columns.push(names[i]); //Getting columns name
}
var newArray = [];
data.forEach((item, index, array) => {
newArray.push(item._backingStore);
});
}
my question is how do you show your data in UI using breezejs properly?
(i'm using angualr (hottowel))
Assuming you're trying to solve issues like this:
The grid doesn't like the entityType and entityAspect properties on Breeze entities.
This grid doesn't know how to handle Knockout style "properties" that are functions.
Creating a POCO object using the Breeze entity's property values disconnects you from the change tracking goodness.
You could try creating your POCO object using Object.defineProperty, using the Knockout observable as the property's getter and setter functions. Here's a simple example:
Typescript + Knockout:
class PocoBreezeEntity {
constructor(private entity: breeze.Entity) {
entity.entityType.dataProperties.forEach(
dataProperty => {
Object.defineProperty(
this,
dataProperty.name,
{
get: entity[dataProperty.name],
set: entity[dataProperty.name],
enumerable: true,
configurable: true
});
});
}
}
Typescript + Angular:
class PocoBreezeEntity {
constructor(private entity: breeze.Entity) {
entity.entityType.dataProperties.forEach(
dataProperty => {
Object.defineProperty(
this,
dataProperty.name,
{
get: function() { return entity[dataProperty.name]; },
set: function(newValue) { entity[dataProperty.name] = newValue; },
enumerable: true,
configurable: true
});
});
}
}
With this kind of approach you have the benefits of POCO entities without losing the Breeze change tracking.

BreezeJS executeQueryLocally and return string

I can execute a query in breeze from the server (using EF) which returns a load of boostrap data thus:
em.executeQuery(_lookupsQuery).then(function (data) {
_lookups = data.results;
console.log(_lookups[0].currentUserId);
This returns currentUserId which is a guid. I then store em using local storage for querying locally later:
_lookups = [{
currentUserId: em.executeQueryLocally(_lookupsQuery.toType(breeze.DataType.String))
}];
However this does not work as it requires an entity type e.g:
em.executeQueryLocally(_lookupsQuery.toType(em.metadataStore.getEntityType("Measure")))
Since currentUserId is a guid I am not sure which type to cast the query to. I have tried to make an entity type on the client just for this but it does not seem to work. Any help on solving this would be appreciated.
Edit:
After a suggestion, I modified lookups:
[HttpGet]
public async Task<object> Lookups()
{
var currentUser = await UserManager.FindById(Guid.Parse(User.Identity.GetUserId()));
var companyId = currentUser.CompanyId.Value;
return new
{
currentUser = new
{
Id = currentUser.Id
}
};
}
When querying remotely using:
em.executeQuery(_lookupsQuery).then(function (data) {
_lookups = data.results;
console.log(_lookups[0].currentUser);
I get:
Object { id="f2dceb4b-29e7-4533-99e2-2052dc39143a"}
I set up the new entity type:
metadataStore.addEntityType({
shortName: "CurrentUser",
dataProperties: {
id: { dataType: "String", isPartOfKey: true }
}
});
but when I query locally:
_lookups = [{
currentUser: em.executeQueryLocally(_lookupsQuery.toType(em.metadataStore.getEntityType("CurrentUser"))) }];
console.log(_lookups[0].currentUser);
this returns []
What am I doing wrong?
There are a few ways you can handle it. I am only going to touch on the two most basic methods that I personally use and hopefully one sparks your interest.
-1. Create an entityType in your metadataStore for user-type information.
metadataStore.addEntityType({
shortName: "User",
dataProperties: {
userId: { dataType: "String", isPartOfKey: true },
userName: { dataType: "String" }
}
});
This will add an entity type that you can serialize your return results to. It is important to note here that if you change anything about that user without accepting changes locally it will try to save it next time you call saveChanges() so make sure you handle those situations if applicable.
Of course that isn't the only option. You can most certainly just grab that user id from the query results without Breeze ever knowing about it or what it is for.
-2. POJO
function user(data) {
var self = this;
self.UserId = data.userId;
self.UserName = data.userName;
}
// After your query returns data
query.execute().then(userReturned);
function userReturned(data) {
// data is the returned object, which contains
// an httpResponse which is what breeze returns
// as the raw response
new user(data.httpResponse.data);
}
This method basically just grabs the httpResponse when the promise returns and uses it to create a Plain Old JavaScript Object without Breeze knowing about it. Of course for this to work you need to examine your data object that is returned and find what you are looking for and serialize that.
Edit
Your query locally should look like this -
query = breeze.entityQuery().from("Whatever").toType("CurrentUser").executeLocally();
em.executeQueryLocally(query);

BreezeJS - Using expand

I am querying the server to get an entity with expand
function _loadIncidents() {
var deffered = Q.defer(),
queryObj = new breeze.EntityQuery().from('Incidents').expand(['Deployments', 'IncidentComments', 'DTasks', 'ExtendedProperties', 'IncidentEvents']);
dataRepository.fetchEntitiesByQuery(queryObj, true).then(function (incidents) {
var query = breeze.EntityQuery.from("DTasks"),
incidentIds = dataRepository.getEntitiesByQuerySync(query);
deffered.resolve();
}, function(err) {
deffered.reject(err);
});
return deffered.promise;
};
I am getting the results and all is fine, how ever when I query breeze cache to get the entities - I am getting empty collection. So when using expand does the expanded entities are added to the cache?
Yes the related entities identified in the expand should be in cache ... if the query is "correct" and the server interpreted your request as you intended.
Look at the payload of the response from the first request. Are the related entities present? If not, perhaps the query was not well received on the server. As a general rule, you want to make sure the data are coming over the wire before wondering whether Breeze is doing the right thing with those data.
I do find myself wondering about the spelling of the items in your expand list. They are all in PascalCase. Are they these the names of navigation properties of the Incident type? Or are they the names of the related EntityTypes? They need to be former (nav property names), not the latter.
I Had problem with the navigation property - as I am not using OData webapi not using EF , there is problem with the navigation properties so for the current time i just wrote
Object.defineProperty(this, 'Deployments', {
get: function () {
return (this.entityAspect && this.entityAspect.entityManager) ?
this.entityAspect.entityManager.executeQueryLocally(new breeze.EntityQuery("Deployments").
where('IncidentID', 'eq', this.IncidentID)) :
[];
},
set: function (value) { //used only when loading incidents from the server
if (!value.results) {
return;
}
var i = 0,
dataRepository = require('sharedServices/dataRepository');
for (i; i < value.results.length; i++) {
dataRepository.addUnchangedEntity('Deployment', value.results[i]);
}
},
enumerable: true
});

Avoiding null or empty values to save in database using Entity Framework

I have an application where we are using entity framework and on button click an ajax call is made to save values.
Everything is working fine, but even if we try to click the save button, with no values, it gets saved in database as null or empty(takes space).
There is some minor error which I am overlooking, please help me out.
What I tried:
$.ajax({
type: "POST",
url: '~/SaveGroups',
data: {
GroupId: (GroupId == null ? -1 : GroupId),
counterId: (counterId == null ? -1 : counterId),
GroupCode: GroupCode,
GroupDesc: GroupDescription
},
success: function (result) {
if (result != null) {
$("#PartialGroupsTable").html(result.GroupsPageString);
$("#GCode").val('') //GCode is the name of the textbox
$("#GDescription").val(''); //GDescription is the other textbox
GroupId = -1;
}
},
complete: function () {
$("#GCode").empty();
$("#GDescription").empty();
},
error: function () {
alert("Details Not Saved. Please try again later");
}
});
Thanx in advance.
You have several options to fulfill your requirements, two of them are Client Side validation and server side validations that means you need to validate the data before send it to database for saving.. like
you have model classes right.. that represent your tables
public class User
{
[Required]
public string UserName{get; set;}
...
...
}
suppose if you try to send null values to controllers or dbcontext it wont allows you to insert null values into it since it allows only non-null values.
and also you can do this as well using jquery
function buttonClick()
{
if($('#txtName').val()=='')
{
alert('Please enter name');
return false;
}
else
{
//make ajax call here
}
}
like this you can do it in many ways you can use unobtrusive javascript as well. Hope this helps
If you expect to get -1 for GroupId or counterId and instead get something else, the reason is probably that the existing value is not null but perhaps an empty string. Check for that as well as checking for null/undefined. If that's not the problem then additional information may be needed.
For example:
GroupId: ((GroupId == null || GroupId == '') ? -1 : GroupId),

Breeze querying local cache with EF and Web API

Problem
I have a view with 6 drop downs. Each of which is being populated by a Web API call. I want
to use breeze to run the query locally once it has populated from the remote server
The code runs fine when the data call is against the server. The issue is when trying to query the local cache. I never get any results returned. Is my approach flawed or am I doing something wrong ?
SERVER SIDE
View model
class genericDropDown()
{
public int value{get;set;}
public string option{get;set;}
}
The WebAPI [A single sample method]
[HttpGet]
// GET api/<controller>
public object GetSomeVals()
{
return _context.getClinician();
}
The Repository [A single sample method]
public IEnumerable<genericDropDown> getDropDownVal()
{
return context.somemodel(a=>new{a.id,a.firstname,a.lastname}).ToList().
Select(x => new GenericDropDown
{ value = x.id, option = x.firstname+ " " + x.lastname});}
}
CLIENT SIDE
Datacontext.js
var _manager = new breeze.EntityManager("EndPoint");
//Being called from my view model
var getDropDownBindings = function(KO1, KO2) {
//First add the entity to the local metadatastore then populate the entity
$.when(
addDD('clinicianDropDown', webAPIMethod),
getData(KO1, webAPIMethod, null, 'clinicianDropDown'),
addDD('docTypeDropDown', webAPIMethod);
getData(KO2, webAPIMethod, null, 'docTypeDropDown'),
).then(querySucceeded).fail(queryFailed);
function querySucceeded(data) {
logger.log('Got drop down vals', "", 'dataContext', true);
}
};
//Add the entity to local store. First param is typename and second is
resource name (Web API method)
var addDD = function(shortName,resName) {
_manager.metadataStore.addEntityType({
shortName: shortName,
namespace: "Namespace",
autoGeneratedKeyType: breeze.AutoGeneratedKeyType.Identity,
defaultResourceName:resName,
dataProperties: {
value: { dataType: DataType.Int32,
isNullable: false, isPartOfKey: true },
option: { dataType: DataType.String, isNullable: false }
}
});
return _manager.metadataStore.registerEntityTypeCtor(shortName, null, null);
};
//Get the data
var getData = function(observableArray, dataEndPoint, parameters, mapto) {
if (observableArray != null)
observableArray([]);
//TO DO: Incorporate logic for server or local call depending on
// whether this method is accessed for the first time
var query = breeze.EntityQuery.from(dataEndPoint);
if (mapto != null && mapto != "")
query = query.toType(mapto);
if (parameters != null)
query = query.withParameters(parameters);
//This approach doesnt work on local querying as Jquery complains
//there is no 'then' method. Not sure how to implement promises
//when querying locally
/* return _manager.executeQuery(query).then(querySucceeded).fail(queryFailed);
function querySucceeded(data) {
if (observableArray != null)
observableArray(data.results);
}
*/
//The array length from this query is always 0
var data = _manager.executeQueryLocally(query);
observableArray(data.results);
return;
};
//Generic error handler
function queryFailed(error) {
logger.log(error.message, null, 'dataContext', true);
}
viewmodel.js
//In Durandal's activate method populate the observable arrays
dataContext.getDropDownBindings (KO1,KO2);
Viewmodel.html
<select class="dropdown" data-bind="options: KO1, optionsText: 'option', value: 'value', optionsCaption: 'Clinicians'"></select>
<select class="dropdown" data-bind="options: KO2 optionsText: 'option', value: 'value', optionsCaption: 'Document Types'"></select>
You can only execute local queries against types that are described by metadata.
Without more information I can't be sure, but my guess is that your GetSomeVals method is not returning 'entities' but just loose data. In other words, the types of objects returned from the GetSomeVals method must be entities (or contain entities within a projection) in order for breeze to be able to perform a local query. This is because Breeze knows how to cache and query entities but has no ideas how to cache 'arbitrary' query results.
Note that you can return an anonymous type containing entities of different types from the server, (in order to populate mostly static small datasets), but the individual items must be 'entities'. In this case, Breeze will take apart the anon result and pick out any entities to include in the EntityManager cache.
Per you question of how to perform an local query with promises, use the FetchStrategy.FromLocalCache with the using method.
i.e. this query
var results = em.executeQueryLocally(query)
can also be expressed as:
query = query.using(FetchStrategy.FromLocalCache);
return em.executeQuery(query).then(data) {
var results = data.results;
}
The local query is still executed synchonously but is made to look async.

Resources