Write CRM Plugin that Fires when a Field is updated - plugins

We're on CRM 2013 on-premise. I'm writing a plugin that fires when a field on Quote entity is updated.
So I registered my plugin on 'Update' message. Then the event is 'Post-operation'. (I tried Pre-operation but still no luck)
Basically the goal is when the field is updated, create a new entity 'ContractShell' and then create relationship between the Quote and the newly created 'ContractShell'.
However my problem is when the field is updated, my plugin never seems to fire. I just simply put a InvalidPluginExecutionException in my code, but for some reason it never fires.... Any ideas? Thanks.
Here's a screenshot of my plugin step:
Here's my code:
var trace = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
// The InputParameters collection contains all the data passed in the message request.
var targetEntity = context.GetParameterCollection<Entity>(context.InputParameters, "Target");
if (targetEntity == null)
throw new InvalidPluginExecutionException(OperationStatus.Failed, "Target Entity cannot be null");
if (!context.OutputParameters.Contains("id"))
return;
Guid QuoteId = (Guid)targetEntity.Attributes["quoteid"];
var serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
var service = serviceFactory.CreateOrganizationService(context.UserId);
var contractShellEntity = new Entity();
contractShellEntity = new Entity("new_);
//assign the portfolio
if (targetEntity.Attributes.Contains(Schema.Quote.Portfolio))
{
var quotePortfolio = (EntityReference)targetEntity.Attributes[Schema.Quote.Portfolio];
contractShellEntity[Schema.new_ContractShell.PortfolioName] = new EntityReference(quotePortfolio.LogicalName, quotePortfolio.Id);
}
var contractShellId = service.Create(contractShellEntity);
throw new InvalidPluginExecutionException(OperationStatus.Failed, "I created New Contract Shell");
//Creating relationship between Contract Shell and the newly created Accounts
var quoteContractReferenceCollection = new EntityReferenceCollection();
var quoteContractRelatedEntity = new EntityReference
{
Id = contractShellId,
LogicalName = contractShellEntity.LogicalName
};
quoteContractReferenceCollection.Add(quoteContractRelatedEntity);
var quoteContractReferenceCollectionRelRelationship = new Relationship
{
SchemaName = Schema.new_ContractShell.ContractQuoteRelationship
};
service.Associate("quote", QuoteId, quoteContractReferenceCollectionRelRelationship, quoteContractReferenceCollection);

You need to register not only the plugin but an SDKMessageProcessingStep. Also, you have to implement the Execute method in your plugin to be able to register it, so either you're missing code in your snippet, or your code is the problem.
Also, your InvalidPluginExecutionException is nested after a number of checks. Theres a good chance you don't have any output parameters if you don't know how to register a plugin, so your code would actually return before you hit the exception.

Related

EF Core in memory, delete unit testing giving error: entity already tracked

I am working with entity framework core in memory to test my code.I created in memory context and added data. I have delete method which is going to delete record with id=1. Test method is throwing error following error:
The instance of entity type 'cats' cannot be tracked because another instance with the key value '{Id: 204}' is already being tracked When attaching existing entities, ensure that only one entity instance with a given key value is attached.
Could any one help me , how to resolve this issue. I tried different scenarios on internet, but nothing did not work.
Test.cs:
[Fact(DisplayName ="Delete Service Must Return Result")]
public void DeleteService()
{
var serviceId = _collectionFixture.context.cat.Where(x => x.Id == 201).AsNoTracking();
var okObject = _collectionFixture.CatController.DeleteService(serviceId) as OkObjectResult;
var baseResponse = okObject.Value as BaseResponse;
Assert.True(baseResponse.Success);
}
my service class:
public catResponse DeleteService(int serviceId)
{
try{
var serviceDto = _Context.catserviceTreatment.Where(x => x.Id
==serviceId).AsNoTracking().firstordefault();
if(serviceDto!=null)
{
this._Context.catserviceTreatment.Remove(serviceDto);
this._Context.SaveChanges();
return new catResponse { Success = true, Error = "service
deleted successfully" };
}
}catch(exception e)
{
}
}
my collection fixture:
public catContext GetDbContext()
{
var options = new DbContextOptionsBuilder<catContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
.ConfigureWarnings(x =>
x.Ignore(InMemoryEventId.TransactionIgnoredWarning))
// .ConfigureWarnings(x=>x.Ignore(InMemoryEventId.))
.EnableSensitiveDataLogging()
.Options;
var context = new CatDirectoryContext(options);
var catCategory = new List<catcategory>()
{
new catCategory{Id=100,CategoryName="Youth
teereafjkd",UpdatedBy="Test",UpdatedWhen=DateTime.Now},
new catCategory{Id=101,CategoryName="Adult
fdsafd",UpdatedWhen=DateTime.Now},
};
context.catcategory.AddRange(catCategory);
context.SaveChanges();
var catService = new List<catserviceTreatment>()
{
new catserviceTreatment{Id=200,Name="House of
Hope",Company="",
Address ="2001 st street",City="alone
city",State="UT",ZipCode=8404,Category=null,
CategoryId =100,Description="this is
description",UpdatedBy="test",UpdatedWhen=DateTime.Now},
new catserviceTreatment{Id=201,Name="Odyssey
House",Company="",
Address ="2001 st",City="bare
city",State="UT",ZipCode=84dfd,Category=null,
CategoryId =101,Description="this is d
description",UpdatedBy="test",UpdatedWhen=DateTime.Now},
}
context.catserviceTreatment.AddRange(catService);
context.SaveChanges();
return context;
}
Most probably you made some query and then navigate through the results of that query and try to delete the found items.
Something like:
var catsToDelete = myContext.Cats
.Where(cat => cat.Id == 204);
foreach (var cat in catsToDelete) {
myContext.Cats.Remove(cat);
}
It will not work this way because each item is still tracked by Entity Framework.
To avoid that tracking you need either to add ToList() or AsNoTracking() call at the end of the LINQ query:
var catsToDelete = myContext.Cats
.AsNoTracking()
.Where(cat => cat.Id == 204);
foreach (var cat in catsToDelete) {
myContext.Cats.Remove(cat);
}
I had the same error.
During setup I added some test records starting with id 1.
It appeared that the in memory database also starts numbering at 1 when new records are added, causing the "entity already tracked" error.
After I changed my initialization code to use ids as of 101, the problem was solved.

Logic for tracking entity framework property value changes in MVC

I think I am missing something in my understanding of tracking property value changes in entity framework.
I have an application where i store service requests. Whenever a team value in changed in the service request record, I want to create a team history record in a related teamhistory entity.
I have created the app in MVC using the standard scaffolding for controllers and views.
In the (post)edit task in the controller, the standard logic generated has the following code
if (ModelState.IsValid)
{
db.Entry(serviceRequest).State = EntityState.Modified;
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(serviceRequest);
I have modified that to include the creating of the teamhistory record and an individualhistory record for individual assigned within team. The code for creating these related records work, BUT i want these records only created when the values on team or member(individual) change from what they were previously.
So far the conditions i have specified due not trigger this correctly because I havent gotten the condition right. Below is the current code:
//string teamorig = db.Entry(serviceRequest).Property(u => u.Team).OriginalValue.ToString();
//string teamcurr = db.Entry(serviceRequest).Property(u => u.Team).CurrentValue.ToString();
//if (teamorig != teamcurr)
var TeamIsModified = db.Entry(serviceRequest).Property(u => u.Team).IsModified;
if (TeamIsModified)
{
serviceRequest.TeamAssignmentHistories.Add(new TeamAssignmentHistory()
{
DateAssigned = DateTime.Now,
AssignedBy = User.Identity.Name,
ServiceRequest = serviceRequest.Id,
Team = serviceRequest.Team
});
}
//=========================================================================================
// if individual assigned has changed add individual history record========================
var IndividualIsModified = db.Entry(serviceRequest).Property(u => u.Member).IsModified;
if (IndividualIsModified)
{
serviceRequest.IndividualAssignmentHistories.Add(new IndividualAssignmentHistory()
{
DateAssigned = DateTime.Now,
AssignedBy = User.Identity.Name,
ServiceRequest = serviceRequest.Id,
AssignedTo = serviceRequest.Member.Value,
});
}
//===========================================================================================
The var teamismodified logic doesnt work. When I save the page without making any changes on it- the logic kicks off because in debugging it thinks the field has been modified.
When I comment out that code and uncomment the code above it for original and currentvalues- ie the teamorig and teamcurr logic, teamcurr and teamorig have the same values in debug, even when they have been forced into a change on the save in the MVC view page. Because they have the same values, the if condition is false so the team history record is not created.
The above code has been sandwiched in between
db.Entry(serviceRequest).State = EntityState.Modified;
and
await db.SaveChangesAsync();
statements.
What am I not understanding about entity framework tracking changes in mvc? Why does think its modified when i make not changes to team, and why are teamorig and teamcurr the same when I do make the changes?
Any advice would be welcome. Thanks

CRM 2011 How can i get the Default Queue of a user by using SOAP

I want to be able to retrieve the default queue of a user in dynamics crm 2011 using SOAP calls. I tried searching but did not generate any leads. Any help will be appreciated.
Thanks
Try to query queueid field from systemuser entity.
Example:
var userId = new Guid("BCF84567-DE3C-4470-9A61-4B9A32351CB6");
var query = new QueryExpression("systemuser") { ColumnSet = new ColumnSet(new string[] { "queueid" }) };
query.Criteria.AddCondition(new ConditionExpression("systemuserid", ConditionOperator.Equal, userId));
var req = new RetrieveMultipleRequest()
{
Query = query
};
var res = (RetrieveMultipleResponse)slos.Execute(req);
var result = res.EntityCollection[0].Attributes["queueid"];
This will return entity reference to default user query.

Sub entity doesn't update when assign null value to its reference

I have an entity named: Application and it has field ApplicationStatus which is entity as well, ApplicationStatus values are yes or no and it's nullable in Application.
So when I execute:
Application application = MyEntities.Applications.First();
application.ApplicationStatus = null;
MyEntities.SaveChanges();
ApplicationStatus won't update its value, unless I debug it and do quick watch application.ApplicationStatus.
However
application.ApplicationStatusId = null;
works fine.
My question, why this one doesn't work:
application.ApplicationStatus = null;
Thanks
EDIT further question:
As #LadislavMrnka answer solves debug behaviour and and the issue related lazy loading:
how can we interpret these situation?
Application application = MyEntities.Applications.First();
application.ApplicationStatusId = null; //This updates value in db
ApplicationStatus applicationStatus = MyEntities.ApplicationStatuses.First(a => a.Name == "no");
app.ApplicationStatus = applicationStatus; //This updates value in db too
app.ApplicationStatus = null; // But this doesn't
Use this and it will work:
Application application = MyEntities.Applications.Include("ApplicationStatus").First();
The reason is that your ApplicationStatus was never loaded. If you do debug and access it your debugging access will trigger lazy loading and loads it.

EF SaveChanges() throws an exception 'The EntityKey property can only be set when the current value of the property is null'

I have been reading all similar posts I found regarding EF but I can't still manage to found a solution.
I'm very new to the EF and I have been reading some useful info about working with it but I think that I'm missing something.
The scenario is like this:
I want a user to be able to create an account in an ASP.NET webpage. So I have a table named 'Accounts'. The user must agree with the condition terms of the site, that may be updated in the futere, so I have also a table called 'ConditionTerms' that has 1 to many relation with the account (many accounts have an unique condition term).
I wanted to separete the specific personal user data from the data of the account so I also created a table called 'Persons' and I set the relation ship so that a person may have many accounts.
Now, when I want to save an account into the database, I retrieve the last conditionTerm available in the database and I attach it to the account entity. Then when I try to save the data via SaveChanges() I get the exception mentioned in the title of the post. The thing is that if all entities are new, when the associations are created, the EntityState for all the items is 'Detached' and it works, but when I retrieve the existing conditionTerm from the data base and I add it to the account.ConditionTerm, the account changes its state to 'Added' and then it throws the exception.
I read somewhere that when this happens, it means that all the entity tree is considered as already added by the context and I should only need to call SaveChanges() without the AddObject() method since it is already added. I tried this and then I get no exception and the code ends, but then if I check the database (SQL Server 2008 express) the account hasn't been added at all.
This is the code I'm trying and I think it should work but it's clear that I'm missing something:
[TestMethod]
public void TestCreateNewAccount()
{
try
{
AccountRepository accountRepository = new AccountRepository();
Account account = new Account()
{
Username = "TestUsername",
Password = "TestPassword",
Email = "TestEmail",
Nickname = "TestNickName",
Quote = "Test Quote",
AgreedToTermsDate = DateTime.Now,
CreationDate = DateTime.Now,
LastUpdateTime = DateTime.Now
};
// This works (all new entities!)
//ConditionTerm conditionTerm = new ConditionTerm()
//{
// Text = "This is some test condition term.",
// CreationDate = DateTime.Now,
// LastUpdateTime = DateTime.Now
//};
//This does not work (attaching an existing entity to a new one)
ConditionTerm conditionTerm = new ConditionTermsRepository().GetCurrentTerm();
Person person = new Person()
{
FirstName = "TestName",
Surname = "TestSurname",
CreationDate = DateTime.Now,
LastUpdateTime = DateTime.Now
};
account.ConditionTerm = conditionTerm;
account.Person = person;
using (ImproveEntities entities = Connection.GetModel())
{
if (account.ID > 0)
{
Account newAccount = new Account();
newAccount.ID = account.ID;
entities.Accounts.Attach(newAccount);
entities.Accounts.ApplyCurrentValues(account);
}
else
{
entities.Accounts.AddObject(account);
entities.SaveChanges();
}
}
}
catch (Exception)
{
}
}
Any help would be very much apreciated!
EDIT: This is the GetCurrentTerm() method:
public ConditionTerm GetCurrentTerm()
{
using (ImproveEntities entities = Connection.GetModel())
{
ConditionTerm conditionTerm = (from ct in entities.ConditionTerms
orderby ct.CreationDate descending
select ct).FirstOrDefault();
return conditionTerm;
}
}
If I understand correctly you want to insert a new account along with a new user into the database. But you don't want to create a new ConditionTerm but instead assign an existing ConditionTerm to the account.
The problem is that you fetch the existing ConditionTerm in another context (in your GetCurrentTerm) than the context you use for saving the new account. This second context doesn't know anything about the ConditionTerm, so you must EF explicitely tell that this conditionTerm already exists by attaching it to the second context:
// ...
using (ImproveEntities entities = Connection.GetModel())
{
entities.ConditionTerms.Attach(conditionTerm);
account.ConditionTerm = conditionTerm;
account.Person = person;
entities.Accounts.AddObject(account);
entities.SaveChanges();
}
// ...