How to access return value from a custom code activity? - workflow-foundation-4

I build a simple custom native activity that return a string value.
public sealed class MyActivity : NativeActivity<string>
{
public InArgument<string> Id { get; set; }
protected override void Execute(NativeActivityContext context)
{
var returnString = QuerySomthing();
context.SetValue<string>(base.Result, returnString);
}
}
How can I get this value in the workflow's variables?

You can access the 'Result' property of your activity. All you need to do is create a variable on the workflow (of type String) and bind this to the 'Result' property. Then you can access the variable later on in the workflow to analyse its value. HTH

Related

Blazor get nested components by Reflection

I'm actually working on form validation in Blazor project (0.8.0).
I have created a component called InputValidation. This component receive many parameters to test if a property value is correct according a condition we can set up.
#using System.Linq.Expressions;
#typeparam TItem
#if (!Valid)
{
<span id="#(Id)_validation" class="form-text text-danger">#Message</span>
}
#functions {
[Parameter]
string Id { get; set; }
[Parameter]
TItem Property { get; set; }
[Parameter]
Expression<Func<TItem, bool>> On { get; set; }
[Parameter]
string Message { get; set; }
[Parameter]
bool ActiveOnLoad { get; set; } = true;
internal bool Valid { get; set; }
bool Activated;
protected async override Task OnInitAsync()
{
Activated = ActiveOnLoad;
}
protected async override Task OnAfterRenderAsync()
{
Activated = true;
}
protected async override Task OnParametersSetAsync()
{
Valid = !On.Compile().Invoke(Property);
}
}
You can implement it on your parent component like this :
<InputValidation Id="#nameof(ViewModel.UrlInput)" Property="#ViewModel.UrlInput" On="#(x => string.IsNullOrEmpty(x))" Message="Url is empty" ActiveOnLoad="#false"/>
I have coded a class that verifies that all InputValidation components have the property Valid at true.
#if (ViewModel.IsValid(this))
this represents the parent component.
The problem is... it's not working !
Here is the code of the validator :
public static class ModelValidator
{
public static bool IsValid<T, V>(this T viewmodel, V component) where T : IViewModel where V : ComponentBase
=> component.GetType().GetFields().OfType<InputValidation<T>>().All(x => x.Valid);
}
It's not working, I know, but even if we use Reflection (GetProperties, GetFields, GetMembers), it won't return any of the InputValidation members of the parent component.
My question is : is there a way to get all child components by using Reflection ? If yes, how to do it ?
I know that Blazor is still on early stage and I hope it will be released soon because it's a very pleasant technology !
Thank you for your responses !
You don't need reflection here (the InputValidation component is not a field in the parent, it is a component that will be rendered by the RenderTree).
You can capture a reference to each InputValidation component using the ref attribute.
<InputValidation ref="#InputValidationRef" Id="#nameof(ViewModel.UrlInput)" Property="#ViewModel.UrlInput" On="#(x => string.IsNullOrEmpty(x))" Message="Url is empty" ActiveOnLoad="#false"/>
Normally this ref "InputValidationRef" would be a field, but you can, instead use a property with a custom setter to build a list (or whatever collection you like)
List<InputValidation> InputValidations = new List<InputValidation>();
InputValidation InputValidationRef { set => InputValidations.Add(value); }
So, each InputValidation will now be captured as a reference and the Property InputValidationRef will be set for each one, which will, in turn, store all the references in the collection "InputValidations".
Now, you have a collection, you can test against
InputValidations.All(iv => iv.Valid)
Note: the collection is only populated after the component/page is rendered, so during the initial page load the collection of references is empty until the OnAfterRender/OnAfterRenderAsync method is called.

How can I access the workflow ID in BeginOnLoad / PersistenceIOParticipant?

