I have written a C# plugin for post update of the parent record based on the multiple fields.
In this I am trying to calculate the total value in the parent entity based on the values updated in the child entity, which has rate and units fields in it. So basically, total=rate*unit. The code builds fine, but when creating a new form in dynamics crm it genetrates a Business Process Error: Unexpected exception from plug-in (Execute): Parentchild1.parentchildpluginSystem.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
Here is my code:
namespace Parentchild1
{
public class parentchildplugin : IPlugin
{
private Entity abcevent_parent;
public void Execute(IServiceProvider serviceProvider)
{
// Obtain the execution context from the service provider.
IPluginExecutionContext context =
(IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
// Get a reference to the Organization service.
IOrganizationServiceFactory factory =
(IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = factory.CreateOrganizationService(context.UserId);
if (context.InputParameters != null)
{
//entity = (Entity)context.InputParameters["Target"];
//Instead of getting entity from Target, we use the Image
Entity entity = context.PostEntityImages["PostImage"];
Money rate = (Money)entity.Attributes["abcevent_rate"];
int unit = (int)entity.Attributes["abcevent_unit"];
// EntityReference parent = (EntityReference)entity.Attributes["abcevent_parentid"];
//Multiply
// Money total = new Money(rate.Value * units);
//Set the update entity
Entity parententity = new Entity("abcevent_parent");
//parententity.Id = parent.Id;
//parententity.Attributes["abcevent_total"] = total;
// abcevent_parentid = Guid IOrganizationservice.Create(Entity parentid);
Guid parentGuid = service.Create(abcevent_parent);
EntityReference parent = (EntityReference)entity.Attributes["abcevent_parentid"];
Money total = new Money(rate.Value * unit);
//Update
//service.Update(parententity);
}
the problem you ask for:
The given key was not present in the dictionary.
It is because one key is not in the list, if you try to get an attribute like
int number = (int)entity.Attributes["random_attribute"]
This throws the error, because random_attribute it is not in the context.
You have to make sure the attribute is in the context... the best practice to this is asking for a Contains:
if (entity.Contains("random_attribute"))
This way you know you can safely access to the attribute.
Another reason may be the Image, make sure it is in the context.
Related
I try to retrieve the createdby field when I create a cases with a plugin, but the first retrieval fails, and the second and subsequent retrieval are successful. And then when I logged out and login with other user the first retrieval fails (retrieve result is the user before i change the user), and the second and subsequent retrieval are successful.
here is the code i write :
public void Execute(IServiceProvider serviceProv)
{
IPluginExecutionContext context = (IPluginExecutionContext)serviceProv.GetService(typeof(IPluginExecutionContext));
IOrganizationServiceFactory servicefac = (IOrganizationServiceFactory)serviceProv.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = servicefac.CreateOrganizationService(context.UserId);
ITracingService trace = (ITracingService)serviceProv.GetService(typeof(ITracingService));
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
{
Entity ent = (Entity)context.InputParameters["Target"];
if (ent.LogicalName != "incident")
return;
QueryExpression qe = new QueryExpression("incident");
string[] cols1 = { "createdby" };
qe.ColumnSet = new ColumnSet(true);
EntityCollection ec = service.RetrieveMultiple(qe);
foreach (Entity act in ec.Entities)
{
created = act. GetAttributeValue<EntityReference>("createdby").Name;
}
if (created == "CRM SNA")
{
created = string.Empty;
}
else
{
//here is the autonumber code
created = string.Empty;
}
}
}
What I want to make is an autonumber plugin, when cases are created by "CRM SNA" then the autonumber must not run, when cases are created by other users the autonumber will run.
How to make the first retrieve successful? and did not retrieve the user before?
thanks.
I assume your plugin runs in the Pre-Create step. CreatedBy and CreatedOn are not available in this step (probably because the record is not saved yet).
If you are just trying to get the user that executed the action that fired the plugin, use context.InitiatingUserId. You could also look into the documentation for the WhoAmI request.
Hope that helps!
I'm writing a plugin on Campaign which used a Business Process Flow. The fields ProcessId and StageId which are created by the Business Process Flow in Campaign Entity. I need to retrieve these values for a record in my plugin.
They don't appear in the Plugin Registration Tool's Step Image. They don't even appear in CRM workflows were I can populate them in some other field.
Is there a good alternative on how I can achieve this?
Why not just grab a service from your IServiceProvider, and retrieve the fields?
public void Execute(IServiceProvider serviceProvider)
{
IPluginExecutionContext context = CommonPluginLibrary.GetContextFromIServiceProvider(serviceProvider);
IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = factory.CreateOrganizationService(context.UserId);
Entity campaign = service.Retrieve(context.PrimaryEntityId, context.PrimaryEntityName, new ColumnSet("processid", "stageid"));
// ...
// Do your stuff with campaign["processid"], campaign["stageid"]
// ...
}
Another approach would be creating two shadow fields for stageid and processid (e.g. new_stageid, new_processid), and populating these fields using a synchronous workflow that triggers on process/stage update.
Then, you could register your plug-in on these shadow fields as they would be your entity's custom attributes.
You should be able to retrieve this by passing the correct input parameters to a workflow activity in this business process flow.
1) if you have these string inputs:
[RequiredArgument]
[Input("Process Name")]
public InArgument Process { get; set; }
[RequiredArgument]
[Input("Process Stage Name")]
public InArgument ProcessStage { get; set; }
2) Execute code Get Process:
using (var _context= new OrganizationServiceContext(service))
{
// Get the processid using the name provided
var process = (from p in _context.CreateQuery()
where
p.Name == Process.Get(executionContext)
&&
p.StateCode == WorkflowState.Activated
select new Workflow
{WorkflowId = p.WorkflowId}
).FirstOrDefault();
if (process==null)
throw new InvalidPluginExecutionException(string.Format("Process '{0}' not found",Process.Get(executionContext)));
Get the stage id using the name provided
var stage = (from s in _context.CreateQuery()
where
s.StageName == ProcessStage.Get(executionContext)
&&
s.ProcessId.Id == process.WorkflowId
select new ProcessStage
{ProcessStageId = s.ProcessStageId}
).FirstOrDefault();
if (stage == null)
throw new InvalidPluginExecutionException(string.Format("Stage '{0}' not found", Process.Get(executionContext)));
You can now Change Update the stage with your values retrieved ...
Entity uStage = new Entity(context.PrimaryEntityName);
uStage.Id = context.PrimaryEntityId; //
uStage["stageid"] = stage.ProcessStageId; //retrieved stage
uStage["processid"] = process.WorkflowId; //process id
To access process and stage ids you can use processid and stageid fields of record. I'm pretty sure that you can get it from Images or through direct read of a record. Additionally you can recheck following article:
https://deepakexploring.wordpress.com/tag/updating-process-id-in-crm-2013/
I have found some plugin code on the web that enables me to get the entity ID and the object type code for an entity in a plugin. The plugin is fired on RetrieveMultiple on activitypointer. The code lets me get the id and object code of the entity that is currently being viewed (which is displaying the activities grid which is firing the plugin).
This code works fine when using the web interface. However I need it to also work in the Outlook preview pane and currently it does not. The activities grid in the Outlook preview pane just says "an error has occurred". Below is the code that the plugin is using to get the details from the web header.
internal static Dictionary<string, string> GetHeaderFields(HttpContext webcontext, string objectTypeCode, string objectId)
{
Dictionary<string, string> fields = new Dictionary<string, string>();
string callerentitytype = null;
string callerentityidstring = null;
try
{
// Activities Navigation Pane
if (new List<string>(webcontext.Request.Params.AllKeys).Contains("oType"))
{
callerentitytype = webcontext.Request.Params["oType"];
callerentityidstring = webcontext.Request.Params["oId"];
}
// Activities Sub Grid
else
{
string requeststring = webcontext.Request.UrlReferrer.Query;
requeststring = requeststring.Substring(1);
string[] parts = requeststring.Split(new string[] { "=", "&" }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < parts.Length - 1; i++)
if (parts[i].ToLower() == "otype" || parts[i].ToLower() == "etc")
callerentitytype = parts[i + 1];
else if (parts[i].ToLower() == "oid" || parts[i].ToLower() == "id")
callerentityidstring = parts[i + 1];
}
fields.Add(objectTypeCode, callerentitytype);
fields.Add(objectId, callerentityidstring);
}
catch (Exception ex)
{
throw new Plugin.LoggableException(string.Format("Failed to obtain header information; {0}", ex.Message), ex.InnerException);
}
return fields;
}
The reason is that webcontext.Request.UrlReferrer is NULL. Is there anywhere else I can get this info of the 'calling' entity? (Not the activity sub grid that is triggering the plugin, but the actual parent entity that the sub grid is on).
Thanks for any help or direction with this.
This might work. Each of the activitypointers that are returned should all be "regarding" the same record (if in a sub grid). If you take say the 1st one and examine the regardingobjectid property, that should be an entity reference which will give you the logical name of the parent and it's guid. If that works, it will work across all clients (in theory anyway).
I'm trying to bring test layer to my project but I'm not getting there :( hope someone can help me.
Controller (based on Automapper mapping and Dependency Injection Container):
public virtual ActionResult SearchCategories(string keywords)
{
var result = _categoryService.SearchCategories(keywords);
var resultViewModel = Mapper.
Map<IList<SearchCategoriesDto>,
IList<SearchCategoriesViewModel>>(result);
return View(resultViewModel);
}
Service Layer:
public IList<SearchCategoriesDto> SearchCategories(String keywords)
{
// Find the keywords in the Keywords table
var keywordQuery = _keywordRepository.Query;
foreach (string keyword in splitKeywords)
{
keywordQuery = keywordQuery.Where(p => p.Name == keyword);
}
// Get the Categories from the Search
var keywordAdCategoryQuery = _keywordAdCategoryRepository.Query;
var categoryQuery = _categoryRepository.Query;
var query = from k in keywordQuery
join kac in keywordAdCategoryQuery on k.Id equals kac.Keyword_Id
join c in categoryQuery on kac.Category_Id equals c.Id
select new SearchCategoriesDto
{
Id = c.Id,
Name = c.Name,
SearchCount = keywordAdCategoryQuery
.Where(s => s.Category_Id == c.Id)
.GroupBy(p => p.Ad_Id).Count(),
ListController = c.ListController,
ListAction = c.ListAction
};
var searchResults = query.Distinct().ToList();
return searchResults;
}
Test maded but not working:
[TestMethod]
public void Home_SearchCategories_Test()
{
// Setup
var catetoryService = new CategoryService(
_categoryRepository,
_keywordRepository,
_keywordAdCategoryRepository);
// Act
var result = catetoryService.SearchCategories("audi");
// Add verifications here
Assert.IsTrue(result.Count > 0);
}
Thanks.
I am assuming you want to create an integration test for your category service, using real repositories and database. (as oposed to an unit test where you would use stub\mocks for those repositories and test the service class in isolation)
So you would have a seaparated test assembly where you will add your integration tests, for example having a class for the integration tests at the service level.
On that class you will then create instances of all the repositories and the CategoryService before running each test, on a method with the attribute [TestInitialize]. (A method with this attribute will be run by msTest before each test)
As you are also truly working with the database you would like to be sure that any resources used are disposed. For example, disposing an entity framework context. In that case you would add a method with an attribute [TestCleanup] where you will perform any cleanup logic needed. (A method with this attribute will be run by msTest after each test)
With those methods you will make sure you have a fresh service and repositories instances on each test. Then you will implement each individual integration test following the AAA pattern (Arrange, Act, Assert)
So an example of your integration test class with that single test may look like:
public class CategoryServiceIntegrationTest
{
//dependencies of your class under test
private ICategoryRepository _categoryRepository;
private IKeywordRepository _keywordRepository;
private IKeywordAdCategoryRepository _keywordAdCategoryRepository;
//your class under test
private CategoryService _categoryService;
[TestInitialize]
public void InitializeBeforeRunningATest()
{
//manually create instances of the classes implementing the repositories
//I donĀ“t know about how they are implemented but I guess
//you would need to provide the name of a connection string in the config file (in that case this should be in a config file of the test project)
//,the connection string itself
//or maybe you need to initialize an entity framework context
_categoryRepository = new CategoryRepository( /*whatever you need to provide*/);
_keywordRepository = new KeywordRepository( /*whatever you need to provide*/);
_keywordAdCategoryRepository = new KeywordAdCategoryRepository( /*whatever you need to provide*/);
//Create the class under test with all repositories dependencies
//as it is an integration test, they are your real objects and not mocks\stubs
_categoryService = new CategoryService(_categoryRepository,
_keywordRepository,
_keywordAdCategoryRepository);
}
[TestCleanup]
public void CleanDatabaseResources()
{
//just in case you need to do something like disposing an EF context object
}
[TestMethod]
public void Home_SearchCategories_Test()
{
// Arrange
var keywords = "audi";
// Act (the _categoryService instance was created in the initialize method)
var result = _categoryService.SearchCategories(keywords);
// Assert
Assert.IsTrue(result.Count > 0);
}
}
Solution to build an Integration test for a Service (in this case, Category Service), using Autofac, Automapper (not necessary in this Service but if it would be necessary, you would need to put in the TestInitialize method as you can see in the coment line in the following solution) and Entity Framework with Daniel J.G. help (thanks Daniel):
First of all I created a separated Test Project using MSTest (only because there is a lot of documentation about it).
Second you need to put the connection string for the Entity Framework where the test data is:
<connectionStrings>
<add name="DB" connectionString="Data Source=.\sqlexpress;Database=DBNAME;UID=DBUSER;pwd=DBPASSWORD;MultipleActiveResultSets=True;" providerName="System.Data.SqlClient" />
</connectionStrings>
In the < configuration > section after the < / configSections >
Third you create the class for the test:
namespace Heelp.Tests
{
[TestClass]
public class CategoryServiceIntegrationTest
{
// Respositories dependencies
private IRepository<Category> _categoryRepository;
private IRepository<Keyword> _keywordRepository;
private IRepository<KeywordAdCategory> _keywordAdCategoryRepository;
// Service under test: Category Service
private CategoryService _categoryService;
// Context under test: HeelpDB Connection String in app.config
private HeelpDbContext db;
[TestInitialize]
public void InitializeBeforeRunningATest()
{
// IoC dependencies registrations
AutofacConfig.RegisterDependencies();
// HERE YOU CAN CALL THE AUTOMAPPER CONFIGURE METHOD
// IN MY PROJECT I USE AutoMapperConfiguration.Configure();
// IT'S LOCATED IN THE App_Start FOLDER IN THE AutoMapperConfig.cs CLASS
// CALLED FROM GLOBAL.ASAX Application_Start() METHOD
// Database context initialization
db = new HeelpDbContext();
// Repositories initialization
_categoryRepository = new Repository<Category>(db);
_keywordRepository = new Repository<Keyword>(db);
_keywordAdCategoryRepository = new Repository<KeywordAdCategory>(db);
// Service initialization
_categoryService = new CategoryService(_categoryRepository,
_keywordRepository,
_keywordAdCategoryRepository);
}
[TestCleanup]
public void CleanDatabaseResources()
{
// Release the Entity Framework Context for other tests that will create a fresh new context.
// With this method, we will make sure that we have a fresh service and repositories instances on each test.
db.Dispose();
}
[TestMethod]
public void Home_SearchCategories_Test()
{
// Arrange
var keywords = "audi";
// Act (the _categoryService instance was created in the initialize method)
var result = _categoryService.SearchCategories(keywords);
// Assert
Assert.IsTrue(result.Count > 0);
}
}
}
Now you just have to run the test to see if it passes.
To garantee integration tests, I would recomend a second database identical from the original/production database in terms of tables, but with only your test data.
This will ensure that the tests results will remain the same based on your test data.
The only drawback is that you will need to keep sincronized the tables, but you can use SQL Admin Studio Freeware Tool from Simego to achieve that.
Regards.
Currently I'm developing an OAuth2 authorization server using DotNetOpenAuth CTP version. My authorization server is in asp.net MVC3, and it's based on the sample provided by the library. Everything works fine until the app reaches the point where the user authorizes the consumer client.
There's an action inside my OAuth controller which takes care of the authorization process, and is very similar to the equivalent action in the sample:
[Authorize, HttpPost, ValidateAntiForgeryToken]
public ActionResult AuthorizeResponse(bool isApproved)
{
var pendingRequest = this.authorizationServer.ReadAuthorizationRequest();
if (pendingRequest == null)
{
throw new HttpException((int)HttpStatusCode.BadRequest, "Missing authorization request.");
}
IDirectedProtocolMessage response;
if (isApproved)
{
var client = MvcApplication.DataContext.Clients.First(c => c.ClientIdentifier == pendingRequest.ClientIdentifier);
client.ClientAuthorizations.Add(
new ClientAuthorization
{
Scope = OAuthUtilities.JoinScopes(pendingRequest.Scope),
User = MvcApplication.LoggedInUser,
CreatedOn = DateTime.UtcNow,
});
MvcApplication.DataContext.SaveChanges();
response = this.authorizationServer.PrepareApproveAuthorizationRequest(pendingRequest, User.Identity.Name);
}
else
{
response = this.authorizationServer.PrepareRejectAuthorizationRequest(pendingRequest);
}
return this.authorizationServer.Channel.PrepareResponse(response).AsActionResult();
}
Everytime the program reaches this line:
this.authorizationServer.Channel.PrepareResponse(response).AsActionResult();
The system throws an exception which I have researched with no success. The exception is the following:
Only parameterless constructors and initializers are supported in LINQ to Entities.
The stack trace: http://pastebin.com/TibCax2t
The only thing I've done differently from the sample is that I used entity framework's code first approach, an I think the sample was done using a designer which autogenerated the entities.
Thank you in advance.
If you started from the example, the problem Andrew is talking about stays in DatabaseKeyNonceStore.cs. The exception is raised by one on these two methods:
public CryptoKey GetKey(string bucket, string handle) {
// It is critical that this lookup be case-sensitive, which can only be configured at the database.
var matches = from key in MvcApplication.DataContext.SymmetricCryptoKeys
where key.Bucket == bucket && key.Handle == handle
select new CryptoKey(key.Secret, key.ExpiresUtc.AsUtc());
return matches.FirstOrDefault();
}
public IEnumerable<KeyValuePair<string, CryptoKey>> GetKeys(string bucket) {
return from key in MvcApplication.DataContext.SymmetricCryptoKeys
where key.Bucket == bucket
orderby key.ExpiresUtc descending
select new KeyValuePair<string, CryptoKey>(key.Handle, new CryptoKey(key.Secret, key.ExpiresUtc.AsUtc()));
}
I've resolved moving initializations outside of the query:
public CryptoKey GetKey(string bucket, string handle) {
// It is critical that this lookup be case-sensitive, which can only be configured at the database.
var matches = from key in db.SymmetricCryptoKeys
where key.Bucket == bucket && key.Handle == handle
select key;
var match = matches.FirstOrDefault();
CryptoKey ck = new CryptoKey(match.Secret, match.ExpiresUtc.AsUtc());
return ck;
}
public IEnumerable<KeyValuePair<string, CryptoKey>> GetKeys(string bucket) {
var matches = from key in db.SymmetricCryptoKeys
where key.Bucket == bucket
orderby key.ExpiresUtc descending
select key;
List<KeyValuePair<string, CryptoKey>> en = new List<KeyValuePair<string, CryptoKey>>();
foreach (var key in matches)
en.Add(new KeyValuePair<string, CryptoKey>(key.Handle, new CryptoKey(key.Secret, key.ExpiresUtc.AsUtc())));
return en.AsEnumerable<KeyValuePair<string,CryptoKey>>();
}
I'm not sure that this is the best way, but it works!
It looks like your ICryptoKeyStore implementation may be attempting to store CryptoKey directly, but it's not a class that is compatible with the Entity framework (due to not have a public default constructor). Instead, define your own entity class for storing the data in CryptoKey and your ICryptoKeyStore is responsible to transition between the two data types for persistence and retrieval.