retrieving id of notification that is inserted using PublishAsync method is not implemented - aspnetboilerplate

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)

Related

How to create Action and Call action in MS Dynamics CRM?

How to create Action step by step and Call action in MS Dynamics CRM?
How many ways to call action in MS Dynamics CRM?
What the benefits of action instead of Workflow/plugin?
Actions
Actions are a type of process in Microsoft Dynamics 365. You can invoke actions, including custom actions, directly from a workflow or dialog, without writing code! More information: Invoke custom actions from a workflow or dialog
Actions can also be invoked by running custom code that uses the Microsoft Dynamics 365 Web services.
You can call actions:
It can be called from both client & server side, enabling the Single Point of Approach (Implement once, consume anywhere), for ex:- From code that executes within a plug-in, custom workflow and any C# code.
From a command that is placed in the application and executes the operation using JavaScript code.
Can receive input parameters and return output parameters in a straight forward manner, similar to an Organization level Web Service
From an integration with another system that uses the Microsoft Dynamics 365 web services.
From a custom client application that uses the Microsoft Dynamics 365 web services.
Why use actions?
Actions open a range of possibilities for composing business logic. Before Actions, the primary way to implement business processes was limited to plug-ins or custom workflow activities. With Actions, you can perform operations, such as Create, Update, Delete, Assign, or Perform Action. Internally, an action creates a custom Dynamics 365 message. With Actions you can create a custom message (for example: submitquote , leadtoax etc. Once an action is defined and activated, a developer can use that message like any of the other messages provided by the Microsoft Dynamics 365 platform.
Suppose you have button on Quote form which sends information from CRM to another platform (for ex another platform is AX).
Create and activate a Custom Action (Settings > Process)
Now you can call this Action(ofs_submitquotetoax) from JavaScript on some event (OnLoad, OnSave,etc). In this example I am calling the action from SUBMIT QUOTE button on Quote form which sending quote information to other system (AX).
// Call this below method from Button click event or from any event on the form
// For Alert.showLoding method you can see another Alert.js library
// For Process.callAction method you can see another Process.js library
// You can download Alert.js & Process.js from this Path: https://drive.google.com/drive/folders/0B2CUbevE8v9YMkZlMEhUZ3NJc1U
function submitquote() {
var actionName = "ofs_submitquoteax";
var entityName = Xrm.Page.data.entity.getEntityName();
var entityId = Xrm.Page.data.entity.getId();
Alert.showLoading("Submitting...", 400, 150);
var inputParams = [
{
key: "EntityRef", type: Process.Type.EntityReference,
value: new Process.EntityReference(entityName, entityId)
}
];
// call process callection method
Process.callAction(actionName, inputParams, cloneSuccessCallback, errorCallback);
}
function cloneSuccessCallback() {
Alert.hide();
Alert.show("Action Success", "", null, "SUCCESS");
Alert.hide();
var entityName = Xrm.Page.data.entity.getEntityName();
var entityId = Xrm.Page.data.entity.getId();
Xrm.Utility.openEntityForm(entityName, entityId);
//Xrm.Page.data.refresh();
}
function errorCallback(error, trace) {
alert(error);
alert(alert(error));
}
e, for this event you can register & trigger a Plugin and receive the input-parameter (in our case we sending input-parameter key as EntityRef in which we are sending entityName and entityId.
Parameters: Action Unique Name, Input Parameters (array), Success Callback (function), Error Callback (function), CRM Base URL (not required on forms/views)
Each Input Parameter object should contain key, value, and type. Types are defined by the Process.Type enum. EntityReference values should be an object containing id and entityType.
The Success Callback function should accept one argument which is an array of output parameters, each containing key, and value.
You can write plugin in below way and access input parameter
protected override void ExecuteCrmPlugin(LocalPluginContext localContext)
{ // Register the plugin in PreValidation stage as this plugin will trigger from Javascript (Action)
if (localContext == null)
{
throw new InvalidPluginExecutionException("localContext");
}
IPluginExecutionContext context = localContext.PluginExecutionContext;
if (context.Depth > 1) { return; }
IOrganizationService service = localContext.OrganizationService;
ITracingService trace = localContext.TracingService;
Entity quoteEntity = null;
EntityReference qEntity = null;
if (context.InputParameters.Contains("EntityRef") && context.InputParameters["EntityRef"] is EntityReference)
{
//if (context.PrimaryEntityName.ToLower() != "quote") { return; }
qEntity = context.InputParameters["EntityRef"] as EntityReference;
if (qEntity.LogicalName.ToLower().Equals("quote"))
{
try
{
quoteEntity = service.Retrieve("quote", qEntity.Id, new ColumnSet("ofs_parentaccountid", "quotenumber", "revisionnumber", "ofs_well"));
// Execute Your logic
}
catch (Exception ex)
{
trace.Trace(string.Format("Exception Quote_Create_AXIntegration Plugin: {0}", new[] { ex.ToString() }));
}
}
}
else { return; }
}
Register your plugin and then register the step in below way, you can notice your custom message name in below screen-shot "ofs_submitquoteax";
Ref:https://community.dynamics.com/crm/b/mylifemicrosoftdynamiccrm/archive/2017/04/17/microsoft-dynamics-crm-actions

Breeze entity state doesn't change after saving

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.

How can I determine the revision of a calendar item in EWS when using a PullSubscription

I am trying to do some synchronization work with and Exchange Calendar. I want to keep another external calendar in sync with Exchange. Currently when the other app triggers a creation or update of some sort in Exchange, that change is then sent back to the other calendar creating an endless loop.
I had hoped to use the AppointmentSequenceNumber property when binding the Appointment item, but it always has the value of 0 no matter how many times it is updated. I am including AppointmentSequenceNumber in my PropertySet.
If anyone knows of a way to catch these updates and keep them from being sent back, that would be very helpful.
Thank you.
PropertySet pSet = new PropertySet(BasePropertySet.FirstClassProperties, ItemSchema.Subject, ItemSchema.Body, AppointmentSchema.Start, AppointmentSchema.End,AppointmentSchema.ArchiveTag, AppointmentSchema.InstanceKey, AppointmentSchema.AppointmentSequenceNumber);
ChangeCollection<ItemChange> changes = null;
.....
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013)
{
Url = new Uri(exInfo.ServiceURL),
Credentials = new WebCredentials(exInfo.UserName, exInfo.Password)
};
//Pull Subscription Info
Microsoft.Exchange.WebServices.Data.PullSubscription sub = service.SubscribeToPullNotifications(
new FolderId[] { WellKnownFolderName.Calendar }, 30, "",
EventType.Created, EventType.Modified, EventType.Deleted);
syncState = exInfo.SyncState;
//Pull Changes
while (!syncComplete )//&& Count < MaxItems)
{
changes = service.SyncFolderItems(new FolderId(WellKnownFolderName.Calendar),
PropertySet.FirstClassProperties, null, 100, SyncFolderItemsScope.NormalItems, syncState);
foreach (ItemChange change in changes)
{
if (change.ChangeType != ChangeType.Delete) { eventItem = Appointment.Bind(service, change.ItemId, pSet); }
switch (change.ChangeType)
{
case ChangeType.Update:
...
break;
case ChangeType.Create:
...
break;
case ChangeType.Delete:
...
break;
}
Count++;
}
syncState = changes.SyncState;
syncComplete = !changes.MoreChangesAvailable;
}...
The AppointmentSequenceNumber would only be valid for Meetings; on normal Appointments it isn't used.
I had hoped to use the AppointmentSequenceNumber property when binding the Appointment item
That wouldn't work even if it was incrementing. Exchange will always provide you with the current version and the only things valid in a Bind is the EWSId of the appointment (or the Recurrence Sequence).
If anyone knows of a way to catch these updates and keep them from being sent back, that would be very helpful.
Synchronization is complicated but (from a notification perspective) if you modify an item in Exchange it's going to fire a notification and the ChangeKey attribute on the Item will be updated (quote):
"When you work with items in Exchange, another value to keep in mind is the ChangeKey attribute. This value, in addition to the item ID, is used to keep track of the state of an item. Any time an item is changed, a new change key is generated. When you perform an UpdateItem operation, for example, you can use the ChangeKey attribute to let the server know that your update is being applied to the most current version of the item. If another application made a change to the item you’re updating, the change keys won’t match and you will not be able to perform the update."

Can I switch use of 'entities.SingleOrDefault' ==> 'entities.Find' without hazards?

In my WCF service's business logic, most of the places when I need to locate an entity, I use this syntax:
public void UpdateUser(Guid userId, String notes)
{
using (ProjEntities entities = new ProjEntities())
{
User currUser = entities.SingleOrDefault(us => us.Id == userId);
if (currUser == null)
throw new Exception("User with ID " + userId + " was not found");
}
}
I have recentely discovered that the DbContext has the Find method, and I understand I can now do this:
public void UpdateUser(Guid userId, String notes)
{
using (ProjEntities entities = new ProjEntities())
{
User currUser = entities.Find(userId);
if (currUser == null)
throw new Exception("User with ID " + userId + " was not found");
}
}
Note : the 'userId' property is the primary key for the table.
I read that when using Find method entity framework checks first to see if the entity is already in the local memory, and if so - brings it from there. Otherwise - a trip is made to the database (vs. SingleOrDefault which always makes a trip to the database).
I was wondering if I now will convert all my uses of SingleOrDefault to Find is there any potential of danger?
Is there a chance I could get some old data that has not been updated if I use Find and it fetches the data from memory instead of the database?
What happens if I have the user in memory, and someone changed the user in the database - won't it be a problem if I always use now this 'memory' replica instead of always fetching the latest updated one from the database?
Is there a chance I could get some old data that has not been updated
if I use Find and it fetches the data from memory instead of the
database?
I think you have sort of answered your own question here. Yes, there is a chance that using Find you could end up having an entity returned that is out of sync with your database because your context has a local copy.
There isn't much more anyone can tell you without knowing more about your specific application; do you keep a context alive for a long time or do you open it, do your updates and close it? obviously, the longer you keep your context around the more susceptible you are to retrieving an up to date entity.
I can think of two strategies for dealing with this. The first is outlined above; open your context, do what you need and then dispose of it:
using (var ctx = new MyContext())
{
var entity = ctx.EntitySet.Find(123);
// Do something with your entity here...
ctx.SaveChanges();
}
Secondly, you could retrieve the DbEntityEntry for your entity and use the GetDatabaseValues method to update it with the values from the database. Something like this:
var entity = ctx.EntitySet.Find(123);
// This could be a cached version so ensure it is up to date.
var entry = ctx.Entry(entity);
entry.OriginalValues.SetValues(entry.GetDatabaseValues());

WCF Data Service - Add Object With Related Object

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.

Resources