While activity in WF 4 rehosted designer - workflow-foundation-4

I have written a custom activity which contains a simple ExpressionTextBox:
<sapv:ExpressionTextBox HintText="List of Strings"
Grid.Row ="0" Grid.Column="1" MaxWidth="150" MinWidth="150" Margin="5"
OwnerActivity="{Binding Path=ModelItem}"
Expression="{Binding Path=ModelItem.Test, Mode=TwoWay,
Converter={StaticResource ArgumentToExpressionConverter},
ConverterParameter=In }" />
In the library, i've added Test property as follows:
public InArgument<string> Test { get; set; }
So, this is the whole thing:
A while and a variable i of type i defined in its scope. I would expect to get back "Test1", "Test2" ... and so on, but instead i get :
So, that variable i is seen as a string and not interpreted as the integer defined in the variables section.
I've tried this with a simple property of type string also. Then i thought that InArgument might handle the thing.. i don't know what to do more. Any clues about this?

I might need more of your code posting to bb able to help more, and understand fully what you want to achieve. But from the screen shot I can see that your not accessing the Runtime Arguments in the cache meta data method. Subsequently the console writeline method you are calling is interpreting the raw text value rather than correctly evaluating the expression.
Try the following in your code activity
using System;
using System.ComponentModel;
using System.IO;
using System.Runtime;
using System.Activities.Validation;
using System.Collections.Generic;
using System.Windows.Markup;
using System.Collections.ObjectModel;
using System.Activities;
namespace WorkflowConsoleApplication2
{
public sealed class CodeActivity1 : CodeActivity
{
// Define an activity input argument of type string
[DefaultValue(null)]
public InArgument<string> Test
{
get;
set;
}
protected override void CacheMetadata(CodeActivityMetadata metadata)
{
RuntimeArgument textArgument = new RuntimeArgument("Test", typeof(string), ArgumentDirection.In);
metadata.Bind(this.Test, textArgument);
metadata.SetArgumentsCollection(
new Collection<RuntimeArgument>
{
textArgument,
});
}
// If your activity returns a value, derive from CodeActivity<TResult>
// and return the value from the Execute method.
protected override void Execute(CodeActivityContext context)
{
Console.WriteLine(this.Test.Get(context));
}
}
}

Related

Invoke C# .NET Core Lambda from AWS CodePipeline Action

AWS CodePipeline allows you to invoke a custom Lambda from an action as described here, https://docs.aws.amazon.com/codepipeline/latest/userguide/actions-invoke-lambda-function.htmltion
I am having trouble determining how my C# Lambda function should be defined in order to access the input data from the pipeline.
I tried numerous attempts, was thinking it would be something similar to below. I have also tried to create my own C# classes that the input JSON data would be deserialized to.
public void FunctionHandler( Amazon.CodePipeline.Model.Job
CodePipeline, ILambdaContext context)
I was able to find out a solution. Initially the first step that helped was to change the input parameter for my lambda function to a Stream. I was then able to convert the stream to a string and determine exactly what was being sent to me, e.g
public void FunctionHandler(Stream input, ILambdaContext context)
{
....
}
Then, based on the input data I was able to map it to a C# class that wrapped the AWS SDK Amazon.CodePipeline.Model.Job class. It had to be mapped to the json property "CodePipeline.job". The below code worked, I was able to retrieve all input values.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Amazon.Lambda.Core;
using Amazon.CodePipeline;
using Newtonsoft.Json;
using System.IO;
// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))]
namespace lambdaEmptyFunction
{
public class Function
{
public class CodePipelineInput
{
[JsonProperty("CodePipeline.job")]
public Amazon.CodePipeline.Model.Job job { get; set; }
}
public void FunctionHandler(CodePipelineInput input, ILambdaContext context)
{
context.Logger.LogLine(string.Format("data {0} {1} {2}", input.job.AccountId, input.job.Data.InputArtifacts[0].Location.S3Location.BucketName, input.job.Id));
}
}
}

Trying to understand how to access dictionaries outside of object/script

I don't think I understand dictionaries at all. I understand how to set them up and access them from the same script/object but when it comes to accessing it from a different object/script that's not parent/child I have no idea what I'm doing. This is my script for the gameobject holding the dictionaries:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class HeroStats : MonoBehaviour {
//Dictionary Structure <Key, value>
Dictionary<string,int> HeroStat = new Dictionary<string,int>();
Dictionary<string,string>HeroName = new Dictionary<string,string>();
Dictionary<string,string>StatDef = new Dictionary<string,string>();
//Initialization:
void Start(){
//===NAME==========================
HeroName.Add("Name","Insert Name");
//===STATS=========================
HeroStat.Add("Constitution", 3);
HeroStat.Add("Dexterity", 3);
HeroStat.Add("Intelligence", 3);
HeroStat.Add("Strength", 3);
HeroStat.Add("Wisdom", 3);
//===STAT DEFINITION==============
StatDef.Add("Constitution", "Your Overall Healthiness");
print (HeroName["Name"]);
print (HeroStat["Strength"]);
}
}
and this is the script I'm trying to use to access the dictionary:
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System;
public class heroName : MonoBehaviour {
public HeroStats heroStats;
public Text text;
public string lookUp;
public string what;
// Use this for initialization
void Start(){
heroStats = FindObjectOfType<HeroStats>();
what = heroStats.GetType().GetField("Name").GetValue(this).ToString();
text = gameObject.GetComponent<Text>();
lookUp = this.gameObject.name;
}
void Update(){
}
}
I just have no idea what I'm doing at this point or know what to google. Every video/tutorial I've seen always shows them accessing the dictionary from the same script/object.
A Dictionary is just a way of storing data. Just like an int, or string. You need to have access to your Dictionary in your external script.
You would need to make your Dictionary public in HeroStats, and then in heroName you would simply have
heroStats.HeroStat["Constitution"]

