Given a TFS build definition how to get the value and type of an arbitrary process parameter? - tfs

Given a build definition, I extract the following pieces from it:
m_template = (DynamicActivity)WorkflowHelpers.DeserializeWorkflow(buildDefinition.Process.Parameters);
Properties = m_template.Properties.ToDictionary(p => p.Name, StringComparer.OrdinalIgnoreCase);
Metadata = WorkflowHelpers.GetCombinedMetadata(m_template).ToDictionary(m => m.ParameterName);
m_parameters = WorkflowHelpers.DeserializeProcessParameters(buildDefinition.ProcessParameters)
Now I wish to know the value of an arbitrary process parameter.
My current code is:
public ParameterValue GetParameterValue(string name)
{
object propValue;
var valueType = GetParameterType(name, out propValue);
object value;
if (!m_parameters.TryGetValue(name, out value))
{
value = propValue;
}
return new ParameterValue(valueType, value);
}
private Type GetParameterType(string name, out object value)
{
value = null;
if (Properties != null)
{
DynamicActivityProperty property;
if (Properties.TryGetValue(name, out property))
{
var inArgument = property.Value as InArgument;
if (inArgument != null)
{
if (inArgument.Expression != null)
{
var exprString = inArgument.Expression.ToString();
if (!exprString.StartsWith(": VisualBasicValue<"))
{
value = exprString;
}
}
return inArgument.ArgumentType;
}
if (property.Value != null)
{
value = property.Value;
return property.Value.GetType();
}
var typeName = property.Type.ToString();
if (typeName.StartsWith(IN_ARGUMENT_TYPE_NAME_PREFIX))
{
typeName = typeName.Substring(IN_ARGUMENT_TYPE_NAME_PREFIX.Length, typeName.Length - IN_ARGUMENT_TYPE_NAME_PREFIX.Length - 1);
return Type.GetType(typeName, true);
}
return property.Type;
}
}
return typeof(string);
}
Unfortunately, this code stumbles for parameters satisfying all of the following conditions:
The parameter value is wrapped as InArgument<T>.
T is a non primitive type, for example string[]
The build definition does not override the value inherited from the process template.
What happens is that:
Because the value is non primitive exprString.StartsWith(": VisualBasicValue<") and I do not know how to handle it. Hence propValue is null.
Because the value is not overridden by the build definition !m_parameters.TryGetValue(name, out value) and hence I just return propValue.
As a result my logic returns null. But it is wrong! For example, I have a string[] parameter which has a list of string in the process template, but my logic returns null for the reasons explained.
So, what is the proper way to compute it?

You can use the following code (included in another link) to get value and type of one process parameter:
TfsTeamProjectCollection tfctc = new TfsTeamProjectCollection(new Uri("http://tfsservername:8080/tfs/DefaultCollection"));
IBuildServer bs = tfctc.GetService<IBuildServer>();
IBuildDetail[] builds = bs.QueryBuilds("teamprojectname", "builddefinitionname");
foreach (var build in builds)
{
var buildefinition = build.BuildDefinition;
IDictionary<String, Object> paramValues = WorkflowHelpers.DeserializeProcessParameters(buildefinition.ProcessParameters);
string processParametersValue = paramValues["argument1"].ToString();
Console.WriteLine(processParametersValue);
}
Also have a check on this case: TFS 2010: Why is it not possible to deserialize a Dictionary<string, object> with XamlWriter.Save when I can use XamlReader for deserializing

Related

ModelState.IsValid is false when I have a nullable parameter