I am using a class named:
public class ProcessFlowPersistenceIOParticipant : PersistenceIOParticipant
It inherits from System.Activities.Persistence.PersistenceIOParticipant in the WF4 framework.
I would like to override the BeginOnLoad(..) method.
In this override I would like to intercept the deserialization of the workflow instance and inject the business entity from the entity database into the entity workflow variable.
BeginOnLoad is fired when the workflow instance is loaded from the instance store.
via: workflowApplication.Load(workflowInstanceGuid);
I can see the workflow guid inside the readwritevalues dictionary.
Find the key in readWriteValues.Keys matching namespace
"urn:schemas-microsoft-com:System.Activities/4.0/properties"
Then the guid is inside:
value = readWriteValues[xName];
_workflowInstanceId = ((System.Activities.Runtime.ActivityExecutor)value).WorkflowInstanceId;
It is accessible via a debugger but not in code as ActivityExecutor is an internal class.
So is there a way to determine what workflow instance is being loaded inside BeginOnLoad?
Any tips much appreciated.
Mike
This question is similar to Access workflow id from inside BeginOnSave.
However in the case of BeginOnLoad there is no activity execution context available to get the workflow instance guid.
If you also implement the IWorkflowInstanceExtension interface in the ProcessFlowPersistenceIOParticipant you should be able to get at the workflow ID through the WorkflowInstanceProxy in the SetInstance() function.
public class TaskActivity : NativeActivity
{
protected override void Execute(NativeActivityContext context)
{
context.GetExtension<RelevantDataExtensionIO>().WorkflowInstanceId = context.WorkflowInstanceId;
}
}
public class RelevantDataExtensionIO : PersistenceIOParticipant
{
public const string NamespaceWorkflowInstanceId = "WorkflowInstanceId";
public Guid WorkflowInstanceId { get; set; }
public static XNamespace Namespace
{
get { return XNamespace.Get("http://sample.com/RelevantDataIO"); }
}
public RelevantDataExtensionIO()
: base(false, false)
{
}
protected override void CollectValues(out IDictionary<XName, object> readWriteValues, out IDictionary<XName, object> writeOnlyValues)
{
readWriteValues = new Dictionary<XName, object>();
readWriteValues.Add(Namespace.GetName(NamespaceWorkflowInstanceId), this.WorkflowInstanceId);
writeOnlyValues = null;
}
protected override IAsyncResult BeginOnLoad(IDictionary<XName, object> readWriteValues, TimeSpan timeout, AsyncCallback callback, object state)
{
Guid id = this.WorkflowInstanceId;
return base.BeginOnLoad(readWriteValues, timeout, callback, state);
}
}

C# Optional Object Action MVC Parameter

Is it possible to specify an object as a parameter in MVC with default values in some way?
E.g.
public virtual ViewResult Index(RequirementFilters requirementFilters)
I'd like to initialize the values of a couple of parameters on RequirementFilters?
At the moment I am doing
public virtual ViewResult Index(int status=1, bool required =false)
I wanted to create a Filter Object so I could re-use it but I can't figure out way of setting defaults for the object in the Action Parameters.
Thanks
Graeme
You could create a custom ActionFilter attribute and create an instance of your Filter Object there. You can provide some properties through the custom attribute.
Here's an example:
public class DefaultQuerySettingsAttribute : ActionFilterAttribute
{
public string ParameterName { get; set; }
public Type SettingsType { get; set; }
public int Rows { get; set; }
public string SortColumn { get; set; }
public string SortOrder { get; set; }
public bool PagingEnabled { get; set; }
public DefaultQuerySettingsAttribute()
{
this.ParameterName = "settings";
var defaultSettings = new QuerySettings();
this.Rows = defaultSettings.Rows;
this.SortColumn = defaultSettings.SortColumn;
this.SortOrder = defaultSettings.SortOrder;
this.PagingEnabled = defaultSettings.PagingEnabled;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
if (filterContext.ActionParameters.ContainsKey(this.ParameterName))
{
var querySettings = filterContext.ActionParameters[this.ParameterName] as QuerySettings;
if (querySettings == null || string.IsNullOrWhiteSpace(querySettings.SortColumn))
filterContext.ActionParameters[this.ParameterName] = this.GetQuerySettings();
}
}
private QuerySettings GetQuerySettings()
{
var querySettings = (QuerySettings)Activator.CreateInstance(SettingsType ?? typeof(QuerySettings));
querySettings.Rows = Rows;
querySettings.SortColumn = SortColumn;
querySettings.SortOrder = SortOrder;
querySettings.PagingEnabled = PagingEnabled;
return querySettings;
}
}
ParameterName is the name of the argument in the action method (requirementFilters in your case).
You can also specify actual type that will be instantiated by providing SettingsType.
Users sometimes prefer to see the defaults on screen, rather than allowing the system to hide the defaults internally.
A better way of having defaults will be to actually show the defaults on int UI, in the HTML by rendering it with together with the defaults. That way when someone posts the page, the defaults which you pre-rendered is also posted and binded to the model.
So try and see if you can render with defaults whatever for you are rendering and posted to the Index action.
Finally, if you can't do it that way, what is preventing you from initializing the properties with default values in the no-arg constructor while creating the object?
EDIT
Or you can use the C# language feature the null coalescent operator to implement defaults. Look here to read about it.
As long as you don't need to change the defaults per action, you can set them in the default constructor of the Model.

Custom textbox control in asp.net

I'm trying to create a custom textbox with a enum kind property in it(like textmode). The enum values will come from database. But enums cant be dynamic..is there another way out??
The closest would be an integer property.
Enums are compile-time constants. If the database values won't change at runtime, then you could always use a codegen tool to generate the enum values from the database (at pre-compile time). If they will change, you may need to just do a String Property or something similar, instead of the Enum.
You have to write a custom TypeConverter to accomplish this duty.
public class MyItemsConverter : TypeConverter
{
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
StringCollection values = new StringCollection();
// Connect to database and read values.
return new StandardValuesCollection(values);
}
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
return (context != null);
}
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
{
return true;
}
}
public class MyControl : WebControl
{
[TypeConverter(typeof(MyItemsConverter))]
public string MyItem { get; set; }
}

