Microsoft Dynamis plugin - crm

How is life...
I'm new to Microsof Dynamics...
Can anyone tell me why this code work OK for the account number and do nothing for the revenu... won't write the value in the field ???
using System;
// Microsoft Dynamics CRM namespace(s)
using Microsoft.Xrm.Sdk;
namespace Microsoft.Crm.Sdk.Samples
{
public class accountManager : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
// Obtain the execution context from the service provider.
Microsoft.Xrm.Sdk.IPluginExecutionContext context = Microsoft.Xrm.Sdk.IPluginExecutionContext)
serviceProvider.GetService(typeof(Microsoft.Xrm.Sdk.IPluginExecutionContext));
// The InputParameters collection contains all the data passed in the message request.
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
{
// Obtain the target entity from the input parameters.
Entity entity = (Entity)context.InputParameters["Target"];
switch(entity.LogicalName)
{
case "account":
int accountNumber;
accountNumber = 100;
entity.Attributes.Add("accountnumber", accountNumber.ToString());
int accountRevenu;
accountRevenu = 1000;
entity.Attributes.Add("revenue", accountRevenu.ToString());
break;
}
}
}
}
}
Best Regards
Steph

Revenue is "currency" field so you should try using following code:
entity["revenue"] = new Money(1000);

Related

Mono.Cecil AddInterfaceImplementation equivalent?