I reproduced the issue I am having in a brand new MVC Web API project.
This is the default code with a slight modification.
public string Get(int? id, int? something = null)
{
var isValid = ModelState.IsValid;
return "value";
}
If you go to http://localhost/api/values/5?something=123 then this works fine, and isValid is true.
If you go to http://localhost/api/values/5?something= then isValid is false.
The issue I am having is that if you provide a null or omitted value for an item that is nullable, the ModelState.IsValid flags a validation error saying "A value is required but was not present in the request."
The ModelState dictionary also looks like this:
with two entries for something, one nullable, which I am not sure if it is significant or not.
Any idea how I can fix this so that the model is valid when nullable parameters are omitted or provided as null? I am using model validation within my web api and it breaks it if every method with a nullable parameter generates model errors.
It appears that the default binding model doesn't fully understand nullable types. As seen in the question, it gives three parameter errors rather than the expected two.
You can get around this with a custom nullable model binder:
Model Binder
public class NullableIntModelBinder : IModelBinder
{
public bool BindModel(System.Web.Http.Controllers.HttpActionContext actionContext, ModelBindingContext bindingContext)
{
if (bindingContext.ModelType != typeof(int?))
{
return false;
}
ValueProviderResult val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (val == null)
{
return false;
}
string rawvalue = val.RawValue as string;
// Not supplied : /test/5
if (rawvalue == null)
{
bindingContext.Model = null;
return true;
}
// Provided but with no value : /test/5?something=
if (rawvalue == string.Empty)
{
bindingContext.Model = null;
return true;
}
// Provided with a value : /test/5?something=1
int result;
if (int.TryParse(rawvalue, out result))
{
bindingContext.Model = result;
return true;
}
bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Cannot convert value to int");
return false;
}
}
Usage
public ModelStateDictionary Get(
int? id,
[ModelBinder(typeof(NullableIntModelBinder))]int? something = null)
{
var isValid = ModelState.IsValid;
return ModelState;
}
Adapted from the asp.net page: http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api for further reading and an alternative method to set it at the class(controller) level rather than per parameter.
This handles the 3 valid scenarios:
/test/5
/test/5?something=
/test/5?something=2
this first give "something" as null. Anything else (eg ?something=x) gives an error.
If you change the signature to
int? somthing
(ie remove = null) then you must explicitly provide the parameter, ie /test/5 will not be a valid route unless you tweak your routes as well.
You'll have to register a custom model-binder for nullable types as the default binder is calling the validator for nullable parameters as well, and the latter considers those empty values as invalid.
The Model Binder:
public class NullableModelBinder<T> : System.Web.Http.ModelBinding.IModelBinder where T : struct
{
private static readonly TypeConverter converter = TypeDescriptor.GetConverter( typeof( T ) );
public bool BindModel( HttpActionContext actionContext, System.Web.Http.ModelBinding.ModelBindingContext bindingContext )
{
var val = bindingContext.ValueProvider.GetValue( bindingContext.ModelName );
// Cast value to string but when it fails we must not suppress the validation
if ( !( val?.RawValue is string rawVal ) ) return false;
// If the string contains a valid value we can convert it and complete the binding
if ( converter.IsValid( rawVal ) )
{
bindingContext.Model = converter.ConvertFromString( rawVal );
return true;
}
// If the string does contain data it cannot be nullable T and we must not suppress this error
if ( !string.IsNullOrWhiteSpace( rawVal ) ) return false;
// String is empty and allowed due to it being a nullable type
bindingContext.ValidationNode.SuppressValidation = true;
return false;
}
}
Registration:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// ...
var provider = new SimpleModelBinderProvider(typeof(int?), new NullableModelBinder<int>());
config.Services.Insert(typeof(ModelBinderProvider), 0, provider);
// ...
}
}
Remove the default null value from the second parameter. The model binder will set it to null if it's something other than int.
I've found a working workaround for me (just exclude null values from data being sent - as opposed to sending values as nulls).
See https://stackoverflow.com/a/66712465/908608

BreezeSharp - Query with parameters against the local cache

