I have the following situation, I have a WCF Data Service with User objects and message objects and the message object has two relations to user, a sender and a receiver.
When I try to add a new Message object the related users are left null
Message message = new Message();
message.text = InputText; // string
message.Sender = Sender; // User object
message.Receiver = Receiver; // User object
context.AddToMessages(message);
context.BeginSaveChanges(new AsyncCallback((result) =>
{
// Some code
}));
Now the Sender and Receiver will be null. When I try to set a link before the BeginSaceChanges like this I get the error "InvalidOperationException: The context is not currently tracking the entity."
context.AddToMessages(message);
context.AddLink(message, "Sender", message.Sender);
context.AddLink(message, "Receiver", message.Receiver);
context.BeginSaveChanges(new AsyncCallback((result) =>
{
// Some code
}));
How do I make sure the relations are created properly?
Thanks to Pratik I found the solution. I had to use attach the already existing users Sender and Receiver to the context first because they weren't tracked (and added a if if they are on the second call). Then I add the message and use SetLink to set the link to both users (instead of AddLink)
if(context.GetEntityDescriptor(message.Sender) == null)
context.AttachTo("Users", message.Sender);
if (context.GetEntityDescriptor(message.Receiver) == null)
context.AttachTo("Users", message.Receiver);
context.AddToMessages(message);
context.SetLink(message, "Sender", message.Sender);
context.SetLink(message, "Receiver", message.Receiver);
context.BeginSaveChanges(new AsyncCallback((result) =>
{
// Some code
}));
I believe you need to use the DbSet.Attach method instead. I assume you use Entity Framework on the back end here.
Related
I am using ASP.NET Boilerplate with Code-First Entity Framework and MVC 5.
and in order to send a notification I am using the following code
public async Task SendNotification(Guid auditId, int auditNo, int? tenantId, long userId)
{
var notificationData = new LocalizableMessageNotificationData(
new LocalizableString(
"NotificationName",
EODAConsts.LocalizationSourceName
)
);
notificationData["auditNo"] = auditNo;
notificationData["auditId"] = auditId;
await _notificationPublisher.PublishAsync(NotificationName, notificationData, severity: NotificationSeverity.Error, userIds: new[] { new UserIdentifier(tenantId, userId) });
}
we know that sending the notification means adding it to AbpTenantNotifications and AbpUserNotifications ,but after sending it what is the way to retrieve inserted notification id in AbpTenantNotifications ,because PublishAsync method doesn't return any value
i mean what is the unique key in table AbpTenantNotifications which insures selecting specific one notification that is inserted after calling PublishAsync method
NotificationInfo only persist in the table for a short time only.
When you calls PublishAsync, NotifcationInfo is created immediately (see here).
Subsequently, it is consumed by NotificationDistributor.DistributeAsync and deleted right after converting NotificationInfo into TenantNotification & UserNotification (see here)
If you want to capture the TenantNotification when it is created, you can try with entity event handler (see here)
My application uses BreezeJS, ASP.NET Web API and EF.
I'm trying to save an object using breeze, as follows:
var saveOptions = this.manager.saveOptions.using({ resourceName: "SaveLocationSettings", tag: clientId, allowConcurrentSaves: true });
var obj = self.manager.saveChanges(null, saveOptions).then(saveSucceeded, saveFailed);
I'm using a custom save method on the server side, which returns a SaveResult object. However, on the client side, the entity manager still maintains the modified state.
My controller on the Web API is a BreezeController.
According to the breeze documentation, if your custom method has the signature similar to the Breeze SaveChanges() method, it should work similar to SaveChanges() method. However, if I use the breeze SaveChanges(), the entity state gets updated properly. But my custom endpoint save does not update the entity state, although the data is saved in the database.
UPDATE:
After some investigation, I figured that this happens only with one entity type that goes to this particular save endpoint. Say, I have a 'location' object, with a collection of 'availability' associated with it, as follows:
Class Location {
public Location() {
this.Availabilities = new HashSet<Availability>();
}
}
Now from the client side, if I only change some property of the Location object, it handles the hasChanges property correctly. But if I change the Availability only or Availability along with another property of the location, then the hasChanges is not updated properly on client side.
This is my server side code that's called from the WebAPI controller:
public SaveResult SaveLocation(Location l, List<MaxAvailability> maxAvailability, int changedBy)
{
// Create a SaveResult object
// we need to return a SaveResult object for breeze
var keyMappings = new List<KeyMapping>();
var entities = new List<object> {l, maxAvailability};
var saveResult = new SaveResult() { Entities = entities, KeyMappings = keyMappings, Errors = null };
try
{
if (l.LocationId == -1)
{
// add new location
l.LocationId = this.AddNewLocationWithItsAssociatedData(l, maxAvailability, changedBy);
}
else
{
// do changes to the existing location
this.UpdateExistingLocationWithItsAssociatedData(l, maxAvailability, changedBy);
}
}
catch (DbEntityValidationException ex)
{
// Log the error and add the errors list to SaveResult.
// Retrieve the error messages as a list of strings.
saveResult.Errors = this.GetErrors(ex);
}
return saveResult;
}
I think I figured out the answer. It was due to some bad practice in my code. When modifying the availability of an existing location, instead of updating the existing record, I was deleting the existing record and adding a new one. This was causing the client side availability object and the database object to have two different states. Once it was resolved, the hasChanges() state was behaving as expected.
I am trying to get a Reservation object which contains a pointer to Restaurant.
In Parse Cloud code, i am able to get the restaurants objects associated with Reservations via query.include('Restaurant') in log just before response.success. However, the Restaurants reverted back to pointer when i receive the response on client app.
I tried reverted JSSDK version to 1.4.2 & 1.6.7 as suggested in some answers, but it doesn't work for me.
Parse.Cloud.define('getreservationsforuser', function(request, response) {
var user = request.user
console.log(user)
var query = new Parse.Query('Reservations')
query.equalTo('User', user)
query.include('Restaurant')
query.find({
success : function(results) {
console.log(JSON.stringify(results))
response.success(results)
},
error : function (error) {
response.error(error)
}
})
})
response :
..."restaurant":{"__type":"Pointer",
"className":"Restaurants",
"objectId":"kIIYe7Z0tD"},...
You can't directly send the pointer objects back from cloud code even though you have included it. You need to manually copy the content of that pointer object to a javascript object. Like below:
var restaurant = {}
restaurant["id"] = YOUR_POINTER_OBJECT.id;
restaurant["createdAt"] = YOUR_POINTER_OBJECT.createdAt;
restaurant["custom_field"] = YOUR_POINTER_OBJECT.get("custom_field");
ps: in your code you seem do nothing else other than directly send the response back. I think parse REST api might be a better choice in that case.
It turned out that my code implementation was correct.
I have an http module where I'm adding a response filter below for compression. This works for all API calls except for 1, the call to MetaData. If I remove the [BreezeController] decoration it works fine. I think it has to do with action filter attribute that converts the string return type into an HttpResponse return type with string content.
The error I'm getting is " Exception message: The stream state of the underlying compression routine is inconsistent."
I've done some testing where a method thats defined to return an HttpResponse works fine. So I think its the scenario where the method is defined to return string, and then the action filter changes it to HttpResponse at runtime.
Any ideas how I can get this to work?
Here's the response filter being added in BeginRequest:
HttpApplication app = (HttpApplication)sender;
// Check the header to see if it can accept compressed output
string encodings = app.Request.Headers.Get("Accept-Encoding");
if (encodings == null)
return;
Stream s = app.Response.Filter;
encodings = encodings.ToLower();
if (encodings.Contains("gzip"))
{
app.Response.Filter = new GZipStream(s, CompressionMode.Compress);
app.Response.AppendHeader("Content-Encoding", "gzip");
}
Don't know the specifics of what you're doing but I know that the [BreezeController] attribute strips out filters and adds back just the ones that breeze wants.
One approach might be to define a separate controller (ModelMetadataController) that only serves the metadata. This controller doesn't have the [BreezeController] attribute; it's a plain old Web API controller.
Then you create a "Breeze controller" (ModelController) with all of the usual methods except the Metadata method.
You call the metadata controller from the client during app launch via MetadataStore.fetchMetadata just to get metadata.
Once you have populated a metadataStore in this fashion, you use it in your EntityManager which sends query and save requests to the "real" Web API data controller.
The client code might look something like this:
var ds = new breeze.DataService({
serviceName: 'breeze/Model' // the breeze query & save controller
});
var ms = new MetadataStore({
namingConvention: breeze.NamingConvention.camelCase, // assuming that's what you want
});
ms.addDataService(ds); // associate the metadata-to-come with the "real" dataService
var manager = new breeze.EntityManager({
dataService: ds,
metadataStore: ms
});
// the fun bit: fetch the metadata from a different controller
var promise = ms.fetchMetadata('breeze/ModelMetadata') // the metadata-only controller!
return promise; // wait on it appropriately
In my app, I have users and posts, and I am trying to instantiate my Activity class which has pointers to user and post objects. Here is my code:
var Activity = Parse.Object.extend("Activity");
var User = Parse.Object.extend("_User");
var Post = Parse.Object.extend("Posts");
var activityHandler = function(request, response){
//toUser is the objectId of the User not the object itself.
var toUserId = request.toUser;
var fromUserId = request.fromUser;
var postId = request.post;
var arg = request.argument;
var toUser = new User();
toUser.set("objectId",toUserId);
var fromUser = new User();
fromUser.set("objectId",fromUserId);
var post = new Post();
post.set("objectId",postId);
var activity = new Activity();
activity.set("activityFrom", fromUser);
activity.set("activityTo", toUser);
activity.set("argument", arg);
activity.set("post", post);
activity.save(null, {
useMasterKey: true,
success: function(o){
console.log("activity logged successfully");
response.success();
},
error: function(err){
console.log(err);
},
}
);
};
Parse.Cloud.define("createActivity", activityHandler);
The code is self-explanatory, but just to summarize, the client sends the appropriate user and post IDs, and the cloud function should instantiate a new Activity object, with pointers to the users and post. However, when I try to save, I'm getting:
{"code":141,"error":"Uncaught Tried to save an object with a pointer to a new, unsaved object."}
The problem is that, the object IDs sent to the function are completely valid, already-existing objects in the database. ACL is also not an issue as I'm on the master key.
What am I doing wrong?
It was my bad.
There were multiple problems with my code.
I was using object.set("objectId", id); syntax, which actually should be object.id = id. I'm not sure if it is necessary, maybe it would work without that change too, but I've read everywhere that it's a bad practice.
I wasn't getting the request parameters correctly. I was using request.param1 instead of request.params.param1, and the parameter variable was undefined. I haven't checked if it was undefined or not for a long time, as the error message wasn't really helpful about parameter being undefined, from my perspective back then. (now it makes sense, no ID, and it automatically tries to create a new object)
I've corrected them and it does save correctly now.
This also happens when you forget to call the method:
response.success(object);