I'm using the Revit API for extensible storage using IronRuby, and I think my problem is an IronRuby one. I'm trying to reproduce in IronRuby this C# example from Jeremy Tannik's Revit blog:
// create a field to store a string map
FieldBuilder fieldBuilder =
schemaBuilder.AddMapField( "StringMap", typeof( string ), typeof( string ) );
. . .
// set the value for this entity
IDictionary<string, string> stringMap = new Dictionary<string, string>();
stringMap.Add( "key1", "value1" );
stringMap.Add( "key2", "value2" );
entity.Set<IDictionary<string, string>>( field, stringMap );
When I try to do what I think is the same sort of thing in IronRuby:
# create a field to store a string map
schema_builder.AddMapField( "manually_checked", System::String.to_clr_type, System::Boolean.to_clr_type )
. . .
# set the value for this entity
xDict = Dictionary[System::String,System::Boolean].new
IDictionary_module = xDict.class.ancestors.detect { |i| i.to_s ==
"System::Collections::Generic::IDictionary[System::String, System::Boolean]" }
xDict.Add( "blah", true )
# all three of these fail with the same error
x.Set( "manually_checked", xDict )
x.method(:Set).of(Dictionary[System::String,System::Boolean]).call( "manually_checked", xDict )
x.method(:Set).of(IDictionary_module).call( "manually_checked", xDict )
All three of the ways I call Set() fail with the same message:
Unsupported type:
System.Collections.Generic.Dictionary`2[[System.String, mscorlib,
Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089],[System.Boolean, mscorlib,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
(InvalidOperationException)
Which makes me think that the "Dictionary`2" in the error message is a reference to xDict.class.ancestors[2], which is the position of IDictionary[System::String,System::Boolean] in the ancestors array.
So, my question is, is there an IronRuby syntax that will allow this, or have a bumped up against a showstopper because of the difference between a Ruby module and a .NET interface?
Your dictionary Add() is using an IronRuby string as the key parameter, but the dictionary is expecting a CLR string type. Add a "to_clr_string" call to your Ruby string to bring it in line with what's expected.
Related
My code:
public static IConfigurationBuilder AddModel<T> (
this IConfigurationBuilder builder,
T model,
JsonSerializerOptions options = null)
{
var dic = new Dictionary<string, T>
{
{typeof(T).Name, model}
};
byte[] bytes;
if (options != null)
bytes = JsonSerializer.SerializeToUtf8Bytes(dic, options);
else
bytes = JsonSerializer.SerializeToUtf8Bytes(dic);
var mem = new MemoryStream(bytes);
return builder.AddJsonStream(mem);
}
But il2cpp reports:
System.Text.Json.Serialization.Converters.DictionaryOfTKeyTValueConverter`3[
[System.Collections.Generic.Dictionary`2[
[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[Microsoft.Extensions.Logging.LogLevel, Microsoft.Extensions.Logging.Abstractions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],
[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[Microsoft.Extensions.Logging.LogLevel, Microsoft.Extensions.Logging.Abstractions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]]::.ctor'
for which no ahead of time (AOT) code was generated.
However,DictionaryOfTKeyTValueConverter is internal. I can't create instance in Assembly-CSharp to avoid this error.
Elsewhere in your code, you need to specifically call AddMove with all the types you will ever need. That's because the compiler will need to build versions of the method for every type. In an AOT (Ahead of Time) environment, the code can't make new types at runtime.
For example, you might do something like:
void SetupAOT()
{
_ = AddModel<ModelType> (default, new ModelType() );
throw new InvalidOperationException("This method is only for AOT code generation.");
}
This way, it looks like you'll call the method, the compiler is made aware of Dictionary <string, ModelType> and the compiler then adds the correct code to work with AOT.
This method should also be referenced from a point in the code that won't ever be true. Compilers can determine methods that aren't ever called, and strip them. So, to make sure that method isn't stripped, you could do something like:
// Never modify this from the default false
private bool alwaysFalse {get;set;}
// ... elsewhere
if ( alwaysFalse ) SetupAOT();
Alternatively, you could decorate to SetupAOT method with the Unity Preserve Attribute like this:
[Preserve]
void SetupAOT()
{
// ...
}
You can read up on the PreserveAttribute here. My preference would be to try the [Preserve] approach first.
Here's the source code for the DictionaryOfTKeyTValueConverter in your original question.
I am sure this problem has no perfect solution under Unity3d2020.3.22f. This is the most valuable report I've found: https://forum.unity.com/threads/il2cpp-does-not-generate-constructor-for-generic-classes-that-have-struct-as-parameter.997274 /.
The root of the problem is that Il2cpp cannot generate internal type code with a value type generic parameter, and Microsoft.Extensions.Logging.LogLevel is a value type.
The response plan is as follows:
Not using value types as generic parameters
Recompile System.Text.Json and expose its internal type
Use a non-standard compiler
I use scheme 1 and expect Unity to optimize il2cpp's support for value types as soon as possible
Thanks to #roddy for his answer to my query here
Copy pasting from earlier to set the context :
here is my data structure :
public class Premium{
private Map<String,Map<String,String>> valuesMap = new HashMap<String,Map<String,String>>();
public Map<String, Map<String, String>> getValuesMap() {
return valuesMap;
}
}
Sample values that will be present inside this 'valuesMap' :
Map<String,String> m1= new HashMap<String,String>();
m1.put("death","100");
m1.put("income","50");
valuesMap.put("Male",m1);
valuesMap.put("Female",m2);
....
Thanks to #Roddy now I can extract the map 'm1' embedded within 'valuesMap' for "Male"
rule "rule#7 testing me 001 "
when
// below line extracts 'valuesMap' from Premium object
$pr:Premium($masterMap:valuesMap)
// now have a handle to the embedded map for 'Male'
Map( $male: this["Male"] ) from $masterMap
// defining an object in which I want to populate the value from map obtained for male
$rulesResponse:RulesResponse();
then
System.out.println("rule#7 map " + $map);
// this is where in below code it is failing
$rulesResponse.abc = $innerMap.get("income");
end
when I am trying to extract the string from map against key 'income' and assign it to the 'RulesResponse' object it fails with :
[Error: unable to resolve method using strict-mode: java.lang.Object.get(java.lang.String)]
[Near : {... nse.abc = $innerMap.get("income"); ....}]
The response object is a simple POJO with getter and setter for attribute : abc
public class RulesResponse {
private String abc = "";
public String getAbc() {
return abc;
}
public void setAbc(String abc) {
this.abc = abc;
}
If I try and assign a hard coded value - it works and also reflects after the rule is executed
// this works
$rulesResponse.abc = "hard coded value";
When you get this["Male"] out of the map, it's an Object, not anything typed. It's basically due to type erasure -- Map<String, ?>.
You can get "income" out by doing Map( $income: this["income"]) from $male. Of course, now $income will too also be an Object so you'll need to cast it again. Could be as simple as a (String)$income on the right-hand side, or a $incomeStr: String() from $income on the left.
rule "Example"
when
$pr: Premium( $masterMap: valuesMap != null )
Map( $male: this["Male"] != null ) from $masterMap
Map( $income: this["income"] != null ) from $male
$rulesResponse: RulesResponse()
then
$rulesResponse.abc = (String)$income; // cast as necessary
end
We lose the nested type identity because of type erasure -- you've got a Map<String, ?> which becomes Map<String, Object> in practice.
Strongly suggest using a properly structured POJO instead of a Map as a rule input. Even if your actual code uses these nested maps (bad practice!), you should leverage a transform before calling the rules -- not only will your rules be a lot simpler and easier to work with, but they'll also be much more performant.
Even converting that inner map into an object will make things easier:
class GenderValues {
String death;
String income;
}
class Premium {
Map<String, GenderValues> valuesByGender;
}
Best practice would be to omit the Map entirely.
I'd like to know, how can I create a DSL, using Xtext.
This is my code, that I created:
Model:
(entities += Entity)*
(access += Acessing)*
;
Entity:
'entity' name = ID '{'
( variables += Variable )*
'}'
;
Variable:
'var' name=ID
;
Acessing:
'use' (entity = [Entity])'.'(variable = [Variable])
;
The code is a little bit incomplete, but in this way I'd like perform this operation as follows:
entity user {
var name
var phone
var address
}
use user.phone
I understand that I can use this tag [Entity] as a identifier from a specific element, but I don't know how can I get those sub elements from it.
How can I procede?
You are using the nameattribute for Entity and Variable. This is a special attribute which Xtext automatically uses to create namespaces and delivers a working scope provider for free. Elements are identified by their qualified name. You need only a single reference to access them.
To solve your problem, you only have to modify your Use grammar rule and introduce a rule which describes a qualified name. Your grammar then could look like this:
Model:
(entities+=Entity)*
(access+=Acessing)*;
Entity:
'entity' name=ID '{'
(variables+=Variable)*
'}';
Variable:
'var' name=ID;
Acessing:
'use' var=[Variable|QualifiedName];
QualifiedName:
ID ('.' ID)*;
As you can see, it now uses the QualifiedName name to identify a variable. I have just tried it and it works out of the box.
Let's say I have the following EF code:
context.Employees.Select(e => e
{
FullName = e.FirstName + " " + e.LastName,
StartDate = e.StartDate,
... // Grab other data
};
Now maybe I notice that I construct the full name in multiple places, but would like in centralized. Is it possible to refactor this?
If I make it a method or a Func, I get EF errors, because it can't translate it into SQL.
NOTE: This is a simple example, assume it can get much more complicated with "Select"s, "Where"s, whatever in the assignment, so adding a ToList and then running additional code would be suboptimal and does not fit the definition of refactoring since I would have to change functionality and not just make it more maintainable.
One solution is to use the AsExpandable method from LinqKit:
Expression<Func<Employee,string>> fullName = e => e.FirstName + " " + e.LastName;
context.Employees.AsExpandable().Select(e => e
{
FullName = fullName.Compile().Invoke(e),
StartDate = e.StartDate,
... // Grab other data
};
From the linked article:
Compile is an inbuilt method in the Expression class. It converts the
Expression into a plain Func which
satisfies the compiler. Of course, if this method actually ran, we'd
end up with compiled IL code instead of an expression tree, and LINQ
to SQL or Entity Framework would throw an exception. But here's the
clever part: Compile never actually runs; nor does LINQ to SQL or
Entity Framework ever get to see it. The call to Compile gets stripped
out entirely by a special wrapper that was created by calling
AsExpandable, and substituted for a correct expression tree.
Alternatively, you could look into creating an Model Defined Function with Entity Framework. There's also the Microsoft.Linq.Translations library if you want to define a FullName property on the Employee class itself.
I think the better centralized way to do it in the entity class itself. You can add ReadOnly property to your entity class which should be NotMapped to the database to return required formatted data.
Public class Employee
{
//...
public string fullName{get { return FirstName + " " + LastName;}}
}
I'm hoping to achieve something as follows:
var comboBoxItems = from state in states
select new
{
Key = state.Code,
Value = string.Format("{0} ({1})", state.Name, state.Code)
};
this.stateComboBox.DisplayMember = "Value";
this.stateComboBox.ValueMember = "Key";
this.stateComboBox.DataSource = new BindingSource(comboBoxItems, null);
However, it gives me the following error when it attempts to bind to the DataSource:
"LINQ to Entities does not recognize the method 'System.String
Format(System.String, System.Object, System.Object)' method, and this
method cannot be translated into a store expression."
Is there any way to include a method like string.Format() in the Anonymous Type?
var comboBoxItems = from state in states.ToList()
select new
{
Key = state.Code,
Value = string.Format("{0} ({1})", state.Name, state.Code)
};
You cannot use Format in LINQ 2 Entities as it cannot be translated to SQL. A call to ToList will cause the items to be loaded from DB and your format will now execute properly.