I'm using the Beta 2 version of Visual Studio 2010 to get a head start on learning to use WF4, and have run into a problem with persistence. In the code below, if I use the commented out creattion of a WorkflowApplication object, persistence works fine. If I use the un-commented creation below, where I pass a dictionary for arguments I want to pass in, then persistence breaks. Any ideas why this may be, and how to fix it?
List<Approver> approversRequired = new List<Approver>();
approversRequired.Add(new Approver("Dept Manager"));
approversRequired.Add(new Approver("Center Manager"));
Dictionary<String, Object> wfArguments = new Dictionary<string, object>();
wfArguments.Add("ApproversRequired", approversRequired);
//WorkflowApplication workflowApp = new WorkflowApplication(
// new WebCARSWorkflow());
WorkflowApplication workflowApp = new WorkflowApplication(
new WebCARSWorkflow(), wfArguments);
InstanceStore instanceStore = new SqlWorkflowInstanceStore(
#"Data Source=.\SQLEXPRESS;Integrated Security=True;Initial Catalog=WorkflowInstanceStore");
InstanceView view = instanceStore.Execute(
instanceStore.CreateInstanceHandle(), new CreateWorkflowOwnerCommand(),
TimeSpan.FromSeconds(30));
instanceStore.DefaultInstanceOwner = view.InstanceOwner;
workflowApp.InstanceStore = instanceStore;
workflowApp.PersistableIdle = (waie) => PersistableIdleAction.Unload;
workflowApp.Run();
WorkflowGuid.Text = workflowApp.Id.ToString();
workflowApp.ResumeBookmark("RequestSubmitted", "Submitted");
Is the Approver you pass in as a parameter decorated with the Serializable or the DataContract attribute?
You can see persitence errors using the Aborted callback
workflowApp.Aborted = e => Console.WriteLine(e.Reason);
Related
I have some integration tests that use a real database targetting a ConfigurationDbContext. When upgrading to Duende IdentityServer 6.0, the constructor for ConfigurationDbContext breaks (only accepts 1 arg instead of 2) because of the DbContext connection pooling feature that was added.
This code breaks:
public static ConfigurationDbContext GetConfigurationDbContext()
{
var connectionString = Configuration.GetConnectionString("ConfigurationDbContext");
var builder = new DbContextOptionsBuilder<ConfigurationDbContext>();
builder.UseSqlServer(connectionString);
var options = new ConfigurationStoreOptions
{
DefaultSchema = Schema.IdSrv
};
return new ConfigurationDbContext(builder.Options, options);
}
So I changed it to:
return new ConfigurationDbContext(builder.Options);
Now I can build, but my tests fail with this error:
Unable to resolve service for type 'Duende.IdentityServer.EntityFramework.Options.ConfigurationStoreOptions'
How am I supposed to pass the ConfigurationStoreOptions in? Looking at the code in Github, it looks like it relies on dependency injection. (Getting the options from services collection).
OK, I figured out my own problem, but I had to hunt and peck around. It is not listed as a breaking change in the upgrade documentation:
https://docs.duendesoftware.com/identityserver/v6/upgrades/v5.2_to_v6.0/
The solution is to upgrade your project to 6.1
<PackageReference Include="Duende.IdentityServer.EntityFramework.Storage" Version="6.1.5" />
Then you can use this code instead (StoreOptions has been made a public set property)
public static ConfigurationDbContext GetConfigurationDbContext()
{
var connectionString = Configuration.GetConnectionString("MyIdentity");
var builder = new DbContextOptionsBuilder<ConfigurationDbContext>();
builder.UseSqlServer(connectionString);
var options = new ConfigurationStoreOptions
{
DefaultSchema = Schema.IdSrv
};
var dbContext = new ConfigurationDbContext(builder.Options);
dbContext.StoreOptions = options;
return dbContext;
}
This will work for ConfigurationDbContext and PersistedGrantDbContext.
I'm doing a bit of a technical investigation into the ExecuteTransactionRequest. It's not something I've ever used before so I knocked up a very quick experiment just to see how it works. However, when sending off the request the OrganizationService is throwing back a FaultException (below). What I believe is happening is that my version of CRM doesn't support that OrganizationRequest. Although I'm pretty sure I have the right assemblies and version.
Can anyone please shed some light on what I'm missing?
CRM Deployment Version: 7.0.1.129
Organization Version: 7.0.2.53
Microsoft.Xrm Assembly Version: 7.0.0.0 (Also happened with 8.0.0.0)
An unhandled exception of type 'System.ServiceModel.FaultException'
occurred in Microsoft.Xrm.Sdk.dll
Additional information: The formatter threw an exception while trying
to deserialize the message: There was an error while trying to
deserialize parameter
http://schemas.microsoft.com/xrm/2011/Contracts/Services:request. The
InnerException message was 'Error in line 1 position 451. Element
'http://schemas.microsoft.com/xrm/2011/Contracts/Services:request'
contains data from a type that maps to the name
'http://schemas.microsoft.com/xrm/2011/Contracts:ExecuteTransactionRequest'.
The deserializer has no knowledge of any type that maps to this name.
Consider changing the implementation of the ResolveName method on your
DataContractResolver to return a non-null value for name
'ExecuteTransactionRequest' and namespace
'http://schemas.microsoft.com/xrm/2011/Contracts'.'. Please see
InnerException for more details.
CrmConnection connection = CrmConnection.Parse(GetCrmConnectionString("unmanaged"));
IOrganizationService orgService = new OrganizationService(connection);
ExecuteTransactionRequest transactionRequest = new ExecuteTransactionRequest()
{
ReturnResponses = true,
Requests = new OrganizationRequestCollection()
};
Entity newContact = new Entity("contact");
newContact["firstname"] = "Stack";
newContact["lastname"] = "Overflow";
CreateRequest createRequest = new CreateRequest()
{
Target = newContact
};
transactionRequest.Requests.Add(createRequest);
ExecuteTransactionResponse transactionResponse = (ExecuteTransactionResponse)orgService.Execute(transactionRequest);
Update
Quick look at your code, looked like it was because of the CreateRequest not being added to the collection. After your comments and double checking the crm organization version, you are on CRM 2015 (not on update 1). ExecuteTransactionRequest is only supported by CRM 2015 update 1 (version 7.1.XXX) and up (version 8.0.XXX) organizations. So unfortunately, your query won't work until at least the 2015 update is applied to the organization.
You did not add your create request to the ExecuteTransactionRequest - Requests collection. An empty request collection is causing the exceptions most likely.
ExecuteTransactionRequest transactionRequest = new ExecuteTransactionRequest()
{
ReturnResponses = true,
Requests = new OrganizationRequestCollection()
};
Entity newContact = new Entity("contact");
newContact["firstname"] = "Stack";
newContact["lastname"] = "Overflow";
CreateRequest createRequest = new CreateRequest()
{
Target = newContact
};
transactionRequest.Requests.Add(createRequest); //missing
ExecuteTransactionResponse transactionResponse = (ExecuteTransactionResponse)orgService.Execute(transactionRequest);
My specific problem is how can I automate "add-migration" in a build process for the Entity Framework. In researching this, it seems the mostly likely approach is something along the lines of automating these steps
Open a solution in Visual Studio 2013
Execute "Add-Migration blahblah" in the Package Manager Console (most likely via an add-in vsextention)
Close the solution
This initial approach is based on my own research and this question, the powershell script ultimately behind Add-Migration requires quite a bit of set-up to run. Visual Studio performs that setup automatically when creating the Package Manager Console and making the DTE object available. I would prefer not to attempt to duplicate that setup outside of Visual Studio.
One possible path to a solution is this unanswered stack overflow question
In researching the NuGet API, it does not appear to have a "send this text and it will be run like it was typed in the console". I am not clear on the lines between Visual Studio vs NuGet so I am not sure this is something that would be there.
I am able to find the "Pacakage Manager Console" ironically enough via "$dte.Windows" command in the Package Manager Console but in a VS 2013 window, that collection gives me objects which are "Microsoft.VisualStudio.Platform.WindowManagement.DTE.WindowBase". If there is a way stuff text into it, I think I need to get it to be a NuGetConsole.Implementation.PowerConsoleToolWindow" through reviewing the source code I am not clear how the text would stuffed but I am not at all familiar with what I am seeing.
Worst case, I will fall back to trying to stuff keys to it along the lines of this question but would prefer not to since that will substantially complicate the automation surrounding the build process.
All of that being said,
Is it possible to stream commands via code to the Package Manager Console in Visual Studio which is fully initialized and able to support an Entity Framework "add-migration" command?
Thanks for any suggestions, advice, help, non-abuse in advance,
John
The approach that worked for me was to trace into the entity framework code starting in with the AddMigrationCommand.cs in the EntityFramework.Powershell project and find the hooks into the EntityFramework project and then make those hooks work so there is no Powershell dependency.
You can get something like...
public static void RunIt(EnvDTE.Project project, Type dbContext, Assembly migrationAssembly, string migrationDirectory,
string migrationsNamespace, string contextKey, string migrationName)
{
DbMigrationsConfiguration migrationsConfiguration = new DbMigrationsConfiguration();
migrationsConfiguration.AutomaticMigrationDataLossAllowed = false;
migrationsConfiguration.AutomaticMigrationsEnabled = false;
migrationsConfiguration.CodeGenerator = new CSharpMigrationCodeGenerator(); //same as default
migrationsConfiguration.ContextType = dbContext; //data
migrationsConfiguration.ContextKey = contextKey;
migrationsConfiguration.MigrationsAssembly = migrationAssembly;
migrationsConfiguration.MigrationsDirectory = migrationDirectory;
migrationsConfiguration.MigrationsNamespace = migrationsNamespace;
System.Data.Entity.Infrastructure.DbConnectionInfo dbi = new System.Data.Entity.Infrastructure.DbConnectionInfo("DataContext");
migrationsConfiguration.TargetDatabase = dbi;
MigrationScaffolder ms = new MigrationScaffolder(migrationsConfiguration);
ScaffoldedMigration sf = ms.Scaffold(migrationName, false);
}
You can use this question to get to the dte object and from there to find the project object to pass into the call.
This is an update to John's answer whom I have to thank for the "hard part", but here is a complete example which creates a migration and adds that migration to the supplied project (project must be built before) the same way as Add-Migration InitialBase -IgnoreChanges would:
public void ScaffoldedMigration(EnvDTE.Project project)
{
var migrationsNamespace = project.Properties.Cast<Property>()
.First(p => p.Name == "RootNamespace").Value.ToString() + ".Migrations";
var assemblyName = project.Properties.Cast<Property>()
.First(p => p.Name == "AssemblyName").Value.ToString();
var rootPath = Path.GetDirectoryName(project.FullName);
var assemblyPath = Path.Combine(rootPath, "bin", assemblyName + ".dll");
var migrationAssembly = Assembly.Load(File.ReadAllBytes(assemblyPath));
Type dbContext = null;
foreach(var type in migrationAssembly.GetTypes())
{
if(type.IsSubclassOf(typeof(DbContext)))
{
dbContext = type;
break;
}
}
var migrationsConfiguration = new DbMigrationsConfiguration()
{
AutomaticMigrationDataLossAllowed = false,
AutomaticMigrationsEnabled = false,
CodeGenerator = new CSharpMigrationCodeGenerator(),
ContextType = dbContext,
ContextKey = migrationsNamespace + ".Configuration",
MigrationsAssembly = migrationAssembly,
MigrationsDirectory = "Migrations",
MigrationsNamespace = migrationsNamespace
};
var dbi = new System.Data.Entity.Infrastructure
.DbConnectionInfo("ConnectionString", "System.Data.SqlClient");
migrationsConfiguration.TargetDatabase = dbi;
var scaffolder = new MigrationScaffolder(migrationsConfiguration);
ScaffoldedMigration migration = scaffolder.Scaffold("InitialBase", true);
var migrationFile = Path.Combine(rootPath, migration.Directory,
migration.MigrationId + ".cs");
File.WriteAllText(migrationFile, migration.UserCode);
var migrationItem = project.ProjectItems.AddFromFile(migrationFile);
var designerFile = Path.Combine(rootPath, migration.Directory,
migration.MigrationId + ".Designer.cs");
File.WriteAllText(designerFile, migration.DesignerCode);
var designerItem = project.ProjectItems.AddFromFile(migrationFile);
foreach(Property prop in designerItem.Properties)
{
if (prop.Name == "DependentUpon")
prop.Value = Path.GetFileName(migrationFile);
}
var resxFile = Path.Combine(rootPath, migration.Directory,
migration.MigrationId + ".resx");
using (ResXResourceWriter resx = new ResXResourceWriter(resxFile))
{
foreach (var kvp in migration.Resources)
resx.AddResource(kvp.Key, kvp.Value);
}
var resxItem = project.ProjectItems.AddFromFile(resxFile);
foreach (Property prop in resxItem.Properties)
{
if (prop.Name == "DependentUpon")
prop.Value = Path.GetFileName(migrationFile);
}
}
I execute this in my project template's IWizard implementation where I run a migration with IgnoreChanges, because of shared entites with the base project. Change scaffolder.Scaffold("InitialBase", true) to scaffolder.Scaffold("InitialBase", false) if you want to include the changes.
I'm sending an email from my ASP.NET MVC app using the Spark View Engine based on this example by Andrew Kharlamov.
I've setup a unit test, CanSendEmail, but I need to specify the viewfolder in the config.
I found the documentation here and the examples give this:
<spark>
<views>
<add name="{any-unique-name}"
folderType="FileSystem|EmbeddedResource|VirtualPathProvider|Custom"
type="{name, assembly of IViewFolder type}"
constuctor-param-names="values"
subfolder="{optional subfolder to target}"/>
</views>
</spark>
My question is this. Which folderType do I use and do I need any other parameters. My test product is call myProject.Tests and my web project containing the views is called myProject.Web with a Views folder in it.
Do I use FileSystem, VirtualPathProvider ... ?
Edit [14/11/2011]:
Okay I've got this in my app.config in myProject.Tests:
<views>
<add name="web-view-folder"
folderType="VirtualPathProvider"
virtualBaseDir="~/Views"/>
</views>
I still get "View source file not found." when I run my test. I want the test to use the Views in myproject.Web.
My Solution
Based on the blog posts here and here, and with help from #RobertTheGrey and looking at the tests in the Spark source code, I ended up using ViewFolderType.FileSystem. That worked.
Here's the my code under test:
public string RenderEmailWithCustomViewFolder(string sparkViewName, ViewDataDictionary viewData, Dictionary<string, string> viewFolderParameters)
{
var settings = new SparkSettings()
.SetPageBaseType(typeof (SparkView))
.AddViewFolder(ViewFolderType.FileSystem, viewFolderParameters)
.AddAssembly("MvcContrib");
var engine = new SparkViewEngine(settings);
var sparkViewDescriptor = new SparkViewDescriptor().AddTemplate(sparkViewName);
var view = (SparkView)engine.CreateInstance(sparkViewDescriptor);
try
{
// Merge view data
viewData.Keys.ToList().ForEach(x => view.ViewData[x] = viewData[x]);
// Render the view to a text writer
var writer = new StringWriter();
view.RenderView(writer);
return writer.ToString();
}
finally
{
engine.ReleaseInstance(view);
}
}
And here's my test:
[Test]
public void Can_Render_Order_Confirmation_Email_With_Spark_View_Engine()
{
// Arrange
var order = OrderInstanceFactory.CreateTestOrder();
order.ContactEmail = "test#testicle.com";
var emailService = new EmailService();
var viewData = new ViewDataDictionary();
viewData["Order"] = order;
const string viewFolder = #"../../../../app/myProject.Web/Views";
var viewFolderParameters = new Dictionary<string, string> {{"basePath", viewFolder}};
// Act
var emailBody = emailService.RenderEmailWithCustomViewFolder("Email/OrderConfirmation.spark", viewData, viewFolderParameters);
// Assert
Assert.IsNotNull(emailBody);
Assert.IsTrue(emailBody.Contains("test#testicle.com"));
}
My OrderConfirmation.spark template lives in my web products in the Views/Email/.
If it's an ASP.NET MVC app, then you can use VirtualPathProvider since that hooks into the HttpContext and the rest of the runtime. You would use a FileSystemProvider if you were runnig it from a console app for example, or if you wanted to add a folder from outside your web app, perhaps because the templates were shared by other apps, but I've rarely seen that done.
Hope that helps...
I am trying to create a new contact using Dynamic Entity. The sample i found in CRM SDK had this code.
// Set the properties of the contact using property objects.
StringProperty firstname = new StringProperty();
firstname.Name = "firstname";
firstname.Value = "Jesper";
StringProperty lastname = new StringProperty();
lastname.Name = "lastname";
lastname.Value = "Aaberg";
// Create the DynamicEntity object.
DynamicEntity contactEntity = new DynamicEntity();
// Set the name of the entity type.
contactEntity.Name = EntityName.contact.ToString();
// Set the properties of the contact.
contactEntity.Properties = new Property[] {firstname, lastname};
In my code i have the following implementation.
StringProperty sp_Field1 = new StringProperty("Field1","Value1");
StringProperty sp_Field2 = new StringProperty("Field2","Value1");
CrmService service = new CrmService();
service.Credentials = System.Net.CredentialCache.DefaultCredentials;
// Create the DynamicEntity object.
DynamicEntity contactEntity = new DynamicEntity();
// Set the name of the entity type.
contactEntity.Name = EntityName.contact.ToString();
// Set the properties of the contact.
contactEntity.Properties = new Property[] {sp_Field1,sp_Field2};
I don't see much differences in the code. In the examples i found in the internet i have the same implementation as i found in SDK. But if i run the same i get the following error
CS0029: Cannot implicitly convert type 'Microsoft.Crm.Sdk.StringProperty' to 'Microsoft.Crm.Sdk.PropertyCollection'
I tried created a new variable of type PropertyCollection(one that belongs in mscrm namespace) and added the stringpropertys into that and passed it to the entity.
Microsoft.Crm.Sdk.PropertyCollection propTest = new Microsoft.Crm.Sdk.PropertyCollection();
propTest.Add(sp_SSNNo);
propTest.Add(sp_FirstName);
contactEntity.Properties = new Property[] {propTest};
This gave me the following error
CS0029: Cannot implicitly convert type 'Microsoft.Crm.Sdk.PropertyCollection' to 'Microsoft.Crm.Sdk.Property'
I am sure its a minor typecasting error but i am not able to figure out where the error is. And moreover, even if it was a typecasting error why is it working for all the samples given in the internet and not for me. I tried getting the code sample to run but i am encountering the same conversion error. Please let me know if you need more info on this, any help on this would be appreciated.
Here is an article from Microsoft that makes an attempt to discuss this topic:
http://community.dynamics.com/blogs/cscrmblog/archive/2008/06/23/web-services-amp-dlls-or-what-s-up-with-all-the-duplicate-classes.aspx
This is not a bug that you are running into but more of a difference in design between the way the two assemblies work and what they are designed to do.
If you want to continue to use the Microsoft.Crm.Sdk.dll you should be able to accomplish your goal with the following...
StringProperty sp_Field1 = new StringProperty("Field1","Value1");
StringProperty sp_Field2 = new StringProperty("Field2","Value1");
CrmService service = new CrmService();
service.Credentials = System.Net.CredentialCache.DefaultCredentials;
// Create the DynamicEntity object.
DynamicEntity contactEntity = new DynamicEntity();
// Set the name of the entity type.
contactEntity.Name = EntityName.contact.ToString();
// Set the properties of the contact.
PropertyCollection properties = new PropertyCollection();
properties.Add(sp_Field1);
contactEntity.Properties = properties;
Thanks SaaS Developer, that code is working fine now. One more way of doing it would be to directly add the StringProperty to the entity property collection.
contactEntity.Properties.Add(sp_SSNNo);
Thanks again for replying :)
I believe the issue is that you are referencing the dynamic entity class in the Microsoft.Crm.Sdk assembly. The sample in the SDK is using a reference to the CRM web service. This can get confusing as both assemblies contain many of the same types, however they are different.