I am having issue with mapping dynamic or ExpandoObject onto instance of the destination type. I know documentation says that no mapping needs to be created upfront to map dynamic type, but what if I want to do some post processing. For example I have this
this.CreateMap<ExpandoObject,CustomerCreatedEvent>()
.AfterMap(( o, #event) => #event.NewProp = "some default value" );
And when I try to map
dynamic evnt = JsonConvert.DeserializeObject<ExpandoObject>(Encoding.UTF8.GetString(data));
var instanceType = Type.GetType((string) eventClrTypeName);
var eventInstance = _mapper.Map(evnt, evnt.GetType(), instanceType);
I get only "NewProp" mapped, and all other properties are set to default value, no assignment happens.
Related
since the last update of the Logicmonitor provider in Terraform we're struggling with a sorting isse.
In LogicMonitor the properties of a device are a name-value pair, and they are presented alfabetically by name. Also in API requests the result is alphabetical. So far nothing fancy.
But... We build our Cloud devices using a module. Calling the module we provide some LogicMonitor properties specially for this device, and a lot more are provided in the module itself.
In the module this looks like this:
`
custom_properties = concat([
{
name = "host_fqdn"
value = "${var.name}.${var.dns_domain}"
},
{
name = "ocid"
value = oci_core_instance.server.id
},
{
name = "private_ip"
value = oci_core_instance.server.private_ip
},
{
name = "snmp.version"
value = "v2c"
}
],
var.logicmonitor_properties)
`
The first 4 properties are from the module and combined with anyting what is in var.logicmonitor_properties. On the creation of the device in LogicMonitor all properties are set in the order the are and no problem.
The issue arises when there is any update on a terraform file in this environment. Due to the fact the properties are presented in alphabetical order, Terraform is showing a lot of changes if finds (but which are in fact just a mixed due to sorting).
The big question is: How can I sort the complete list of properties bases on the "name".
Tried to work with maps, sort and several other functions and examples, but got nothing working on key-value pairs. Merging single key's works fine in a map, but how to deal with name/value pairs/
I think you were on the right track with maps and sorting. Terraform maps do not preserve any explicit ordering themselves, and so whenever Terraform needs to iterate over the elements of a map in some explicit sequence it always do so by sorting the keys lexically (by Unicode codepoints) first.
Therefore one answer is to project this into a map and then project it back into a list of objects again. The projection back into list of objects will implicitly sort the map elements by their keys, which I think will get the effect you wanted.
variable "logicmonitor_properties" {
type = list(object({
name = string
value = string
}))
}
locals {
base_properties = tomap({
host_fqdn = "${var.name}.${var.dns_domain}"
ocid = oci_core_instance.server.id
private_ip = oci_core_instance.server.private_ip
"snmp.version" = "v2c"
})
extra_properties = tomap({
for prop in var.logicmonitor_properties : prop.name => prop.value
})
final_properties = merge(local.base_properties, local.extra_properties)
# This final step will implicitly sort the final_properties
# map elements by their keys.
final_properties_list = tolist([
for k, v in local.final_properties : {
name = k
value = v
}
])
}
With all of the above, local.final_properties_list should be similar to the custom_properties structure you showed in your question except that the elements of the list will be sorted by their names.
This solution assumes that the property names will be unique across both base_properties and extra_properties. If there are any colliding keys between both of those maps then the merge function will prefer the value from extra_properties, overriding the element of the same key from base_properties.
First, use the sort() function to sort the keys in alphabetical order:
sorted_keys = sort(keys(var.my_map))
Next, use the map() function to create a new map with the sorted keys and corresponding values:
sorted_map = map(sorted_keys, key => var.my_map[key])
Finally, you can use the jsonencode() function to print the sorted map in JSON format:
jsonencode(sorted_map)```
I have code that sets the PXDataFieldAssign value as follows:
pf = new PXDataFieldAssign<xTACProjectTask.dueDate>(someValue);
I also have a table, holding the DAC field names, such as "xTACProjectTask.dueDate". This table also has a checkbox field to determine whether to use this DAC field as a parameter.
Is there a way to not have the DAC fieldname hard-coded, and instead (maybe using a 'typeof' call?) use the results of the table query to set that field name - like the following?
pf = new PXDataFieldAssign<typeof("xTACProjectTask.dueDate")>(someValue);
or, using my query result:
pf = new PXDataFieldAssign<typeof(query.value)>(someValue);
with query.value being the value in the table holding the DAC field name?
You can create it using Type.GetType and Activator.CreateInstance. Please see the example below:
string typeName = "PX.Objects.IN.InventoryItem+descr,PX.Objects";
Type typeArgument = Type.GetType(typeName);
Type genericClass = typeof(PXDataFieldAssign<>);
Type constructedClass = genericClass.MakeGenericType(typeArgument);
object created = Activator.CreateInstance(constructedClass,new object[] { "Test Description" });
You will get the below wrapped into object in the created
Is it possible to programmatically declare a nodejs const from a variable (string?)
let myConsts = ["const1","const2","const3"];
myConsts.forEach(function(label){
defineConst(label,"value"); // I need this function
})
defineConst should define a const, something like the PHP "define" function, but for nodejs
No, you can't really do that in Javascript. For a bit, I thought maybe you could hack it with eval() which is pretty much always the wrong way to solve a programming problem, but even eval() won't introduce a new const variable to the current scope. It will introduce a new var variable to the current scope as in this:
// dynamic variable name
let varName = "test";
// create variable in the current scope
eval("var " + varName + " = 4;");
// see if that variable exists and has the expected value
console.log(test);
But, alas you can't do this with const. You can read more about why here: Define const variable using eval().
Whatever programming problem you are trying to solve can likely be solved in a much better way since you really shouldn't be introducing dynamically named local variables. It's much better to use something like a Map object or a regular object with dynamically named properties in order to keep track of values with dynamic names to them.
If you shared the actual programming problem you're trying to solve (rather than your attempted solution), we could advise further on the best code for that particular problem.
Here's an example of the ability to store dynamically named properties on an object:
let base = {};
// dynamic property name (any string value in this variable)
let varName = "test";
// set property value with dynamic property name
base[varName] = "Hello";
// can reference it either way
console.log(base[varName]);
console.log(base.test);
Or, it can be done using a Map object:
let base = new Map();
// dynamic Map element key (any string value in this variable)
let varName = "test";
// set Map element with dynamic key
base.set(varName, "Hello");
// can reference it either way by the key
console.log(base.get(varName));
console.log(base.get("test"));
One for the gurus, please convince me/us what is going on.
List<ExpandoObject> peopleList = new List<ExpandoObject>();
dynamic expandoObj1 = new ExpandoObject();
expandoObj1.id = 1;
expandoObj1.first = "fred";
expandoObj1.last = "krugger";
peopleList.Add(expandoObj1);
dynamic expandoObj2 = new ExpandoObject();
expandoObj2.id = 2;
expandoObj2.first = "george";
expandoObj2.last = "benson";
peopleList.Add(expandoObj2);
//test access the props
var expObj = expandoObj1;
var name = expObj.first;
var expObj2 = peopleList[0] as dynamic;
var name2 = expObj2.first;
IDictionary<string, object> expObj3 = peopleList[0] as ExpandoObject;
var name3 = expObj3["first"];
var expObj4 = peopleList[0] as ExpandoObject;
//var name4 = expObj4.first; //THIS DOESN'T WORK - ExpandoObject does not contain a definition for 'first' etc...
In all cases, the LEFT-HAND SIDE is a System.Dynamic.ExpandoObject;
Why then, on the 4th case expObj4, i cannot access the property expObj4.first ?
ExpandoObject is a sealed class which stores data in a dictionary. It implements IDynamicMetaObjectProvider interface which provides dynamic behaviour to the classes implementing it. It also implements IDictionary interface which provides dictionary like behaviour to it. It is supposed to be checked and validated at compile time.
dynamic is a type which is not supposed to be checked by the compiler at compile time. It is checked and breaks at runtime. At compile time, a dynamic entity is assumed to support any operation. So, when you say, it is a expandoobject, the field called first does not get appended to object itself.
Check source code of expando object here
https://github.com/Microsoft/referencesource/blob/master/System.Core/Microsoft/Scripting/Actions/ExpandoObject.cs
Think of dynamic behavior like an object. You can put any type there. When you are adding to list, you are adding to list as dynamic, but the inherent type of item being added is ExpandoObject. So, you are able to cast it back to ExpandoObject.
When you say,
expandoObj1.first = "fred";
it is same as saying,
expandoObj1.Add("first", "fred");
When you used
var expObj = expandoObj1;
var name = expObj.first;
you were using expandoObject in dynamic form. So, you were able to access properties directly. When you cast it to ExpandoObject class, you were using actual ExpandoObject class which stores fields in Dictionary, so dot(.) notation does not work.
var expObj4 = peopleList[0] as ExpandoObject;
variable on left hand side is still ExpandoObject, not a dictionary. ExpandoObject exposes its members through collection search.
var name4 = expObj4.Where(t=>t.Key == "first").First().Value;
When you cast it to a dictionary, it works like a dictionary.
IDictionary<string, object> expObj3 = peopleList[0] as ExpandoObject;
var name3 = expObj3["first"];
When you cast it to a dynamic, you can access these keys like they are properties of the class.
Further reference
Dynamically adding properties to an ExpandoObject
This is because the variable expObj4 is declared as ExpandoObject and not as dynamic. This is an important difference.
Try this:
dynamic a = new ExpandoObject();
a.Name = "Test";
This compiles, but the following doesn't:
ExpandoObject a = new ExpandoObject();
a.Name = "Test";
you get this:
CS1061 'ExpandoObject' does not contain a definition for 'Name' and no extension method 'Name' accepting a first argument of type 'ExpandoObject' could be found
The variables you have that are related to this are:
expandoObj1 - dynamic
expandoObj2 - dynamic
expObj1 - dynamic
expObj2 - dynamic
expObj3 - dictionary, but you use dictionary access here, not dot-access
The magic "let's see if we can access the thing at runtime" code of the compiler only kicks in if the expression or variable is dynamic. ExpandoObject is just a type that supports this.
I am new to the API and I'm trying to get values from the active view. I am using the following code as a mock up to what I'm trying to do:
public void GetViewProperties()
{
String viewname;
String typename;
String levelname;
String Output;
ViewFamilyType VfamType;
Level lev;
//Get document and current view
Document doc = this.ActiveUIDocument.Document;
View currentView = this.ActiveUIDocument.ActiveView;
//Find the view family type that matches the active view
VfamType = new FilteredElementCollector(doc).OfClass(typeof(ViewFamilyType))
.Where(q => q.Name == "1-0-Model").First() as ViewFamilyType;
//Find the level that matches the active view
lev = new FilteredElementCollector(doc).OfClass(typeof(Level))
.Where(q => q.Name == "00").First() as Level;
//Get the view's current name
viewname = currentView.Name.ToString();
//Get the name of the view family type
typename = VfamType.Name;
//Get the name of the level
levelname = lev.Name.ToString();
//Combine results for task dialog
Output = "View: " + viewname + "\n" + typename + "-" + levelname;
//Show results
TaskDialog.Show("View Properties Test",Output);
}
I'm cheating at the moment by grabbing the view type and level by name. I really want them to be found by looking at the properties of the active view. I can't figure out how I am meant to access the view type and level name properties. I need to make lambda use a variable e.g. (q => q.Name == Level.name), (q => q.Name == ViewFamilyType.name).
Thanks in advance!
Here is your code corrected:
public void GetViewProperties()
{
//Get document and current view
Document doc = this.ActiveUIDocument.Document;
View currentView = this.ActiveUIDocument.ActiveView;
//Find the view family type that matches the active view
var VfamType = (ViewFamilyType)doc.GetElement(currentView.GetTypeId());
//Find the level that matches the active view
Level lev = currentView.GenLevel;
//Get the view's current name
string viewname = currentView.Name;
//Get the name of the view family type
string typename = VfamType.Name;
//Get the name of the level
string levelname = lev.Name;
//Combine results for task dialog
string Output = "View: " + viewname + "\n" + typename + "-" + levelname;
//Show results
TaskDialog.Show("View Properties Test", Output);
}
You don't need to use a FilteredElementCollector to get these informations. And if you need elsewhere, you don't need a Where: just put your lambda in the First:
new FilteredElementCollector(doc).OfClass(typeof(ViewFamilyType))
.First(q => q.Name == "1-0-Model")
If you need to access in your lambda a property specific to a class, not defined on Element, you can use Cast:
new FilteredElementCollector(doc).OfClass(typeof(ViewFamilyType))
.Cast<ViewFamilyType>().First(vft => vft.IsValidDefaultTemplate)
And please do not declare all your variable at the start of your methods. You're not writing Pascal. Declare variables as close to the first spot that you use them as possible. It makes your code much more readable. The closer a variable is declared to where it is used, the less scrolling/searching you have to do when reading the code later and it also naturally narrow their scope.
You're probably looking for View.GenLevel property. This will work for views related to levels, such as Plan Views. Note that if this View is not generated by a level, this property is null.