I am trying to execute query with parameters against local cache or server (if nothing is found in cache).
public async Task<List<T>> Get(IDictionary<string, object> parameters, string resourceName = "", FetchSource fetchSource = FetchSource.None)
{
try
{
var query = resourceName == string.Empty ? EntityQuery.From<T>().WithParameters(parameters) : EntityQuery.From<T>(resourceName).WithParameters(parameters);
var queryResult = await this.ExecuteQuery(query, fetchSource);
var result = queryResult.ToList();
return result;
}
catch (Exception e)
{
return new List<T>(); // return empty result instead
}
}
FetchSource is our enum:
public enum FetchSource
{
None = 0,
FromServer = 1,
FromCache = 2,
FromCacheOrServer = 3
}
And here is ExecuteQuery method:
protected async Task<IEnumerable<T>> ExecuteQuery(EntityQuery<T> query, FetchSource fetchSource = FetchSource.None)
{
//...
if (fetchSource == FetchSource.FromCacheOrServer)
{
var result = query.ExecuteLocally(this.EntityManager); // Throws error
if (result != null && result.Any())
{
return result;
}
return await query.Execute(this.EntityManager);
}
//...
}
When I try to execute query locally this exception is thrown:
{"Unable to cast object of type
'WhereEnumerableIterator`1[StanleySteemer.Nimbus.Client.Common.Model.Proxy.RouteOrder]'
to type
'DataServiceOrderedQuery[StanleySteemer.Nimbus.Client.Common.Model.Proxy.RouteOrder]'."}
Although I couldn't find anything in docs specifically regarding to this subject, I have implemented similar functionality in BreezeJS which was working without issue(UPDATE: it doesn't work correctly):
findWithParametersInCacheOrServer = function (parameters, recordsLimit) {
var query = breeze.EntityQuery
.from(resourceName)
.withParameters(parameters);
var r = executeCacheQuery(query);
if (r) {
if (r.length > recordsLimit) {
return Q.resolve(r);
}
}
return executeQuery(query);
};
function executeCacheQuery(query) {
return entityManagerProvider.manager().executeQueryLocally(query);
}
Data architecture in JavaScript is similar to TempHire example.
Is this a known issue? Is there any workaround for it?
Not sure I understand, neither breeze.js nor breeze.sharp can automatically perform a 'local cache query' that involves parameters. This is because the interpretation of the parameters is only really defined on the server and not on the client.
It sounds as though what you have done is define a custom implementation of your specific 'with parameters' query in breeze.js that completely bypasses Breeze's internal implementation. Is this correct?

Reflection + Entity Framework to update data in MVC app

I've got a complex form on a page that is bound to a POCO representing a rather complex entity. One of the requirements is that, on blur, I update the database.
I'm currently passing the property (as key), value, and CampaignId via ajax. The key might look something like: Campaign.FanSettings.SocialSharing.FacebookLinkText.
I am using the code below, and getting "close". My final propertyToSet is the FacebookLinkText is not being set, because my object source is of type Entities.Campaign, while my object value is simply a string. I understand these need to be the same type, but I don't understand how to do that. Two questions:
How do I modify the code below to be able to execute the propertyToSet.SetValue method
Since I'm casting this to an object, I don't see how this would actually update my entity, so when I call SaveChanges it updates appropriately. What am I missing?
Thanks!
Code:
public void UpdateCampaign(int id, string key, string value)
{
using (var context = new BetaEntities())
{
var camp = context.Campaigns.Where(e => e.Id == id).Single();
SetProperty(camp, key,value);
}
}
public void SetProperty(object source, string property, object value)
{
string[] bits = property.Split('.');
for (int i = 0; i < bits.Length - 1; i++)
{
PropertyInfo prop = source.GetType().GetProperty(bits[i]);
source = prop.GetValue(source, null);
}
PropertyInfo propertyToSet = null;
if (source is IEnumerable)
{
foreach (object o in (source as IEnumerable))
{
propertyToSet = o.GetType().GetProperty(bits[bits.Length - 1]);
break;
}
}
else
{
propertyToSet = source.GetType().GetProperty(bits[bits.Length - 1]);
}
propertyToSet.SetValue(source, value, null);
}
Solved.
public void UpdateCampaign(int id, string key, string value)
{
using (var context = new BetaEntities())
{
var camp = context.Campaigns.Where(e => e.Id == id).Single();
SetProperty(camp, key, value);
context.SaveChanges()
}
}
public void SetProperty(object source, string property, object value)
{
string[] bits = property.Split('.');
for (int i = 0; i < bits.Length - 1; i++)
{
PropertyInfo prop = source.GetType().GetProperty(bits[i]);
source = prop.GetValue(source, null);
}
PropertyInfo propertyToSet = null;
if (source is IEnumerable)
{
foreach (object o in (source as IEnumerable))
{
propertyToSet = o.GetType().GetProperty(bits[bits.Length - 1]);
propertyToSet.SetValue(o, value,null);
break;
}
}
else
{
propertyToSet = source.GetType().GetProperty(bits[bits.Length - 1]);
propertyToSet.SetValue(source, value, null);
}
}

