Why breeze sends to server originalvaluesMap for unmapped property? - breeze

I'm using breeze 1.4.11 with Web Api and EFContextProvider
on client after metadata fetched I extend entity type:
var addressStringProperty = new breeze.DataProperty({
name: "addressString",
isUnmapped: true
})
metadataStore.getEntityType('Account').addProperty(addressStringProperty);
this property is computed and used only on client
after entitymanager.saveChanges([accountEntity]) I see on server side in contextprovider.BeforeSaveEntity that entityInfo.OriginalvaluesMap contains Key "AddressString" with Value == null.
same thing with extending entity like:
var accountCtor = function() {
this.addressString = ko.observable()
};
metadataStore.registerEntityTypeCtor('Account', accountCtor);
How to omit this behaviour?

It's a good question and probably an oversight. I'll add it as a new bug, but... just out of curiousity, why is this problematic for you?

Related

Breezejs .net core 3 saving new entities issue

using BreezeJs for .net core 3.1
Issue with fixupKeys when saving new entity
throws "Unable to locate the following fully qualified EntityType name: "
Examining this: the _entityGroupMap entries use another fully qualified format than the keymappings object
e.g.
HoseColor:#Urflex.Webshop.Model (_entityGroupMap) <<==>> Urflex.Webshop.Model.HoseColor (keymappings)
How to resolve this?
problem solved. Overlooked some configuration in startup.cs file of the web api project.
As the breeze documentation states:
var mvcBuilder = services.AddMvc();
services.AddControllers().AddNewtonsoftJson(opt =>
{
// Set Breeze defaults for entity serialization
var ss = JsonSerializationFns.UpdateWithDefaults(opt.SerializerSettings);
if (ss.ContractResolver is DefaultContractResolver resolver)
{
resolver.NamingStrategy = null; // remove json camelCasing; names are converted on the client.
}
ss.Formatting = Newtonsoft.Json.Formatting.Indented; // format JSON for debugging
});
// Add Breeze exception filter to send errors back to the client
mvcBuilder.AddMvcOptions(o => { o.Filters.Add(new GlobalExceptionFilter()); });

MVC model bind from VUE object

