-
Excel-DNA ver: 1.7.0-rc9 I use the IFunctionExecutionHandler for validates my udf's argument and handle the wrong argument before udf execution and use the ExcelAsyncUtil.Observe for cache my function result when the argument(s) is no change. (I think this can help to improve my udf performance). it works well for long time. but now i have some udf with over 23 arguments or more and when add-in startup i can see some warnings :
But these warnings does not affect the add-in and udf(s) running. the strange thing when i change some argument value in these udf,the udf will not recalculation to refresh result. forexample: at first i think perhaps is my function signature has some error but when i remove the IFunctionExecutionHandler for this udf, it final recalculate as expect !! when i add the IFunctionExecutionHandler back for this udf it no recalculate again 😢 in order to avoid some simple mistake caused these . i check my log and visual studio debug output window and I am confident that no relevant anomalies have been found. The log and output panels are as quiet as the end of the world, and the entire udf appears to be completely unexecuted 😿 How can I provide more detailed information to troubleshoot this problem? The code I mentioned contains confidential company information, so I can't provide that code.. P.S. there are some code snap: public static IFunctionExecutionHandler ParameterValidationSelector(ExcelFunctionRegistration functionRegistration)
{
// reconfig function
functionRegistration.FunctionAttribute.ExplicitRegistration = true;
functionRegistration.FunctionAttribute.SuppressOverwriteError = true;
functionRegistration.FunctionAttribute.IsVolatile = true;
functionRegistration.FunctionAttribute.IsMacroType = true;
functionRegistration.CustomAttributes.Add(new CategoryAttribute("MyCategory"));
// reconfig argument attributes and aliase
List<ExcelArgumentDefinition> customDefinitions = JsonConvert.DeserializeObject<List<ExcelArgumentDefinition>>(GetParameterDefinition(functionRegistration.FunctionAttribute.Name));
Dictionary<int, IEnumerable<IValidator>> parameterValidators = new Dictionary<int, IEnumerable<IValidator>>();
for (int i = 0; i < functionRegistration.ParameterRegistrations.Count; i++)
{
ExcelParameterRegistration excelParameter = functionRegistration.ParameterRegistrations.ElementAt(i);
ExcelArgumentDefinition definition = _configs.FirstOrDefault(p => p.ArgumentName.ToLower().Equals(excelParameter.ArgumentAttribute.Name.ToLower()));
ExcelArgumentDefinition customDefinition = customDefinitions == null ? null : customDefinitions.FirstOrDefault(p => p.ArgumentName.ToLower().Equals(excelParameter.ArgumentAttribute.Name.ToLower()));
if (customDefinition != null)
{
definition = customDefinition;
}
if (definition == null)
{
parameterValidators[i] = new List<IValidator>();
continue;
}
excelParameter.ArgumentAttribute.Name = string.IsNullOrEmpty(definition.Aliase) ? definition.ArgumentName : definition.Aliase;
excelParameter.ArgumentAttribute.Description = definition.Description;
excelParameter.CustomAttributes.Add(new DisplayAttribute()
{
Name = definition.ArgumentName,
ShortName = definition.Aliase
});
List<Attribute> attributes = new List<Attribute>();
foreach (var item in definition.GetAttributes())
{
attributes.Add((Attribute)item);
}
excelParameter.CustomAttributes.AddRange(attributes);
parameterValidators[i] = ValidatorManager.Instance.AcquiredValidator(attributes, string.IsNullOrEmpty(definition.Aliase) ? definition.ArgumentName : definition.Aliase).Where(p=>p != null && !p.GetType().Equals(typeof(DefaultValidator))).ToList().AsEnumerable();
//parameterValidators[i] = new List<IValidator>();
}
// create a new IFunctionExecutionHandler for every udf
ParameterValidationHandler handler = new ParameterValidationHandler(parameterValidators);
return handler;
} public override void OnEntry(FunctionExecutionArgs args)
{
_logger.LogInformation($"{args.FunctionName} entry");
if(!CacheUtil.TryGetCache<AccessToken>(CacheKey.AccessToken,out AccessToken token) || string.IsNullOrWhiteSpace ( token?.access_token ) )
{
//throw new UnauthorizedAccessException ( "please ensure user login" );
args.ReturnValue = "please ensure user login";
args.FlowBehavior = FlowBehavior.Return;
return;
}
if (_validators != null && _validators.Count > 0)
{
_logger.LogInformation($"start to validation {args.FunctionName}'s argument(s). (total :{args.Arguments.Length})");
_logger.LogTrace($"{args.FunctionName} has arguments :{string.Join(Environment.NewLine, args.Arguments.Cast<object>().Select(p => p == null ? "NULL" : p.ToString()))}");
XlCall.Excel(XlCall.xlfVolatile, false);
// copy and cache the argument array for validation
Array.Resize(ref arguments, args.Arguments.Length);
Array.Copy(args.Arguments, arguments, arguments.Length);
string message = string.Empty;
CancellationTokenSource tokenSource = new CancellationTokenSource();
#if !DEBUG
tokenSource.CancelAfter(TimeSpan.FromMilliseconds(500));
#endif
ProcessSpecialHandle ( functionName: args.FunctionName );
for (int i = 0; i < args.Arguments.Length; i++)
{
string errorMessage = string.Empty;
object value = args.Arguments[i];
_logger.LogTrace($"validating the {i} of argument with {value}");
if (tokenSource.IsCancellationRequested)
{
_logger.LogWarning($"{args.FunctionName}validation time out after ({TimeSpan.FromMilliseconds(500)}),skip all follow validation。");
message += "...";
break;
}
if (!Validate(i, value, out errorMessage))
{
message += errorMessage + Environment.NewLine;
_logger.LogWarning($"{args.FunctionName} validates failure:{message}");
}
}
message = message.TrimEnd('\r', '\n');
if (!string.IsNullOrEmpty(message) && !message.Equals("..."))
{
throw new ValidationException(message);
}
}
base.OnEntry(args);
} public static object XL_UDF_A(object arg1,object arg2, ......, object arg23)
{
// parameter and object handle/convert
// business logic here
// ......
// all my udf have the same logic like follow:
var caller = XlCall.Excel(XlCall.xlfCaller) as ExcelReference;
object[] parameters = new []{ arg1,arg2,......,arg23,caller};
return ExcelAsyncUtil.Observe("UDFA",parameters,()=> myObservableObj);
} |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
The 'truncated argument names' warnings relate to an Excel limitation that the total length of the concatenated argument names (including some ',' separator characters) cannot be longer than 255 characters. Truncating the argument names passed to Excel would affect the Excel function wizard dialog, but not using the functions from a formula. Assuming you have the formula Then if C1 changes, but the result of From the code snippets you show, one thing that jumps out at me is that you are playing with the 'volatile' flags of the function - both by registering the function as 'IsVolatile=true' and inside the function handler by calling I'm not sure of the details now, but I don't know what the interaction between the volatile function option and the RTD implementation would be. (RTD is the underlying Excel feature used to implement If these comments don't help yet, I suggest making a small alone-standing example that I can debug and discuss further. |
Beta Was this translation helpful? Give feedback.
The 'truncated argument names' warnings relate to an Excel limitation that the total length of the concatenated argument names (including some ',' separator characters) cannot be longer than 255 characters. Truncating the argument names passed to Excel would affect the Excel function wizard dialog, but not using the functions from a formula.
Assuming you have the formula
=XL_UDF_A(A1,B1,XL_UDF_B(C1)......,AA1)
Then if C1 changes, but the result of
XL_UDF_B(C1)
does not change, then we would not expect theXL_UDF_A
function to create a new RTD topic, as the 'parameters' input array is unchanged. I'm not sure what your expectation is in this case, and whether that might be what you're seeing.