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

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.

Related

SAP UI5 Modifying an Odata model obtained from manifest before setModel()

I am creating my first SAP UI5 app and I have a need where i want to set an additional property to the data returned from an Odata service before i set it as a model to in the table view
However i am unable to access the data as the getProperty returns an empty object. Below is the code on the onInit() method of my controller . I am obtaining the model from my manifest.json as it looks a clean way to do things.
var rList = this.getOwnerComponent().getModel("Entities");
var localPromise = this.getPromise(rList, "/");
localPromise.done(function() {
console.log(rList.getProperty("/"));
// here i would like to do the manipulations and then set the view
}.bind(this));
},
i assume the getPromise method must take care of the asynchronous execution part:
getPromise: function(oModel, pathToTestForData) {
var deferred = $.Deferred();
if (oModel.getProperty(pathToTestForData))
deferred.resolve(); //Data already loaded
else
oModel.attachRequestCompleted(deferred.resolve); //Waiting for the event
return deferred.promise();
}
What i want to do is the following:
1) make the Odata two way bind.
2) set another property : anotherProperty : false on each returned Odata record
However as rList.getProperty("/") inside the done of the promise is empty, i am at a roadblock, Also how to access the data (if it was there)

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!

How can I return IQueyrable DTO from Webapi Get so I can use Odata filters

I'm trying to use a Odata filters with ODP.net with Entity framework inside of web api project ASP.NET MVC 4.0 RC. I want to return an IQueryable of OwnDTO . I get an internal 500 error without any details. I know there is an error generation bug with webapi RC, but I dont think that bug is my issue.
Get http://localhost:51744/api/Owner called using Fiddler
[Queryable]
public IQueryable<OwnDTO> Get()
{
using (Entities context = new Entities())
{
var query = from item in context.Owners
select
new OwnDTO
{
Name = item.Name
};
return query.AsQueryable();
}
}
//very simple for example
public class OwnDTO
{
public string Name;
}
I do not want to have use my Oracle EF generated classes (DAO) to return from my Get, but I know I can if I replace EntityObject with a more friendly interface. If I return IEnumerable it works, but I want Odata filters.
Update incase someone wants a working example.. Automapper or simliar should be used in the linq and the context should injected.
[Queryable]
public IQueryable<OwnDTO> Get()
{
{
var query = from item in Hack._EFContext.Owners
select
new OwnDTO
{
Name = item.Name
};
return query.AsQueryable();
}
}
That works fine, but it looks like Odata is removed post RC. So I need to search down another path.
It does work in RC but perhaps not in RTM when it ships - not quite clear yet.
Your problem is that you are disposing your context since you are using a using block. So context get disposed before the data is retrieved.
So instead of using register your object for disposal at the end of request. Tugberk has a post here.

How to stop WCF data Service to fetch data when SavingChanges (during update)

I am trying to update an entity from a WCF client as follows:
Ctxt.MergeOption = MergeOption.NoTracking;
var q = Ctxt.Customers.Where(p => p.MasterCustomerId == "JEFFERSON").Select(o => o);
//DataServiceCollection<Customer> oCustomers = new DataServiceCollection<Customer>(q, TrackingMode.None);
DataServiceCollection<Customer> oCustomers = new DataServiceCollection<Customer>(q);
oCustomers[0].FirstName = "KEFFERSON";
//Ctxt.SaveChanges(SaveChangesOptions.ReplaceOnUpdate);
//ctxt.SaveChangesDefaultOptions = SaveChangesOptions.ReplaceOnUpdate;
Ctxt.SaveChanges();
When I try to save the modified entity, it first tries to load that entity using a select query (to database) and then issues update statement to database.
In my case, I simply want to have the entity to be directly updated in the database without fetching it first. I don't mind if it overwrites the data in database
I tried the following at WCF service:
protected override EF.Model.DataModel.PersonifyEntities CreateDataSource()
{
var ctxt = new EF.Model.DataModel.PersonifyEntities();
ctxt.Customers.MergeOption = System.Data.Objects.MergeOption.NoTracking;
ctxt.ContextOptions.ProxyCreationEnabled = false;
ctxt.ContextOptions.LazyLoadingEnabled = false;
return ctxt;
}
But, no luck. Can anyone help me on this?
For WCF DataServices, the client can only update entities that it tracks. So it has to have the entity downloaded in the client before it can make any changes and save it back. Thats why you see the fetch (I am assuming that this is the first fetch that you are seeing for that specific entity) before the update. Hope this helps.

Is it possible to copy/clone HttpContext of a web request