Modern way to handle and validate POST-data in MVC 2

There are a lot of articles devoted to working with data in MVC, and nothing about MVC 2.
So my question is: what is the proper way to handle POST-query and validate it.
Assume we have 2 actions. Both of them operates over the same entity, but each action has its own separated set of object properties that should be bound in automatic manner. For example:
Action "A" should bind only "Name" property of object, taken from POST-request
Action "B" should bind only "Date" property of object, taken from POST-request
As far as I understand - we cannot use Bind attribute in this case.
So - what are the best practices in MVC2 to handle POST-data and probably validate it?
UPD:
After Actions performed - additional logic will be applied to the objects so they become valid and ready to store in persistent layer. For action "A" - it will be setting up Date to current date.
I personally don't like using domain model classes as my view model. I find it causes problems with validation, formatting, and generally feels wrong. In fact, I'd not actually use a DateTime property on my view model at all (I'd format it as a string in my controller).
I would use two seperate view models, each with validation attributes, exposed as properties of your primary view model:
NOTE: I've left how to combining posted view-models with the main view model as an exercise for you, since there's several ways of approaching it
public class ActionAViewModel
{
[Required(ErrorMessage="Please enter your name")]
public string Name { get; set; }
}
public class ActionBViewModel
{
[Required(ErrorMessage="Please enter your date")]
// You could use a regex or custom attribute to do date validation,
// allowing you to have a custom error message for badly formatted
// dates
public string Date { get; set; }
}
public class PageViewModel
{
public ActionAViewModel ActionA { get; set; }
public ActionBViewModel ActionB { get; set; }
}
public class PageController
{
public ActionResult Index()
{
var viewModel = new PageViewModel
{
ActionA = new ActionAViewModel { Name = "Test" }
ActionB = new ActionBViewModel { Date = DateTime.Today.ToString(); }
};
return View(viewModel);
}
// The [Bind] prefix is there for when you use
// <%= Html.TextBoxFor(x => x.ActionA.Name) %>
public ActionResult ActionA(
[Bind(Prefix="ActionA")] ActionAViewModel viewModel)
{
if (ModelState.IsValid)
{
// Load model, update the Name, and commit the change
}
else
{
// Display Index with viewModel
// and default ActionBViewModel
}
}
public ActionResult ActionB(
[Bind(Prefix="ActionB")] ActionBViewModel viewModel)
{
if (ModelState.IsValid)
{
// Load model, update the Date, and commit the change
}
else
{
// Display Index with viewModel
// and default ActionAViewModel
}
}
}
One possible way to handle POST data and add validation, is with a custom model binder.
Here is a small sample of what i used recently to add custom validation to POST-form data :
public class Customer
{
public string Name { get; set; }
public DateTime Date { get; set; }
}
public class PageController : Controller
{
[HttpPost]
public ActionResult ActionA(Customer customer)
{
if(ModelState.IsValid) {
//do something with the customer
}
}
[HttpPost]
public ActionResult ActionB(Customer customer)
{
if(ModelState.IsValid) {
//do something with the customer
}
}
}
A CustomerModelBinder will be something like that:
public class CustomerModelBinder : DefaultModelBinder
{
protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor)
{
if (propertyDescriptor.Name == "Name") //or date or whatever else you want
{
//Access your Name property with valueprovider and do some magic before you bind it to the model.
//To add validation errors do (simple stuff)
if(string.IsNullOrEmpty(bindingContext.ValueProvider.GetValue("Name").AttemptedValue))
bindingContext.ModelState.AddModelError("Name", "Please enter a valid name");
//Any complex validation
}
else
{
//call the usual binder otherwise. I noticed that in this way you can use DataAnnotations as well.
base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
}
}
and in the global.asax put
ModelBinders.Binders.Add(typeof(Customer), new CustomerModelBinder());
If you want not to bind Name property (just Date) when you call ActionB, then just make one more custom Model Binder and in the "if" statement, put to return the null, or the already existing value, or whatever you want. Then in the controller put:
[HttpPost]
public ActionResult([ModelBinder(typeof(CustomerAModelBinder))] Customer customer)
[HttpPost]
public ActionResult([ModelBinder(typeof(CustomerBModelBinder))] Customer customer)
Where customerAmodelbinder will bind only name and customerBmodelbinder will bind only date.
This is the easiest way i have found, to validate model binding, and i have achieved some very cool results with complex view models. I bet there is something out there that i have missed, and maybe a more expert can answer.
Hope i got your question right...:)

Resources