ConversionErrorInterceptor throws conversion error during quit/cancel (Struts 2)

The scenario of the problem is this
1) We map the struts field values to the dtos. The dtos contain integer fields which again are displayed on the screen.
2) Now I enter an incorrect value which gives conversion error for that integer field.
3) At that point in time I decide to quit the page(i.e press cancel), I get a conversion error. This is because the StrutsConversionErrorInterceptor gets called everytime.
Is there any way that I can skip the strutsConversionErrorInterceptor when I am calling a particular method the way we can skip validation using excludeMethods
Use this code to override Struts's StrutsConversionErrorInterceptor...
public class MyConversionErrorInterceptor extends AbstractInterceptor {
private static final long serialVersionUID = 1L;
public static final String ORIGINAL_PROPERTY_OVERRIDE = "original.property.override";
protected Object getOverrideExpr(ActionInvocation invocation, Object value) {
ValueStack stack = invocation.getStack();
try {
stack.push(value);
return "'" + stack.findValue("top", String.class) + "'";
} finally {
stack.pop();
}
}
#Override
public String intercept(ActionInvocation invocation) throws Exception {
ActionContext invocationContext = invocation.getInvocationContext();
Map<String, Object> conversionErrors = invocationContext.getConversionErrors();
ValueStack stack = invocationContext.getValueStack();
HashMap<Object, Object> fakie = null;
BaseAction baseAction = (BaseAction) invocation.getAction();
String buttonName = baseAction.getButtonName();
for (Map.Entry<String, Object> entry : conversionErrors.entrySet()) {
String propertyName = entry.getKey();
Object value = entry.getValue();
if (shouldAddError(propertyName, value)) {
String message = XWorkConverter.getConversionErrorMessage(propertyName, stack);
Object action = invocation.getAction();
if (action instanceof ValidationAware) {
ValidationAware va = (ValidationAware) action;
if(buttonName.equalsIgnoreCas("Next")){
va.addFieldError(propertyName, message);
}
}
if (fakie == null) {
fakie = new HashMap<Object, Object>();
}
if(buttonName.equalsIgnoreCas("Next")){
fakie.put(propertyName, getOverrideExpr(invocation, value));
}
}
}
if (fakie != null) {
// if there were some errors, put the original (fake) values in
// place right before the result
stack.getContext().put(ORIGINAL_PROPERTY_OVERRIDE, fakie);
invocation.addPreResultListener(new PreResultListener() {
public void beforeResult(ActionInvocation invocation, String resultCode) {
Map<Object, Object> fakie = (Map<Object, Object>) invocation.getInvocationContext().get(ORIGINAL_PROPERTY_OVERRIDE);
if (fakie != null) {
invocation.getStack().setExprOverrides(fakie);
}
}
});
}
return invocation.invoke();
}
protected boolean shouldAddError(String propertyName, Object value) {
if (value == null) {
return false;
}
if ("".equals(value)) {
return false;
}
if (value instanceof String[]) {
String[] array = (String[]) value;
if (array.length == 0) {
return false;
}
if (array.length > 1) {
return true;
}
String str = array[0];
if ("".equals(str)) {
return false;
}
}
return true;
}
}
You can specify you button names on which you want validation to fire. In above code I have used "Next" in code you can see
if(buttonName.equalsIgnoreCas("Next"))
Yes, you can skip calling the interceptor.
Just remove the interceptor definition from your action definition in struts.xml file.
i.e., remove <interceptor-ref name="conversionError"/>
Mainly this interceptor adds any error found in the ActionContext's conversionErrors map as a field error (provided that the action implements ValidationAware). In addition, any field that contains a validation error has its original value saved such that any subsequent requests for that value return the original value rather than the value in the action. This is important because if the value "abc" is submitted and can't be converted to an int, we want to display the original string ("abc") again rather than the int value (likely 0, which would make very little sense to the user).
After you removed this interceptor, if the struts failed to map the field with parameter of the object(i.e., from string to int), it throws result input action error.
This seems to be a better method to handle this scenario - using Conversion Validator. Repopulating Field upon conversion Error section is something very useful:
http://struts.apache.org/2.0.14/docs/conversion-validator.html