I am working on Mono.Cecil codegen util, and I want to preform following operation:
Loop through types
If type contains X attribute:
- Add ITestInterface implementation (where ITestInterface has defined some methods)
// For reference
public interface ITestInterface
{
void Something();
int IntSomething();
}
// Expected result, if type contains X attribute:
// Before codegen:
[X]
public class CodeGenExample
{
}
// After codegen
[X]
public class CodeGenExample : ITestInterface
{
public void Something()
{
// some stuff
}
public int IntSomething()
{
// do some stuff
return 0;
}
}
I have seen that .NET Reflection has a AddInterfaceImplementation method (https://learn.microsoft.com/pl-pl/dotnet/api/system.reflection.emit.typebuilder.addinterfaceimplementation?view=net-5.0).
Is there a Mono.Cecil equivalent or a workaround for this & how to use it?
That can be achieved by:
Iterating over all types defined in the assembly
Checking which types have the attribute applied to
Injecting the methods.
As an example you can do something like:
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace inject
{
interface IMyInterface
{
int Something();
}
class MarkerAttribute : Attribute {}
[Marker]
class Foo
{
}
class Program
{
static void Main(string[] args)
{
if (args.Length == 1)
{
using var a = AssemblyDefinition.ReadAssembly(typeof(Program).Assembly.Location);
var interfaceToImplement = a.MainModule.GetType("inject.IMyInterface");
foreach(var t in a.MainModule.Types)
{
if (t.HasCustomAttributes && t.CustomAttributes.Any(c => c.Constructor.DeclaringType.Name == "MarkerAttribute"))
{
System.Console.WriteLine($"Adding methods to : {t}");
var something = new MethodDefinition("Something", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, a.MainModule.TypeSystem.Int32);
something.Body = new Mono.Cecil.Cil.MethodBody(something);
var il = something.Body.GetILProcessor();
il.Emit(OpCodes.Ldc_I4, 42);
il.Emit(OpCodes.Ret);
t.Methods.Add(something);
// Add the interface.
t.Interfaces.Add(new InterfaceImplementation(interfaceToImplement));
var path = typeof(Program).Assembly.Location + ".new";
a.Write(path);
System.Console.WriteLine($"Modified version written to {path}");
}
}
}
else
{
object f = new Foo();
IMyInterface itf = (IMyInterface) f;
System.Console.WriteLine($"Something() == {itf.Something()}");
}
}
}
}
Another potential solution is to have the methods implemented in an internal class and copying over their method bodies.
As a side note, these are 2 online tools you can use to explore/learn more about CIL, Mono.Cecil, C#:
Sharplab.io
Cecilifier (disclaimer: I'm the author of this one)
That being said if you can use C# 9.0 you may be able to leverage the new Source Generators feature.

JsonConverter and Swashbuckle - Approach for decorating a swagger

I'm playing around and developed a simple custom JsonConverter that takes a min and max temperature and have decorated my model class as follows and validates that the temperature falls in that range.
[JsonConverter(typeof(TemperatureConverter), 5, 10)]
public int Temperature { get; set; }
This is all good but I'm wondering what's the approach to best output the correct decoration in my swagger file generated by swashbuckle... like so:
name: Temperature
schema:
type: integer
minimum: 5
maximum: 10
I know this is a trivial example, but it's more the approach to tying JsonConverter to the generation of the swagger I'm interested in.
I'm currently looking at ISchemaFilter but can't see how I can get the type of converter that decorates the property.
Thanks
You have to be at the parent schema level, looking at it's properties. By the time it gets to the property itself, it is too late, as there is no link back to the parent class.
I was using a custom attribute, not JsonConverter, but something like this should work for detecting the attribute.
public class TemperatureSchemaFilter : ISchemaFilter
{
public void Apply(Schema schema, SchemaFilterContext context)
{
var converterProperties = context.SystemType.GetProperties().Where(
prop => prop.CustomAttributes.Select(
attr => attr.AttributeType == typeof(JsonConverterAttribute)).Any()
).ToList();
foreach (var converterProperty in converterProperties)
{
var converterAttribute = (JsonConverterAttribute)Attribute.GetCustomAttribute(converterProperty.PropertyType, typeof(JsonConverterAttribute));
if (converterAttribute.ConverterType != typeof(TemperatureConverter)) continue;
Schema propertySchema = null;
try
{
propertySchema = schema.Properties.First(x => x.Key.ToLower().Equals(converterProperty.Name.ToLower())).Value;
}
catch (Exception)
{
continue;
}
if (propertySchema == null) continue;
propertySchema.Minimum = (double) converterAttribute.ConverterParameters[0];
propertySchema.Maximum = (double) converterAttribute.ConverterParameters[1];
}
}
}
Unfortunately my environment is currently hosed, so I can't test it out, but I think this is the way to go.

c# how to make a dynamic Context, to access different databases?

I am trying to access a different context with one variable. Please have a look at the code before:
...
private readonly ClientOneType _contextClientOne;
private readonly ClientTwoType _contextClientTwo;
public ExampleService()
{
_contextClientOne = new ClientOneType();
_contextClientTwo = new ClientTwoType();
}
public Stores[] GetStores(Store storeModel)
{
try
{
var _dynamicContext = null; //this throws an error because c# needs a type for runtime.
if (client == "OutBack")
_dynamicContext = _contextClientOne;
else if(client == "DollarGeneral")
_dynamicContext = _contextClientTwo;
var stores = (from s in _dynamicContext.Store //this is where the magic should take place
where s.StoreName == storeModel.StoreName
select p).ToArray();
return stores;
}
...
}
I get an error when running this because _dynamicContext can not be null so how can i create a variable that can be changed into different contexts?
The lazy solution would be to create different methods for each client, but that wouldn't be very effective as it will become unmaintainable.
I will really appreciate the help. Thank you in advance.
public interface IClientType
{
public Store Store { get; }
}
public class ClientOneType : IClientType
{
...
}
public class ClientTwoType : IClientType
{
...
}
public Stores[] GetStores(Store storeModel)
{
try
{
IClientType _dynamicContext = null;
...
Do ClientOneType and ClientTwoType both derrive from a base class that exposes the property named "Store" ?
I'm guessing they do not, and since they do not, there is no way to use the same variable to write the LINQ query you are writing because the compiler has to be able to determine what properties are available.
however, you could use IQueryable to dynamically build the query
IQueryable<Stores> storeQry=null;
if (client == "Walmart")
storeQry= _contextClientOne.Store.AsQueryable();
else if(client == "CHS")
storeQry= _contextClientTwo.Store.AsQueryable();
var stores = (from s in storeQry
where s.StoreName == storeModel.StoreName
select p).ToArray();

NativeActivity in XAML loaded COMPILED workflow throws Expression Activity type 'CSharpValue1' requires compilation in order to run

This is a know error when using C# expressions in windows workflow. The article at https://learn.microsoft.com/en-us/dotnet/framework/windows-workflow-foundation/csharp-expressions#CodeWorkflows explains the reason and how to fix it. It all works fine for me in standard workflows, but as soon as I add a custom NativeActivity to the WF, I get that same error again !
Below the code of how I load the XAML workflow and the simple NativeActivity (which is the ONLY activity in the test workflow and inside that activity is a simple assign expression).
Loading and invoking WF via XAML:
`XamlXmlReaderSettings settings = new XamlXmlReaderSettings()
{
LocalAssembly = GetContextAssembly()
};
XamlReader reader = reader = ActivityXamlServices.CreateReader(new XamlXmlReader(fileURL, settings));
ActivityXamlServicesSettings serviceSettings = new ActivityXamlServicesSettings
{
CompileExpressions = true
};
var activity = ActivityXamlServices.Load(reader, serviceSettings);
WorkflowInvoker.Invoke(activity);`
Doing it in code throws same Exception:
Variable<string> foo = new Variable<string>
{
Name = "Foo"
};
Activity activity = new Sequence
{
Variables = { foo },
Activities =
{
new TimeExecuteUntilAborted
{
Activities =
{
new Assign<string>
{
To = new CSharpReference<string>("Foo"),
Value = new CSharpValue<string>("new Random().Next(1, 101).ToString()")
}
}
}
}
};
CompileExpressions(activity);//the method from the article mentioned above
WorkflowInvoker.Invoke(activity);
The Native Activity:
[Designer("System.Activities.Core.Presentation.SequenceDesigner, System.Activities.Core.Presentation")]
public sealed class TimeExecuteUntilAborted : NativeActivity
{
private Sequence innerSequence = new Sequence();
[Browsable(false)]
public Collection<Activity> Activities
{
get
{
return innerSequence.Activities;
}
}
[Browsable(false)]
public Collection<Variable> Variables
{
get
{
return innerSequence.Variables;
}
}
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
metadata.AddImplementationChild(innerSequence);
}
protected override void Execute(NativeActivityContext context)
{
context.ScheduleActivity(innerSequence);
}
}
Your TimeExecutedUntilAborted class seems to be the culprit. I was able to swap in one of my own template NativeActivities instead and your workflow executed fine with the expressions. I'm guessing that your class is causing an issue in the compiler method when it parses your code. I used this doc as an example for my NativeActivity: https://msdn.microsoft.com/en-us/library/system.activities.nativeactivity(v=vs.110).aspx.
Sizzle Finger's answer is no solution but pointed me into the right direction to simply check what is different. It came out that the simple call to the base class method was missing:
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
base.CacheMetadata(metadata); // !! This needs to be added
metadata.AddImplementationChild(innerSequence);
}

How can I enable logging in NVelocity?

Any idea how to do what the title says? Only thing I found was on the original Velocity site, and I don't think
ve.setProperty( RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS,
"org.apache.velocity.runtime.log.Log4JLogChute" );
ve.setProperty("runtime.log.logsystem.log4j.logger",
LOGGER_NAME);
will work wonderfully well on .NET. I am using log4net, which should make it quite easy, but the documentation on NVelocity is really a mess.
Implement NVelocity.Runtime.Log.ILogSystem (you could write a simple implementation that bridges to log4net) and set this impl type in the property RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS
How I got this information:
Get the code.
Search for "log" in the codebase
Discover the classes in NVelocity.Runtime.Log.
Read those classes' source, they're very simple and thoroughly documented.
Update:
Currently, NVelocity does not support logging. The initializeLogger() and Log() methods in RuntimeInstance Class are commented out.
If you need to log, uncomment the two methods, add a private ILogSystem logSystem; property
Here's our on-the-fly implementation:
public class RuntimeInstance : IRuntimeServices
{
private ILogSystem logSystem;
...
...
private void initializeLogger()
{
logSystem = LogManager.CreateLogSystem(this);
}
...
...
private void Log(LogLevel level, Object message)
{
String output = message.ToString();
logSystem.LogVelocityMessage(level, output);
}
...
}
Then, we implemented ILogSystem for log4net
using log4net;
using NVelocity.Runtime;
using NVelocity.Runtime.Log;
namespace Services.Templates
{
public class Log4NetILogSystem : ILogSystem
{
private readonly ILog _log;
public Log4NetILogSystem(ILog log )
{
_log = log;
}
public void Init(IRuntimeServices rs)
{
}
public void LogVelocityMessage(LogLevel level, string message)
{
switch (level)
{
case LogLevel.Debug:
_log.Debug(message);
break;
case LogLevel.Info:
_log.Info(message);
break;
case LogLevel.Warn:
_log.Warn(message);
break;
case LogLevel.Error:
_log.Error(message);
break;
}
}
}
}
Then, when creating the engine:
var engine = new VelocityEngine();
var props = new ExtendedProperties();
props.SetProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM,
new Log4NetILogSystem(LogManager.GetLogger(typeof(NVelocityEngine))));
engine.Init(props);

Resources