SharePoint Business Connectiviy Data Model Update method failing - visual-studio

I have an external list in SharePoint that references a BCDM I created inside visual studio. The entity as an ID that is auto generated in the database, which means it's read-only.
Create and read method works fine, I'm trying to implement the update method. I have set an input parameter that match my entity and I'm getting this error.
Failed to update a list item for this external list based on the Entity(External Content Type) ‘Notification’ in EntityNamespace ‘Namespace’. Details: The TypeDescriptor with name ‘Id’ in the Method ‘Microsoft.SharePoint.BusinessData.MetadataModel.Static.Method’ on Entity (External Content Type) with Name ‘Namespace’ in Namespace ‘Notification’ is marked ‘PreUpdaterField’, but is contained by another TypeDescriptor marked ‘PreUpdaterField’.
I tried every possible combinaison to make this work, make the id type descriptor read only, pre-updater field = true/ false/, updater field = true/false, removing it, adding another parameter outside the entity. NOTHING WORKS !!! Obviously, I'm about to commit a murder as something so simple just turned out to be the biggest waste of time in my programmation history. What can I do to make this works??

This has been resolved and is explained here:
http://www.dotnetmikael.com/2014/02/sharepoint-2013-bcdm-with-visual-studio.html

Related

Change record owner in Dynamics CRM plugin

I am writing a Post PLugin changing the owner. When the owner has a substitution manager, the owner is changed to the substitution manager. I tried a service.Update and an AssignRequest, but these throw an exception.
When I post the request my entity cannot update (and then throws "The request channel time out while waiting for reply after 10:00:00"). But like I see there is no recursion, because when I logged it I have only one repetition of log and it has stopped before or on line with update.
var assignedIncident = new AssignRequest
{
Assignee = substManagerRef, //get it throw another method, alreay checked in test it`s correct
Target = new EntityReference ("incident", incedentId)
};
service.Execute(assignedIncident);
I tried to write target in another way
Target = postEntityImage.ToEntityReference()
I tried to write simple update but the problem is the same.
Entity incident = new Entity("incident" , incidentId);
incident["ownerid"] = substManagerRef:
service.Update(incident);
Can somebody help me with that? Or maybe show the way to solve it)
The plugin is triggering itself and gets in a loop. The system only allows a maximum of 8 calls deep within plugins and that is the reason it throws an error and rolls back the transaction.
To solve this issue redesign your plugin in the following way:
Register your plugin on the Update message of your entity in the PreValidation stage.
In the plugin pick up the Target property from the InputParameters collection.
This is an Entity type. Modify or set the ownerid attribute on this Entity object.
That is all. You do not need to do an update, your modification is now passed into the plugin pipeline.
Note: for EntityReference attributes this technique only works when your plugin is registered in the PreValidation stage. For other regular attributes it also works in the PreOperation stage.

How to retrieve addresses using the odata endpoint

I'm trying to retrieve an address on a standard entity.
the request is the following :
https://mycrm.api.crm4.dynamics.com/api/data/v9.1/contacts(guid)
In this example, I get all fields, but the ones that is of interest to me is address1_addressid, which seems to be a Guid, referencing some of record, but I cannot find it in the "Many to One" relationship list I retrieved using the following command :
https://mycrm.api.crm4.dynamics.com/api/data/v9.1/EntityDefinitions(LogicalName='contact')?$select=LogicalName&$expand=ManyToOneRelationships($select=ReferencingAttribute,ReferencedEntity)
I would like to retrieve those addresses in a generic manner, as I'm working on a generic NetStandard 2.0 library. I won't be able to know on which entity I'll be working, and thus won't be able to hard-code a list of addresses field names.
Here's one way to find the Contact to Address relationship:
https://myOrg.api.crm.dynamics.com/api/data/v9.1/EntityDefinitions(LogicalName='customeraddress')?$select=LogicalName&$expand=ManyToOneRelationships($select=ReferencingAttribute,ReferencedEntity)
I got an error with https://myOrg.api.crm.dynamics.com/api/data/v9.1/contacts(guid)?$expand=address1_addressid.
I looked into the MetaData and discovered that address1_addressid has a type of Primary Key:
While a normal lookup field has a type of Lookup:
Considering the error message that appears when attempting to expand address1_addressid, I think the issue is address1_addressid's data type.
Property 'address1_addressid' on type 'Microsoft.Dynamics.CRM.contact'
is not a navigation property or complex property. Only navigation
properties can be expanded.
It would seem that rather than using $expand to get the address details, you'd have to make a separate call for it:
https://myOrg.api.crm.dynamics.com/api/data/v9.1/customeraddresses(guid)
UPDATE
By reviewing the full Contact-Address relationship, via this query: https://myOrg.api.crm.dynamics.com/api/data/v9.1/EntityDefinitions(LogicalName='customeraddress')?$select=LogicalName&$expand=ManyToOneRelationships.
I discovered the ReferencedEntityNavigationPropertyName was set to Contact_CustomerAddress.
The error message before talked about the Navigation property so I gave it a shot. Using that property named allowed me to expand the Address info from the Contact:
https://myOrg.api.crm.dynamics.com/api/data/v9.1/contacts(guid)?$expand=Contact_CustomerAddress
Interestingly, expanding that navigation property returns all 3 customer addresses:

Updating opportunity when opportunity is won

