Generic InArgument throws an exception trying to get the value - c#-4.0

I have an Activity where I declared a InArgument without a type (because I want to know the type of the Expression at design time).
When I execute the activity I get this error in var contentTelegram line:
"The argument of type '<type>' cannot be used. Make sure that it is declared on an activity."
Here is my code:
public InArgument Content { get; set; }
protected override PlcMessage Execute(CodeActivityContext context)
{
try
{
var contentTelegram = Content.Get(context);
return new PlcMessage();
}
catch (Exception ex)
{
throw;
}
}

Here is what I did:
The workflow runtime needs to know about the used types in arguments, so the cacheMetadata is the key to make it work, CacheMetadata uses reflection to know about arguments, notice that only works for simple cases.
public sealed class MyActivity: CodeActivity
{
private RuntimeArgument outMyRuntimeArgument;
// Define an activity input argument of type string
public OutArgument MyUntypedArgument { get; set; }
protected override void CacheMetadata(CodeActivityMetadata metadata)
{
outMyArgument= new RuntimeArgument("MyUntypedArgument", MyUntypedArgument.ArgumentType, ArgumentDirection.Out);
metadata.Bind(MyUntypedArgument, outArgument);
metadata.AddArgument(outMyArgument);
}
protected override void Execute(CodeActivityContext context)
{
context.SetValue(outMyRuntimeArgument, Activator.CreateInstance(Type));
}
}

Related

Unable to use protected method override in Acumatica Graph Extension

