Is it possible in C#7 to use deconstruction in a foreach-loop over a Dictionary? Something like this:
var dic = new Dictionary<string, int>{ ["Bob"] = 32, ["Alice"] = 17 };
foreach (var (name, age) in dic)
{
Console.WriteLine($"{name} is {age} years old.");
}
It doesn't seem to work with Visual Studio 2017 RC4 and .NET Framework 4.6.2:
error CS1061: 'KeyValuePair' does not contain a definition for 'Deconstruct' and no extension method 'Deconstruct' accepting a first argument of type 'KeyValuePair' could be found (are you missing a using directive or an assembly reference?)
If you don't like having to write the Deconstruct method, especially if you only need it in one place, here's how to do it as a one-liner with LINQ:
Using your original dictionary:
var dic = new Dictionary<string, int>{ ["Bob"] = 32, ["Alice"] = 17 };
You can do it like this:
foreach (var (name, age) in dic.Select(x => (x.Key, x.Value)))
{
Console.WriteLine($"{name} is {age} years old.");
}
First you have to add an extension method for KeyValuePair:
public static void Deconstruct<T1, T2>(this KeyValuePair<T1, T2> tuple, out T1 key, out T2 value)
{
key = tuple.Key;
value = tuple.Value;
}
Then you will get a different error:
error CS8179: Predefined type 'System.ValueTuple`2' is not defined or imported
According to this answer you have to install the NuGet package System.ValueTuple.
Then it should compile. However Visual Studio 2017 RC4 will say that it cannot resolve the symbol names name and age. They should hopefully fix this in a future update.
Deconstruct of KeyValuePair<TKey,TValue> is implemented in .NET Core 2.0, but not in .NET Framework (up to 4.8 preview) unfortunately.
Related
I have a TypeConverter in which I'm using context.Mapper.Map() to map two subproperties.
The properties are the same Type and use the same (another) TypeConverter. However in one the properties I need to pass some IMappingOperationsOptions.
It looks like this (simplified):
public class MyTypeConverter : ITypeConverter<A, B>
{
public B Convert(A, B, ResolutionContext context)
{
var subProp1 = context.Mapper.Map<C>(B.SomeProp);
var subProp2 = context.Mapper.Map<C>(B.SomeOtherProp, ops => ops.Items["someOption"] = "someValue");
return new B
{
SubProp1 = subProp1,
SubProp2 = subProp2
};
}
}
This was working fine in AutoMapper 8.0.0 but I'm upgrading to AutoMapper 10.1.1 (last version with .NET framework support).
In this newer version of AutoMapper the overload method to pass IMappingOperationsOptions does not exist anymore.
I could (theoretically) solve this by injecting IMapper in the constructor of the TypeResolver and use that instead of the ResolutionContext's Mapper but that doesn't feel right.
At the moment I solved the issue by temporarily updating the ResolutionContext options, but that also doesn't really feel right.
var subProp1 = context.Mapper.Map<C>(B.SomeProp);
context.Options.Items["someOption"] = "someValue";
var subProp2 = context.Mapper.Map<C>(B.SomeOtherProp);
context.Options.Remove("someOption");
Casting ((IMapper)context.Mapper).Map() crashes so that's not an option either. Is there a more elegant way to achieve this?
After migration from an old version of AutoMapper (before 5) to version 9 there is one spot which causes headache. Old implementation:
.ForMember(a => a.Definition, o =>
{
o.Condition(s => s.TypeId == DocumentationType.Medication);
o.ResolveUsing((d, ctx) => ctx.Engine.Map<MedicationDefinitionContent>(d.Content.MedicationContentData));
})
which uses this extension method:
public static class MappingExtensions
{
public static void ResolveUsing<TType>(this IMemberConfigurationExpression<TType> expression, Func<TType, ResolutionContext, object> map)
{
expression.ResolveUsing(result => map((TType)result.Value, result.Context));
}
}
I fixed the first error that that IMemberConfigurationExpression needs 3 arguments, but then I learned that ResolutionContext does not contain a definition for engine anymore. I looked in the upgrade guide of version 5 and found that the ResolutionContext has been changed, but I do not understand how to fix this. The code seems to be pretty tricky. Can someone help, please?
#Lucian Bargaoanu
Ok, but the member "Definition" is the member wie map with MapFrom(s => s.Content.MedicationContentData). So different to the exception there is already a mapping. The member "Definition" is of type SerialisationHelper a helper class for Json stuff. It also has a mapping.
CreateMap<MedicationDefinitionContent, SerialisationHelper>()
.IgnoreAllUnmapped()
.AfterMap((s, t) => t.Write = s);
And MedicationDefinitionContent has a separate mapping.
CreateMap<MedicationContentData, MedicationDefinitionContent>()
MedicationDefinitionContent is annotated with [JsonObject(MemberSerialization.OptIn)]
so, a direct mapping from MedicationDefinitionContent to "Definition" does not work.
How you see I try to understand it, but maybe it needs more time.
Another question on migrating code from v3 to v4:
For v3, I had a customized error reporting, using code like this (in the grammar file):
#members {
public void displayRecognitionError(String[] tokenNames,
RecognitionException e) {
String hdr = getErrorHeader(e);
String msg = getErrorMessage(e, tokenNames);
System.out.println("ERR:"+hdr+":"+msg);
errCount += 1;
}
}
In v4, when compiling the generated java files, I am getting the error:
MyParser.java:163: cannot find symbol
symbol : method getErrorMessage(org.antlr.v4.runtime.RecognitionException,java.lang.String[])
location: class MyParser
String msg = getErrorMessage(e, tokenNames);
^
Is this function replaced by some other function in v4? (I saw some questions and answers on ANTLRErrorListener, but I could not get how to use it for my situation.)
The displayRecognitionError method was removed in ANTLR 4, so even if you correct the body of that method it will not do anything. You need to remove the method from your grammar entirely, and implement ANTLRErrorListener instead. The documentation includes a list of classes that implement the interface, so you can reference those and/or extend one of them to produce the desired functionality.
Once you have an instance of an ANTLRErrorListener, you can use the following code to attach it to a Parser instance.
// remove the default error listener
parser.removeErrorListeners();
// add your custom error listener
parser.addErrorListener(listener);
I have the following unit test for a WF code activity called MyCodeActivity:
[ExpectedException(typeof(ArgumentException))]
[TestMethod]
public void ShouldRequireParam()
{
//arrange
var invoker = new WorkflowInvoker(new MyCodeActivity()
{
MyInt = 2,
MyComplexObject = _complexObject
});
//act
invoker.Invoke();
//assert
Assert.Fail("Expected ArgumentException");
}
When I run the test I get the following exception
'Literal< MyComplexObject>': Literal only supports value types and the immutable type System.String. The type MyComplexObject cannot be used as a literal.
To fix the immediate problem:
MyComplexObject = _complexObject
to
MyComplexObject = new InArgument<MyComplexObject>((ctx) => _complexObject)
Further reading : http://msdn.microsoft.com/en-us/library/ee358749.aspx .
Note: You should also use the Microsoft.Activities.UnitTesting package available on NuGet. It makes IOC alot easier (seeing as WF works with the Service Locator pattern and not Dependency Injection)
I grabbed System.Linq.Dynamic.DynamicQueryable from here:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
The issue that I am running into is in code that looks like this:
var results = dataContext.GetTable<MyClass>.Select("new (MyClassID, Name, Description)").Take(5);
It appears that if that line of code is executed by multiple threads near simultaneously, Microsoft's dynamic Linq code crashes in their ClassFactory.GetDynamicClass() method, which looks like this:
public Type GetDynamicClass(IEnumerable<DynamicProperty> properties)
{
rwLock.AcquireReaderLock(Timeout.Infinite);
try
{
Signature signature = new Signature(properties);
Type type;
if (!classes.TryGetValue(signature, out type))
{
type = CreateDynamicClass(signature.properties);
classes.Add(signature, type); // <-- crashes over here!
}
return type;
}
finally
{
rwLock.ReleaseReaderLock();
}
}
The crash is a simple dictionary error: "An item with the same key has already been added."
In Ms code, The rwLock variable is a ReadWriterLock class, but it does nothing to block multiple threads from getting inside classes.TryGetValue() if statement, so clearly, the Add will fail.
I can replicate this error pretty easily in any code that creates a two or more threads that try to execute the Select("new") statement.
Anyways, I'm wondering if anyone else has run into this issue, and if there are fixes or workarounds I can implement.
Thanks.
I did the following (requires .NET 4 or later to use System.Collections.Concurrent):
changed the classes field to a ConcurrentDictionary<Signature, Type> ,
removed all the ReaderWriterLock rwLock field and all the code referring to it,
updated GetDynamicClass to:
public Type GetDynamicClass(IEnumerable<DynamicProperty> properties) {
var signature = new Signature(properties);
return classes.GetOrAdd(signature, sig => CreateDynamicClass(sig.properties));
}
removed the classCount field and updated CreateDynamicClass to use classes.Count instead:
Type CreateDynamicClass(DynamicProperty[] properties) {
string typeName = "DynamicClass" + Guid.NewGuid().ToString("N");
...