What's the easiest way to clone current request's HttpContext instance?
I'm developing an app in Asp.net MVC v1. I upgraded the regular PartialView capabilities to actually have sub-controllers that act very similar, but have their own context. When you use PartialViews you have to fill view data for the partial view in your main view's controller action. I created my own functionality that makes it possible to call controller actions from within a view. This way I get:
I don't have to provide sub-view's data in my main view's controller action
sub controller methods can manipulate data more encapsulated without any relation to other views/controllers
The problem is that each sub-controller request uses HttpContext. So when I set some HttpContext.Item in a sub-controller it actually populates HttpContext of the actual request.
That's why I want to clone HttpContext. I'm already using:
HttpContext subContext = new HttpContext(request, response);
// what happened to Session, User, Items etc. properties?
but this doesn't set anything else than request and response. But I would probably also need other properties and collections... Like Session, Items, User... etc.
While the "Not Possible" answer is correct, there is an alternative that is much cleaner than writing values into the current context and then rewriting back to its original state. The solution is to make a new HttpContext object entirely that is based on the URL of your choosing.
// A new request/response is constructed to using a new URL.
// The new response is using a StreamWriter with null stream as a backing stream
// which doesn't consume resources
using (var nullWriter = new StreamWriter(Stream.Null))
{
var newRequestUri = new Uri("http://www.somewhere.com/some-resource/");
var newRequest = new HttpRequest("", newRequestUri.ToString(), newRequestUri.Query);
var newResponse = new HttpResponse(nullWriter);
var newContext = new HttpContextWrapper(new HttpContext(newRequest, newResponse));
// Work with the new context here before it is disposed...
}
Reference: https://github.com/maartenba/MvcSiteMapProvider/issues/278#issuecomment-34905271
Not possible
I guess an actual deep cloning is not possible because of server session state. Cloning would also have to clone this value, which is web server specific internal resource that is intrinsically static and can not be cloned. In this case a web server would have multiple Session objects for instance.
Workaround
Anyway. The workaround was to set additional context values before instantiating sub-controller processing. After processing is finished I reverted values back to original. So I actually had context as it was before.
For ASP.Net Core/.Net 5 the following will work (based on the ASP.Net Core source code for SignalR, if you need more features just add them).
public static HttpContext Clone(this HttpContext httpContext, bool copyBody)
{
var existingRequestFeature = httpContext.Features.Get<IHttpRequestFeature>();
var requestHeaders = new Dictionary<string, StringValues>(existingRequestFeature.Headers.Count, StringComparer.OrdinalIgnoreCase);
foreach (var header in existingRequestFeature.Headers)
{
requestHeaders[header.Key] = header.Value;
}
var requestFeature = new HttpRequestFeature
{
Protocol = existingRequestFeature.Protocol,
Method = existingRequestFeature.Method,
Scheme = existingRequestFeature.Scheme,
Path = existingRequestFeature.Path,
PathBase = existingRequestFeature.PathBase,
QueryString = existingRequestFeature.QueryString,
RawTarget = existingRequestFeature.RawTarget,
Headers = new HeaderDictionary(requestHeaders),
};
if(copyBody)
{
// We need to buffer first, otherwise the body won't be copied
// Won't work if the body stream was accessed already without calling EnableBuffering() first or without leaveOpen
httpContext.Request.EnableBuffering();
httpContext.Request.Body.Seek(0, SeekOrigin.Begin);
requestFeature.Body = existingRequestFeature.Body;
}
var features = new FeatureCollection();
features.Set<IHttpRequestFeature>(requestFeature);
// Unless we need the response we can ignore it...
features.Set<IHttpResponseFeature>(new HttpResponseFeature());
features.Set<IHttpResponseBodyFeature>(new StreamResponseBodyFeature(Stream.Null));
var newContext = new DefaultHttpContext(features);
if (copyBody)
{
// Rewind for any future use...
httpContext.Request.Body.Seek(0, SeekOrigin.Begin);
}
// Can happen if the body was not copied
if(httpContext.Request.HasFormContentType && httpContext.Request.Form.Count != newContext.Request.Form.Count)
{
newContext.Request.Form = new Microsoft.AspNetCore.Http.FormCollection(httpContext.Request.Form.ToDictionary(f => f.Key, f => f.Value));
}
return newContext;
}
The ASP.NET MVC framework intentionally makes dependencies to abstract classes with all members virtual. That simply says - extensibility.
Controllers depend on HttpContextBase, not HttpContext. Perhaps you can make your sub-controllers depend on HttpContextBase too so you can wrap it.
Just my 2 cents.
I've used
<% Html.RenderAction("Action", "Controller"); %>
to great effect, allowing me to create completely isolated/escapsulated actions without resorting to complex code. This would seem to offer the same functionality without the same complexity.
The rendered views are standard partial views and the controller actions just like any other.

Resources