Pass subclass of generic model to razor view

The Big Picture:
I have found what seems like a limitation of Razor and I am having trouble coming up with a good way around it.
The Players:
Let's say I have a model like this:
public abstract class BaseFooModel<T>
where T : BaseBarType
{
public abstract string Title { get; } // ACCESSED BY VIEW
public abstract Table<T> BuildTable();
protected Table<T> _Table;
public Table<T> Table // ACCESSED BY VIEW
{
get
{
if (_Table == null)
{
_Table = BuildTable();
}
return _Table;
}
}
}
And a subclass like this:
public class MyFooModel : BaseFooModel<MyBarType>
{
// ...
}
public class MyBarType : BaseBarType
{
// ...
}
I want to be able to pass MyFooModel into a razor view that is defined like this:
// FooView.cshtml
#model BaseFooModel<BaseBarType>
But, that doesn't work. I get a run-time error saying that FooView expects BaseFooModel<BaseBarType> but gets MyFooModel. Recall that MyFooModel in herits from BaseFooModel<MyBarType> and MyBarType inherits from BaseBarType.
What I have tried:
I tried this out in non-razor land to see if the same is true, which it is. I had to use a template param in the View to get it to work. Here is that non-razor view:
public class FooView<T>
where T : BaseBarType
{
BaseFooModel<T> Model;
public FooView(BaseFooModel<T> model)
{
Model = model;
}
}
With that structure, the following does work:
new FooView<MyBarType>(new MyFooModel());
My Question:
How can I do that with Razor? How can I pass in a type like I am doing with FooView?
I can't, but is there any way around this? Can I achieve the same architecture somehow?
Let me know if I can provide more info. I'm using .NET 4 and MVC 3.
EDIT:
For now, I am just adding a razor view for each subclass of BaseFooModel<BaseBarType>. I'm not psyched about that because I don't want to have to create a new view every time I add a new model.
The other option is to just take advantage of the fact that I am able to get this working in regular c# classes without razor. I could just have my razor view #inherits the c# view and then call some render method. I dislike that option because I don't like having two ways of rendering html.
Any other ideas? I know its hard to understand the context of the problem when I'm giving class names with Foo and Bar, but I can't provide too much info since it is a bit sensitive. My apologies about that.
What I have so far, using Benjamin's answer:
public interface IFooModel<out T>
where T : BaseBarModel
{
string Title { get; }
Table<T> Table { get; } // this causes an error:
// Invalid variance: The type parameter 'T' must be
// invariantly valid on IFooModel<T>.Table.
// 'T' is covariant.
}
public abstract class BaseFooModel<T> : IFooModel<T>
where T : BaseBarModel
{
// ...
}
What ended up working:
public interface IFooModel<out T>
where T : BaseBarModel
{
string Title { get; }
BaseModule Table { get; } // Table<T> inherits from BaseModule
// And I only need methods from BaseModule
// in my view.
}
public abstract class BaseFooModel<T> : IFooModel<T>
where T : BaseBarModel
{
// ...
}
You need to introduce an interface with a covariant generic type parameter into your class hierarchy:
public interface IFooModel<out T> where T : BaseBarType
{
}
And derive your BaseFooModel from the above interface.
public abstract class BaseFooModel<T> : IFooModel<T> where T : BaseBarType
{
}
In your controller:
[HttpGet]
public ActionResult Index()
{
return View(new MyFooModel());
}
Finally, update your view's model parameter to be:
#model IFooModel<BaseBarType>
Using interfaces-based models was deliberately changed between ASP.NET MVC 2 and MVC 3.
You can see here
MVC Team:
Having interface-based models is not something we encourage (nor, given the limitations imposed by the bug fix, can realistically support). Switching to abstract base classes would fix the issue.
"Scott Hanselman"
The problem you are experiencing is not a Razor error, but a C# error. Try to do that with classes, and you'll get the same error. This is because the model is not BaseFooModel<BaseBarType>, but BaseFooModel<MyFooModel>, and an implicit conversion cannot happen between the two. Normally, in a program you'd have to do a conversion to do that.
However, with .NET 4, introduced was contravariance and covariance, which sounds like the ability of what you are looking for. This is a .NET 4 feature only, and I honestly don't know if Razor in .NET 4 makes use of it or not.