General solution to updating fields for table in Entity Framework 4.0

I want to create a method which can takes the properties I possibly may update and leaving those not interested untouched.
Here is what I did:
public static void updateTable(int id, string field1, string field2, string field3){
using(var context = new Entities()){
var obj = context.Table.Where(x=>x.id == id).FirstOrDefault();
if(obj != null){
obj.field1 = field1;
...
obj.SaveChanges();
}
}
}
But in this pattern, I need to pass all 4 parameters into the method even I just want to update only one field. Is there any generic solution to update only the fields I passed in?
I came up something like this:
public static void updateTable(int id, object data_json){
using(var context = new Entities()){
var obj = context.Table.Where(x=>x.id == id).FirstOrDefault();
if(obj != null){
if(data_json['field1']!=null) //something like this
obj.field1 = data_json['field1'];
...
obj.SaveChanges();
}
}
}
But this can't handle the case that I do want to set a field to be null. Or is there any better solution?
If you don't care about updating relationships, you can use ApplyCurrentValues, which only updates the scalar properties.
E.g:
public static void updateTable(int id, object data_json){
using(var context = new Entities()) {
var obj = context.Table.Where(x=>x.id == id).FirstOrDefault();
context.ApplyCurrentValues("Table", data_json);
}
}
It assumes an entity with the same key is already attached in the graph. In this case, the query for var obj will ensure the object is in the graph, then it's contents are overridden with the scalar properties on the supplied object.
You might need an explicit cast on data_json to ensure it is of the same type contained in the entity set.
Using an ExpandoObject would allow you to send in only the properties you want to set, and would allow you to specify null values as well.
For example:
public static void updateTable(int id, dynamic data){
using(var context = new Entities()){
var obj = context.Table.Where(x=>x.id == id).FirstOrDefault();
if(obj != null){
if (((IDictionary<string, object>)data).ContainsKey("field1"))
obj.field1 = data.field1;
...
obj.SaveChanges();
}
}
}
and you could call it like this:
dynamic data = new ExpandoObject();
data.field1 = 123;
data.field2 = null;
data.field5 = "abc";
MyClass.updateTable(1, data);
Everything can be solved with a moment of reflection. This function solves the problem:
public void UpdateTable(int id, object values)
{
using (var entities = new MyEntities())
{
var valuesType = values.GetType();
var element = entities.MyTable.Where(t => t.ID == id).First();
//We are iterating through all properties of updated element and checking
//if there is value provided for there properties in values parameter
foreach (var property in element.GetType().GetProperties())
{
var valuesProperty = valuesType.GetProperty(property.Name);
//If values contain this property
if (valuesProperty != null)
{
//taking value out of values parameter
var value = valuesProperty.GetValue(values, null);
//setting it in our element to update
property.SetValue(element, value, null);
}
}
entities.SaveChanges();
}
}
Usage:
UpdateTable(125, new { FieldA = 1, FieldB = "ABCD" });
You can even make this method more universal by adding generic table type parameter.

Resources