I've got the task of updating a CRM plugin for a system migrating from cm 2013 to 2016. The plugin fails because it tries to set the opportunity state to won, simply by updating the field. And you need to use the WinOpporunityRequest to do so.
The logic is as follows:
When the opportunity is won the plugin executes and runs on the opportunityclose entity
The plugin creates a new custom entity record (project) and updates several other records.
It gets the current opportunity by using the opportunityid of the opportunityclose entity
It updates a field on the opportunity with a reference to the newly created project record.
That update is done through the Update() method.
On 5 it fails because when at 3 it gets the current opportunity it already has the state of won. And if you try to update the record with a new state it fails.
My question is, how can I get the opportunity when acting on the opportunityclose entity and update only the one single field. I do not need to set the state as this is done in the standard CRM flow.
--Edit
The line of code that fetches the opportunity:
Xrm.Opportunity currentOpportunityObjectToUpdate = serviceContext.CreateQuery<Xrm.Opportunity>().First(x => x.Id == entityRef.Id);
The platform allows you to update closed opportunities, I just tried it to verify. What is the error you are getting?
In step #5, make sure you're only sending the attributes you're trying to update (opportunityid and lookup to project). So, when you issue the update, don't use any preexisting opportunity object that you either retrieved or created...doing so sends all attributes that are on the object and the platform will process each attribute as if it were being changed even if the value is unchanged. Instead, create a new opportunity object with just the id and project specified, something like this:
context.AddObject(new Opportunity() {
Id = idOfOpportunity, // you may have to specify id both here...
OpportunityId = idOfOpportunity, // ...and here, can never remember. Doesn't hurt to specify in both places.
new_ProjectId = idOfProject
});
context.SaveChanges();
If you get stuck, you always have an easy workaround option: take the logic from #4 and move it to an async plugin on create of project (even a workflow should work).

should a validation function access the repository directly?

I have the following in my application:
Action Orm entity (From telerik open access)
Repository(Of Action)
AppService(Holds an instance of the repository)
when I need to save an instance, I send the instance to the AppService. the AppService then calls a validator to validate the instance to save. the validator is based on http://codeinsanity.com/archive/2008/12/02/a-framework-for-validation-and-business-rules.aspx
(full code on https://github.com/riteshrao/ncommon)
so basically my save function in the AppService looks like this
Public Sub AddAction(ByVal Item As Data.Model.Action)
Contract.Requires(Of ArgumentNullException)(Item IsNot Nothing, "Item is nothing.")
Dim validateResult As Rules.ValidationResult = _ActionValidator.Validate(Item)
If Not validateResult.IsValid Then
Throw New Validation.ValidationException(validateResult)
End If
Try
_ActionRepository.Add(Item)
_unitOfWork.SaveChanges()
Catch ex As Exception
_unitOfWork.ClearChanges()
Throw New DataServiceException(ex.Message, ex)
End Try
End Sub
for checking properties of the Action item the sample code works great. my question begins when i need to make sure that the same action is not added twice to the DB for the same customer (ie. id is difference, name is the same and customer is the same)
as I see it I have a few options:
option 1: check for a duplicate action using something like
function(validatedItem) item.Customer.Actions.Any(function(item) item.id<>validatedItem.id andalso item.name=validatedItem.name))
basically I go from the action being saved back to the customer and then back to all his actions and check if any action exists with a different id and same name
the down sides are:
a. for this to work, when accessing the customer property of the item, the entire customer object is read from DB which is redundant in this case
b. the Any function is being evaluated on the client as item.Customer.Actions returns IList(Of Action)
Option 2: let the validation class have access to the action repository. then i could simply do something like
'assume I already have validatedItem
repository.Any(function(item) item.id<>validatedItem.id and item.customerid=validatedItem.customerid and item.name=validatedItem.name)
this will result in an Exists query being sent to the DB but the downside(?) is that the validation framework should not access the repository directly (as far as I have seen in the very few examples i could find that do use validation and ORM)
Option 3: let the validation class have access to the AppService and use the AppService to check for existence of a duplicate.
problems:
a. I create a circular reference (AppService->Validation Class->AppService)
b. I need to create a lot of useless functions in the AppService for loading items based on criteria that is only relevant for the validation
Any ideas what is the best course here?
The simplest is not to check duplicates in the database from your domain.
When a collection of entities is part of you aggregate then it is a non-issue since you would not permit the duplicate to be added to the collection. Since the aggregate is stored as a whole you would never run into the problem.
For scenarios where you do not want a duplicate, say, e-mail address and no collection of the entities is represented by an aggregate (such as the Users in a system) you can just let the database enforce the uniqueness. Simply pick up the exception and report back. In many instances your validation would not be able to enforce the uniqueness simply because it doesn't have/implement the required locks that a database system would have.
So I'd simply leave that up to the database.

EnvDTE CodeClass.AddDelegate overload

I want to create 2 delegates with the same name but different parameters (overloaded delegate). When I try to add a delegate I get an error on the second try due to a delegate already existing. I tried to add it first with a temp name then add the parameters and then change the name so the signuature would be different but I still get an error stating that an item already exists with that name.
How can I add an overloaded delegate?
The capabilities of the code model to generate code are limited. You can try the to use EditPoint.Insert(...) to insert the code instead of using AddDelegate() method.

Resources