I'm attempting to create a graph extension to modify the behavior of one of the mobile scan screens, and I found in developer release notes for 2020 R1 the ability to use the [PXProtectedAccess] attribute to utilize protected members of a Graph from the extension, even though it does not directly inherit from it.
However, in order to utilize this, the Graph Extension class needs to be abstract and Acumatica no longer seems to recognize it when I do so. I'm sure that I am missing a crucial piece here but I can't figure out what it is based on documentation. EDIT: I was missing the [PXProtectedAccess] attribute on the class itself.
Now I am seeing something else when I try to actually call the abstract method. It throws Unable to cast object of type 'Wrapper.PX.Objects.IN.Cst_INScanIssueHost' to type 'INScanIssueHostDynamicInterface'. when I attempt to call any one of these protected members. I'm not sure what INScanIssueHostDynamicInterface refers to or how to resolve the type conflicts here.
Here is an excerpt of the code I'm using:
[PXProtectedAccess]
public abstract class INScanIssue_Extension : PXGraphExtension<INScanIssue, INScanIssueHost>
{
[PXProtectedAccess]
protected abstract void ReportError(string errorMsg, params object[] args);
public delegate void ProcessConfirmDelegate();
[PXOverride]
public virtual void ProcessConfirm(ProcessConfirmDelegate baseMethod)
{
ReportError("TEST");
}
}
I think you are on the right path. Your graphExtension should be abstract. Also please note that on your extension you use protected member of the graph extension by specifying the parameter of the attribute, as shown below:
public class MyGraph : PXGraph<MyGraph>
{
protected void Bar() { }
}
public class MyExt : PXGraphExtension<MyGraph>
{
protected void Foo() { }
}
[PXProtectedAccess]
public abstract class MySecondLevelExt : PXGraphExtension<MyExt, MyGraph>
{
[PXProtectedAccess]
protected abstract void Bar();
[PXProtectedAccess(typeof(MyExt))]
protected abstract void Foo();
}
So in your case, I think you can try to add that parameter to the ProctectedAccess attribute for those members that from INScanIssue(or overriden there ):
namespace PX.Objects.IN
{
[PXProtectedAccess]
public abstract class INScanIssue_Extension : PXGraphExtension<INScanIssue,
INScanIssueHost>
{
public static bool IsActive()
{
return true;
}
# region Protected Access
*[PXProtectedAccess(typeof(INScanIssue))]*
protected abstract void ClearHeaderInfo(bool redirect = false);
[PXProtectedAccess]
protected abstract void SetScanState(string state, string message = null, params object[] args);
[PXProtectedAccess(typeof(INScanIssue))]
protected abstract bool PromptLocationForEveryLine { get; }
........................................
Use the abstract extension only to access the protected members, then add a second level extension, that calls the exposed members from your first level extension. And I don't think you need to apply the attribute on the extension.
public abstract class INScanIssueProtectedAccessExt : PXGraphExtension<INScanIssue, INScanIssueHost>
{
[PXProtectedAccess]
public abstract void ReportError(string errorMsg, params object[] args);
}
public class INScanIssue_Extension : PXGraphExtension<INScanIssueProtectedAccessExt, INScanIssue, INScanIssueHost>
{
public delegate void ProcessConfirmDelegate();
[PXOverride]
public virtual void ProcessConfirm(ProcessConfirmDelegate baseMethod)
{
this.Base2.ReportError("TEST");
}
}

How to apply AutoMapper ValueConverters against properties that may be null

I think this is an AutoMapper bug but the issue template they have in GitHub states to post something to SO first.
I want to be able to apply an IValueConverter without worrying about null exceptions.
As an example, I'm using a IValueConverter to apply some logic in multiple mappings:
public class ExampleConverter : IValueConverter<string, string>
{
public string Convert(string sourceMember, ResolutionContext context)
{
if (string.IsNullOrEmpty(sourceMember))
{
return string.Empty;
}
return sourceMember.ToUpper();
}
}
If I have the following types I'm mapping to and from:
public class ExampleSource
{
public ExampleNestedSource1 A { get; set; }
}
public class ExampleNestedSource1
{
public ExampleNestedSource2 B { get; set; }
}
public class ExampleNestedSource2
{
public string Input { get; set; }
}
public class ExampleDestination
{
public string Output { get; set; }
}
I can apply the converter like so:
public class ExampleProfile : Profile
{
public ExampleProfile()
{
this.CreateMap<ExampleSource, ExampleDestination>(MemberList.None)
.ForMember(dst => dst.Output, opt => opt.ConvertUsing(new ExampleConverter(), src => src.A.B.Input));
}
}
Using the converter like this however, throws an exception when A or B are null:
mapper.Map<ExampleDestination>(new ExampleSource // Throws null reference
{
A = new ExampleNestedSource1(),
});
mapper.Map<ExampleDestination>(new ExampleSource()); // Throws null reference
Because opt.ConvertUsing(new ExampleConverter(), src => src.A.B.Input) takes in an expression I can't use null propagating operators like src?.A?.B?.Input.
If I remove the converter and use MapFrom, the problem goes away:
public class ExampleProfile : Profile
{
public ExampleProfile()
{
this.CreateMap<ExampleSource, ExampleDestination>(MemberList.None)
.ForMember(dst => dst.Output, opt => opt.MapFrom(src => src.A.B.Input));
}
}
But MapFrom doesn't support IValueConverter it only supports IMemberValueResolver which is less reusable.
Is there a better way to handle this scenario?
If not, I'm thinking AutoMapper should either:
Add support for IValueConverter in the MapFrom method.
ConvertUsing should not invoke the given converter when the expression doesn't resolve to a property and not throw.
ConvertUsing should invoke the given converter with null when the expression doesn't resolve to a property (in the same way that MapFrom) and not throw.

How to use DI in this example?

I followed this example https://learn.microsoft.com/pt-br/azure/app-service/webjobs-sdk-get-started and it is working fine. What I want to do is to make the connection strings (strongly typed) available in all methods within Functions class. My Connection Strings object:
namespace MyApp.Domain
{
public class Secrets
{
public class ConnectionStrings
{
public string SqlServer {get; set;}
public string Storage {get; set;}
public string SendGrid {get; set;}
public string AzureWebJobsDashboard { get; set; }
public string AzureWebJobsStorage {get; set;}
}
}
}
In web project I use (and it works perfectly):
services.Configure<Secrets.ConnectionStrings>(Configuration.GetSection("CUSTOMCONNSTR_ConnectionStrings"));
and in the classes' constructors I use:
public class EmailController: ControllerBase
{
private readonly MyEmail _myEmail;
public EmailController(MyEmail MyEmail)
{
_myEmail = MyEmail;
}
[HttpGet]
public async Task<ActionResult<string>> SendEmail()
{
try
{
...
return await _myEmail.SendMailMI3D(myMsg);
}
catch (System.Exception ex)
{
return ex.Message + " - " + ex.StackTrace;
}
}
[HttpGet("sendgrid")]
public string GetSendGrid(long id)
{
return _myEmail.SendGridConnStr();
}
}
But this way doesn't work on webjobs (console apps).
I tried to insert a simple Console.WriteLine in Functions' constructor but it doesn't work as well. So I think this is the problem: Functions' constructor is not being called. So when I insert a message in my queue I receive this error message related to DI Connection String:
Microsoft.Azure.WebJobs.Host.FunctionInvocationException: Exception while executing function: Functions.ProcessQueueMessage ---> System.NullReferenceException: Object reference not set to an instance of an object.
Can anybody please help me? Thanks a lot.
public Functions(IOptions<Secrets.ConnectionStrings> ConnectionStrings)
{
_connectionStrings = ConnectionStrings;
Console.WriteLine("Simple line");
Console.WriteLine($"Functions constructor: ${_connectionStrings.Value.SendGrid}");
}
Microsoft.Azure.WebJobs.Host.FunctionInvocationException: Exception while executing function: Functions.ProcessQueueMessage ---> System.NullReferenceException: Object reference not set to an instance of an object.
Dependency Injection is available in WebJobs but you do need to take the extra step to create an IJobActivator to define the injection.
namespace NetCoreWebJob.WebJob
{
public class JobActivator : IJobActivator
{
private readonly IServiceProvider services;
public JobActivator(IServiceProvider services)
{
this.services = services;
}
public T CreateInstance<T>()
{
return services.GetService<T>();
}
}
}
Inside Main()
var config = new JobHostConfiguration();
config.JobActivator = new JobActivator(services.BuildServiceProvider());
That should allow the runtime to utilize the parameterized constructor.

NotMapped attribute keeps related property from saving?

I have a few properties that I don't have a direct mapping in the database for, so I'm using the convention of having another variable that is mapped to the database, and a public variable that will be used to do all of my actual work. The common one is [mapping a boolean property to a char column][1], but I also have a StatusID property whose C# enum is different based on the derived type.
My public property has the [NotMapped] attribute on it, and my internal property has the [Column] attribute. I think there's something that because the public property isn't mapped, it's keeping the other property from being mapped as well.
In my project, I start with an abstract base Message class:
[Table("tblMessage")]
public abstract class Message {
[Column("msgIsSample")]
[Required]
internal string dbIsSample { get; set; }
[Column("msgStatusID")]
internal int? dbStatusId { get; set; }
[NotMapped]
public bool IsSample {
get {
return dbIsSample.ToUpper() == "Y";
}
set {
dbIsSample = value ? "Y" : "N";
}
}
public Message() {
this.IsSample = false;
this.dbStatusId = null;
}
}
Right now I only have a single class implementing the base class, Request:
public class Request : Message {
[NotMapped]
public int Status {
get {
return this.dbStatusId.HasValue ? this.dbStatusId.Value : 1;
}
set {
this.dbStatusId = value;
}
}
public Request()
: base() {
this.Status = 1;
}
}
Here is my context:
public class MyContext : DbContext {
public DbSet<Message> Messages { get; set; }
static MyContext() {
Database.SetInitializer<MyContext>(null);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Entity<Message>()
.Map<Request>(m => m.Requires("msgTypeID").HasValue(1));
}
}
Is this something that anyone else has run across? I haven't been able to find anything about why this isn't working, even though this looks like the accepted convention until the EF team adds additional custom mapping. Someone else has to have run across this issue.
When I try to execute this code, I get a DbUpdateException saying that it can't insert a NULL into column "msgIsSample" due to my having set that in the table creation script. This doesn't make any sense because the msgIsSample is defaulted to have a "N".
Instead of making it internal, make it protected internal.
At runtime, EF will subclass your entity dynamically. These extended classes are called dynamic proxies.
EF cannot set your property because it does not have access. To give EF access to your property, it must have either public or protected access. You can still have internal properties, but give subclasses access by adding the protected modifier.
[Table("tblMessage")]
public abstract class Message {
[Column("msgIsSample")]
[Required]
public string dbIsSample { get; protected internal set; }
[Column("msgStatusID")]
public int? dbStatusId { get; protected internal set; }

MEF's GetExports<T, TMetadataView>() fails to find composed parts in the CompositionContainer

When attempting to load an instantiated export with GetExports() (using a LINQ query described below), the method returns null. I notice that when I call GetExports without the LINQ query, the return value is Count: 0. This would indicate to me that MEF is failing to find any exports that have been composed in the container. I can see the ExportDefinition, however, when looking at Container.Catalog.Parts.ExportDefinitions. Any ideas on where I am going wrong? Everything up until the query seems to be working fine.
I have the following contract and metadata view declared and implemented:
public interface IMap
{
void Init();
int ParseData();
}
public interface IMapMetadata
{
string MapName { get; }
string DocumentType { get; }
}
[Export(typeof(IMap))]
[ExportMetadata("MapName", "Map")]
public class Map
{
public Map()
{
}
}
I am using the following code to load a directory that contains DLLs that satisfy this contract with:
public void LoadByDirectory(string zPath)
{
try
{
_catalog.Catalogs.Add(new DirectoryCatalog(zPath));
}
catch (Exception e)
{
String zErrMess = e.Message;
}
}
Using a LINQ query to get an export:
public IMap GetMapInstance(string zMapName)
{
IMap ndeMap;
_container = new CompositionContainer(_catalog);
_container.ComposeParts(this);
try
{
ndeMap = _container.GetExports<IMap, IMapMetadata>()
.Where(p => p.Metadata.MapName.Equals(zMapName))
.Select(p => p.Value)
.FirstOrDefault();
}
catch (Exception ex)
{
throw new Exception("Failed to load map " + zMapName + ": " + ex.Message, ex);
}
return ndeMap;
}
Calling the above method like this:
IMap map = mapFactory.GetMapInstance("Map");
returns null.
UPDATED
In addition to the answer below, I was forgetting to declare the interface on the map class, this resolves the issue (note I removed the DocumentType property):
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class MapExportAttribute : ExportAttribute, IMapMetadata
{
public MapExportAttribute()
: base(typeof(IMap))
{
}
public string MapName { get; set; }
}
[MapExport(MapName="Map")]
public class Map : IMap
{
public Map()
{
}
public void Init()
{
throw new NotImplementedException();
}
public int ParseData()
{
throw new NotImplementedException();
}
}
It looks like you're missing the DocumentType meta-data on your export:
[Export(typeof(IMap))]
[ExportMetadata("MapName", "Map")]
[ExportMetadata("DocumentType", "???")]
public class Map
{
}
The simplest way to ensure you specify the correct meta-data is a custom export attribute:
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class MapExportAttribute : ExportAttribute, IMapMetadata
{
public MapExportAttribute() : base(typeof(IMap))
{
}
public string MapName { get; set; }
public string DocumentType { get; set; }
}
[MapExport(MapName = "Map")]
public class Map
{
}

Resources