I am new to VUE and trying to post a complex object with a list of tokens to an MVC c# route.
The network tab from my post request shows:
tokens[0][field]: 129
tokens[0][id]: 1
tokens[0][name]: MyPriority
tokens[0][operator]:
tokens[1][field]:
tokens[1][id]: 3
tokens[1][name]: -
tokens[1][operator]: -
The MVC controller shows the object, and it shows the number of tokens passed. However, the framework is not binding to the properties being passed in (field, id, name, operator). I am unsure if I need to amend the MVC part to bind, or change the JS object before posting.
the MVC controller is
public ActionResult CreateRule(Rule rule, List<Token> tokens)
the Js Code is:
methods: {
saveRule() {
let tokens = [];
debugger;
for (let i = 0; i < this.tokens.length; i++) {
var t = {};
t.field = this.tokens[i].field;
t.id = this.tokens[i].id;
t.name = this.tokens[i].name;
t.operator = this.tokens[i].operator;
tokens.push(t);
}
let newRule = {
id: this.rule.id,
type: this.rule.type,
name: this.rule.name,
field: this.rule.field,
tokens: tokens
};
this.$emit("save-rule", newRule);
},
It does not feel like a great way to have to copy all the parameters into a new object, so I assume this is not the best way. The vue tutorials had it cloning the data for posting to the server. In any case, it has not made any difference to the MVC reading the post data.
I have seen people try Stringify, but issues around datatypes the { ...rule } seemed to make no difference. I was hoping it was a quick answer as it must be possible, it is far from specialized or unique action!

Web API 2 Odata 4 Parameter Issue

I'm having problems defining a function for odata4. The default get would work but I want to require a user parameter so a client set can be determined, other tables are involved so LINQ is required, I also return a DTO instead of the default table info (EF). Below is the code. I get a "Invalid EntitySetPath detected. 'bindingParameter/Client' is not a valid entity set path for procedure 'Default.GetClients'." What am I doing wrong here?
WebApiConfig
public static void Register(HttpConfiguration config)
{
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<client>("Client").EntityType.HasKey(p => p.int_id);
var function = builder.Function("GetClients");
function.Parameter<string>("user");
function.ReturnsCollectionFromEntitySet<client>("Client");
builder.EntitySet<ClientDTO>("ClientDTO");
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: null,
model: builder.GetEdmModel());
WebApp.Controller
[ODataRoute("GetClients(user={user})")]
[EnableQuery(PageSize=25)]
public IQueryable<ClientDTO> GetClients([FromODataUri] string user)
{
var clients = (from c in db.clients
join ...
If your OData controller is returning the DTO, the function should look like this:
var function = builder.Function("GetClients");
function.Parameter<string>("user");
function.ReturnsCollectionFromEntitySet<ClientDTO>("Client");
With your current setup, your OData route of GetClients says that it is returning a ClientDTO object, but your WebApiConfig is stating you are returning a Client object.
As the Entity Collection being returned is actually the DTO. The part that shows ("Client") is simply how the OData service will report the name of the object to the project consuming the OData service. For my own personal sanity, I typically include DTO as well so I know when I'm using a DTO and when I'm using a direct entity. So in my own setup i'd return ("ClientDTO"), just a personal preference.

how to get an unsaved entity on server but not for saving?

i need to send my unsaved entity from the client to the server but not for saving changes
but inorder to do a process using the data on the entity and then change some of it's values and pass it back to the client
is this possible?
if not what are my options?
i tried to export the entity and then send it to a method on the webapi controller that gets a JObject but didn't find a way to deserialize it to the server entity
We did have a similar problem and found a solution as follows:
You need to take into consideration the way breeze manages it's objects.
1.Create custom saveBundle.
Consider complex order object.You need to fill your save bundle with each nested object inside order.
Like:
var saveBundle = new Array();
saveBundle.push(order.SaleAccountingInfo);
saveBundle.push(order.CostAccountingInfo);
saveBundle.push(order);
2.Create custom save options, where you can point to your custom Save Method on server
Like:
var so = new breeze.SaveOptions({ resourceName: "BookOrder" });
3.Call standard breeze function and pass it created params
manager.saveChanges(saveBundle, so).fail(function () {
// manager.rejectChanges();TODO check what needed
deferred.resolve(true);
});
On server you need to have you custom function ready and hook some breeze delegates
[HttpPost]
public SaveResult BookOrder(JObject orderBundle)
{
context.BeforeSaveEntityDelegate = OrderBeforeSaveEntity;
context.BeforeSaveEntitiesDelegate = SaveOrder;
context.AfterSaveEntitiesDelegate = BookOrderAfterSave;
try
{
return context.SaveChanges(orderBundle);
}
catch (Exception)
{
throw;
}
}
You can a lot of stuff in first two delegates but it is the last one you are looking for
private void BookOrderAfterSave(Dictionary<Type, List<EntityInfo>> orderSaveMap, List<KeyMapping> orderKeyMappings)
{
var orderEntity = orderSaveMap.Where(c => c.Key == typeof(BL.Orders.Order)).Select(d => d.Value).SingleOrDefault();
BL.Orders.Order order = (BL.Orders.Order)orderEntity[0].Entity; //your entity
//logic here
}
Hope it points to right direction.
we are doing something similar here. it'll save the entity so i'm not sure if this fits your question.
you can do:
entity.entityAspect.setModified()
then issue a saveChange()
then you can do your calculations on the server.
in our case we are using breeze.webapi so we are doing this in the beforeSave(entity) method.
breeze by design sends the changed entity then back to the client where the cache gets updated with your changes done on the server.

Custom DataService adapter saveChanges method to set entities to Unchanged

I've implemented a custom DataService adapter for BreezeJS - I wanted to use Breeze with a RESTful back end service (not OData or ASP.NET Web API).
So far - decent results after a learning curve.
I'm having an issue that when I call save changes - afterwards my entities on the client do not get marked as 'Unchanged'. They keep the same entityState.
I assume it has something to do with the success handler of the AJAX request to the backend service (looking at the source code to the WebAPI adapter):
success: function(data, textStatus, XHR) {
if (data.Error) {
// anticipatable errors on server - concurrency...
var err = createError(XHR);
err.message = data.Error;
deferred.reject(err);
} else {
// HACK: need to change the 'case' of properties in the saveResult
// but KeyMapping properties internally are still ucase. ugh...
var keyMappings = data.KeyMappings.map(function(km) {
var entityTypeName = MetadataStore.normalizeTypeName(km.EntityTypeName);
return { entityTypeName: entityTypeName, tempValue: km.TempValue, realValue: km.RealValue };
});
var saveResult = { entities: data.Entities, keyMappings: keyMappings, XHR: data.XHR };
deferred.resolve(saveResult);
}
},
It looks like the response includes an array of 'Entities'. What do these 'Entities' look like? It echoes what the client sent with an updated entityAspect.entityState value (server responses with 'Unchanged')?
Is that what should be passed into the deferred.resolve call?
I've got a working solution for this.
In a nutshell here's what is required for the object that is passed to the
deferred.resolve(saveResult);
Call in the success handler of the save change AJAX request.
Server response should include information about how to map from the client generated id to the server generated id (if the server generated one). This can be one keyMapping property returned in the response (like the Breeze API controller does) or what my service does is return a keyMapping property as a child property of a particular resource
The client code should create an array of objects that look like:
{ entityTypeName: "fully qualified entity type name",
tempValue: "client generated id",
realValue: "server generated id"
}
this array is the keyMappings property of the saveResult object
the entities property of the saveResult object is a flat list of all the entities that were modified from the server. Because of the design of my service API, it can return an entity, and child entities embedded in it, which I had to traverse and pull out into a flat list. Additionally these entity objects should be 'raw' and not include the entityAspect property or anything Breeze might interpret as a 'real' entity.
Also - something that can also be helpful is to look at the new sample from the Breeze folks - the MongoDB Breeze sample. They've implemented a custom dataServiceAdapter that hooks up their NodeJS/MongoDB backend. That provided some additional insight as well.
Good luck!

Resources