WF4 - Composite custom activity throw an strange exception when using OutArgument

I'm trying to use a composite custom activity that apply a regular expression and return a boolean if it match.
The pattern is something encoded in design time.
The source text is coming from an activity. This activity is also specified in design time (I've made a activity designer that allow to drop an activity as source)
But I also need to return what sub string match the expression, so I added an OutArgument to retrieve the string that match, and the string captured.
Here is the code:
public class RegularExpression : NativeActivity<bool>
{
[RequiredArgument]
public string Pattern { get; set; }
public OutArgument<string> Captured { get; set; }
[RequiredArgument]
public Activity<string> RetrieveTextActivity { get; set; }
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
metadata.AddChild(this.RetrieveTextActivity);
}
protected override void Execute(NativeActivityContext context)
{
if (this.RetrieveTextActivity != null)
context.ScheduleActivity<string>(this.RetrieveTextActivity, this.onRetrieveComplete);
}
private void onRetrieveComplete(NativeActivityContext context, ActivityInstance completedInstance, string result)
{
var regexp = new Regex(this.Pattern);
var match = regexp.Match(result);
this.Result.Set(context, match.Success);
if (this.Captured != null)
this.Captured.Set(context, match.Value);
}
}
If I execute this activity without binding a variable to the Captured argument, it works as expected (the Result is correctly set).
But if I use the designer to add a variable, then I bind the variable to the Captured argument this error popup:
The argument of type 'System.String' cannot be used. Make sure that
it is declared on an activity.
The exception is thrown when executing this line:
this.Captured.Set(context, match.Value);
Does someone have an idea why I can't set the argument ?
I also read that I shouldn't test that Captured is null, the runtime should automatically set a default value. But If I don't test, I've a NullReference when I don't bind a variable to the argument...
EDIT:
I want to add more information about the workflow itself. I've read in another topic that it may be VS. Here I just want to specify that I'm using a rehosted designer to create the workflow (and not VS). The workflow is then saved as XML in a database.
When I need to start a new workflow, I read the database, use XamlService.Load and Run the created workflow.
Does the error go away if you declare the argument in CacheMetadata?
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
metadata.AddChild(this.RetrieveTextActivity);
RuntimeArgument argument = new RuntimeArgument("Captured", typeof(string), ArgumentDirection.Out);
metadata.Bind(this.Captured, argument);
metadata.AddArgument(argument);
}
EDIT: I was too quick. The above code should now compile and hopefully fix your problem.
My problem went away when I just called the base.CachMetadata(metadata) after my adds. Try:
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
metadata.AddChild(this.RetrieveTextActivity);
base.CacheMetadata(metadata);
}
You want to do it after your adds, because you want the base class to know what you've added when you call it. I think the base class uses reflection to do Damir Arh's answer for you automatically. This way you don't have to add or modify all that code every time you add or modify your properties. If you had a lot of properties it would become a pain real fast.

Can an ASP.Net control be configued to accept ANY attributes, even ones not defined as properties in the class definition?

Is it possible to define a control to have non-specified set of attributes? For instance:
<MyPrefix:MyControl SomeAttribute="SomeValue" runat="server"/>
I don't want to define a property on the control class beforehand for "SomeAttribute." I'd really just like a HashTable or some other construct like this:
"SomeAttribute" => "SomeValue"
So this control can be used in many places with attributes that are essentially made up at runtime.
I'm wonder if there's some parsing method I can override which iterates through the attributes at parse time. I can:
Look for a property with the name and set it
If I don't find such a property, put the attribute name and value into a HashTable
Possible?
You want to use the IAttributeAccessor interface.
Defines methods used by ASP.NET server controls to provide programmatic access to any attribute declared in the opening tag of a server control.
Example control:
using System;
using System.Collections.Generic;
using System.Web.UI;
namespace App_Code.Controls {
public class OutputAttributesControl : Control, IAttributeAccessor {
private readonly IDictionary<String, String> _attributes = new Dictionary<String, String>();
protected override void Render(HtmlTextWriter writer) {
writer.Write("Attributes:<br/>");
if (_attributes.Count > 0) {
foreach (var pair in _attributes) {
writer.Write("{0} = {1} <br/>", pair.Key, pair.Value);
}
} else {
writer.Write("(None)");
}
}
public String GetAttribute(String key) {
return _attributes[key];
}
public void SetAttribute(String key, String value) {
_attributes[key] = value;
}
}
}
Invocation:
<AppCode:OutputAttributesControl runat="server" attr="value" />
Output:
Attributes:
attr = value
Caveats:
It seems that SetAttribute is only called on attributes that can not be resolved normally. This means you'll not see the id- or the runat-attribute in your code. Assigned properties (attr="<%= DateTime.Now %>") show up as an empty string. Databound properties does not show up at all in design mode, but works in normal mode (assuming that someone called DataBind, as usual).

Resources