A List inside an ExpandoObject - c#

dynamic model = new ExpandoObject();
model.Data = "asdf";
List<dynamic> listOfx = new List<dynamic>();
for (int i = 0; i < 3; i++) {
dynamic x = new ExpandoObject();
x.ID = i;
x.Name = "test" + i.ToString();
listOfx.Add(x);
}
model.listOfx = listOfx;
When I run this, I can see Data inside model, but not listOfx.
Problem: how to get a list(or IEnumerable) inside an ExpandoObject
UPDATE on Solution:
Because I couldn't see the lifOfx in the locals window I thought it wasn't working. Here (through y) you can see it is. :-)

I can't reproduce similar issues on Mono 2.10:
using System.Dynamic;
using System.Collections.Generic;
using System;
public class Program
{
public static void Main(string[] args)
{
dynamic x = new ExpandoObject();
x.Data ="test";
x.Arr = new [] { "test1","test2"};
x.Lst = new List<string> { "aap", "noot", "mies" };
Console.WriteLine(string.Join(", ", x.Arr));
Console.WriteLine(string.Join(", ", x.Lst));
}
}
Output:
/tmp # dmcs test.cs && mono test.exe
test1, test2
aap, noot, mies
I'll be retesting on windows shortly.
Update have tested the following:
the linux-compiled (dmcs) binary run on Windows with Mono 2.10: OK
the linux-compiled (dmcs) binary run on Windows with MS.NET 4.0: OK
the windows-compiled (dmcs) binary run on Windows with Mono 2.10: OK
the windows-compiled (dmcs) binary run on Windows with MS.NET 4.0: OK
the windows-compiled (csc.exe) binary run on Windows with Mono 2.10: OK
the windows-compiled (csc.exe) binary run on Windows with MS.NET 4.0: OK
On linux I have only tested the binary compiled by mono itself, but I don't anticipate any problems. Perhaps there is something subtly different about storing dynamics inside the List<>, I'll test that now

The code you have, above, works perfectly well for setting up the list. For example, adding this after your code will work fine:
// Access value inside list
Console.WriteLine(model.listOfx[1].Name);
// Iterate through list
foreach (var o in model.listOfx)
{
Console.WriteLine(o.ID);
}
For example, try the following (fully functional example):
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
public static class Test
{
public static void Main()
{
dynamic model = new ExpandoObject();
model.Data = "asdf";
List<dynamic> listOfx = new List<dynamic>();
for (int i = 0; i < 3; i++)
{
dynamic x = new ExpandoObject();
x.ID = i;
x.Name = "test" + i.ToString();
listOfx.Add(x);
}
model.listOfx = listOfx;
// Access value inside list
Console.WriteLine(model.listOfx[1].Name);
// Iterate through list
foreach (var o in model.listOfx)
{
Console.WriteLine(o.ID);
}
Console.ReadKey();
}
}
This uses your exact sample code.

The ExpandoObject supports IDictionary. You can cast it such as
IDictionary myModel =(IDictionary)Model;
So then you can iterate it.

Related

Using variables inside for loop scope outside of it. Possible in python, but not in C#

In python, I've gotten into the habit of using variables inside a for loop outside of its "scope". For example:
l = ["one", "two", "three"]
for item in l:
if item == "one":
j = item
print(j)
You can't quite do this in C#. Here are the several attempts I made:
First attempt
I declare a variable j of type string, assign the selected item to it inside the foreach loop scope and then refer back to it once I exit the foreach loop scope:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<string> l = new List<string> { "one", "two", "three" };
string j;
foreach (string item in l)
{
if (item == "one")
{
j = item;
}
}
Console.WriteLine(j);
}
}
The compiler throws an error:
Microsoft (R) Visual C# Compiler version 4.2.0-4.22252.24 (47cdc16a)
Copyright (C) Microsoft Corporation. All rights reserved.
test.cs(19,27): error CS0165: Use of unassigned local variable 'j'
Second attempt
Moving the declaration inside the foreach is also no good, because the variable is not recognized outside of the scope at all:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<string> l = new List<string> { "one", "two", "three" };
foreach (string item in l)
{
string j;
if (item == "one")
{
j = item;
}
}
Console.WriteLine(j);
}
}
The compiler throws the following error:
Microsoft (R) Visual C# Compiler version 4.2.0-4.22252.24 (47cdc16a)
Copyright (C) Microsoft Corporation. All rights reserved.
test.cs(20,27): error CS0103: The name 'j' does not exist in the current context
Third attempt:
Moving the declaration in the innermost scope and assigning the value to the variable results in a similar problem as the second attempt:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<string> l = new List<string> { "one", "two", "three" };
foreach (string item in l)
{
if (item == "one")
{
string j = item;
}
}
Console.WriteLine(j);
}
}
The compiler complains because at line 19 the variable j is not recognized.
Microsoft (R) Visual C# Compiler version 4.2.0-4.22252.24 (47cdc16a)
Copyright (C) Microsoft Corporation. All rights reserved.
test.cs(19,27): error CS0103: The name 'j' does not exist in the current context
The solution
One possible solution is as follows:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<string> l = new List<string> { "one", "two", "three" };
string j = "test";
foreach (string item in l)
{
if (item == "one")
{
j = item;
}
}
Console.WriteLine(j);
}
}
But I find this to be quite ugly and lacking robustness, because I have to assign some dummy value to j. For instance, perhaps the string "test" is recognized by other parts of my program and would make it behave in unexpected ways.
Question
Is there an elegant alternative to achieve this kind of behavior in C#, or am I missing something?
First attempt is more correct, but the compiler is telling you that in certain cases (where your collection is empty), j will never be assigned to. Your solution is nearly there, but instead of j="test", I would use j = null, and then after your foreach, make sure j is not null before using it
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<string> l = new List<string> { "one", "two", "three" };
string j = null;
foreach (string item in l)
{
if (item == "one")
{
j = item;
}
}
if(j!=null) <-- check j has been assigned to, before using it.
{
Console.WriteLine(j);
}
else
{
Console.WriteLine("Item was not found");
}
}
}
Don't write the loop at all. What is your expectation for how often an element in your list should occur1? From your sample, it appears it should be at least once, but maybe exactly once.
Then pick the appropriate LINQ method that expresses your expectations clearly:
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
List<string> l = new List<string> { "one", "two", "three" };
string j = l.Single(item=>item=="one");
Console.WriteLine(j);
}
}
The above will throw an exception if exactly one element doesn't match our criteria. You may instead pick First or Last (or with OrDefault) variants to express your requirements and expectations.
1 Or to put it another way, what is your expectations if the list is empty or no elements in the list match your search criteria?
Your python code appears to continue just assuming that neither of those are true. C# was designed to help you spot where shaky assumptions such as this are coming into play and prevent you from writing code with such assumptions.
Unassigned variables have, historically, been a massive source of hard to track down bugs because they either contain garbage (often but not always provoking an error quite soon) or a default value (which may look like a genuine value for quite some time), with the symptoms arising in a completely different location to the broken assumption.
Maybe a little more sofisticated way to fix this is to initialize the variable like this:
string j = string.Empty;
You are not really using each of the values in the loop but just get the LAST one that matches. As an alternative you could use Linq and then query your list and supply a default value for example:
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
List<string> myList = new List<string>{"one", "two", "three"};
string defaultValue = "whateverwant";
string matchMe = "one";
string j = myList.Where(item => item == matchMe)
.DefaultIfEmpty(defaultValue)
.Select(item => item).FirstOrDefault();
Console.WriteLine(j);
}
}
Using your first example you can set or check for a null for a default
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<string> l = new List<string> { "one", "two", "three" };
string j = null;// or a default value string so we avoid the ternary
foreach (string item in l)
{
if (item == "one")
{
j = item;
break; // we found one so no need to keep looping
}
}
j = string.IsNullOrEmpty(j)?"default":j
Console.WriteLine(j);
}
}

Can I create some components with a dynamic name on c#? [duplicate]

Can we create dynamic variable in C#?
I know my below code is threw error and very poor coding. But this code have small logic like create dynamic variable
var name=0;
for(i=0;i<10;i++)// 10 means grid length
{
name+i=i;
}
var xx1=name1;
var xx2=name2;
var xx3=name3;
Is it possible in c#? Create dynamic variable in c#? and change the variable name in c#? and concatenate the variable name in c#(like we can concatenate any control id or name)...
Why I need the dynamic variable name (scenario):
var variablename=""
var variablename0=No;
var variablename1=Yes;
var variablename2=No;
.
.
.
I have a gridview with multiple rows. And I need assign server side variable to every row. So I need set of variables in server side. the only I can set Text=<%# variablename+rowCount%> for every template field.
This rowCount means every grid row index.
If the grid has 2 rows, Then rowCount values are 0,1,2
Now I need to change the variablename to variablename0,variablename1,variablename2 dynamically for separate row.
C# is strongly typed so you can't create variables dynamically. You could use an array but a better C# way would be to use a Dictionary as follows. More on C# dictionaries here.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace QuickTest
{
class Program
{
static void Main(string[] args)
{
Dictionary<string, int> names = new Dictionary<string,int>();
for (int i = 0; i < 10; i++)
{
names.Add(String.Format("name{0}", i.ToString()), i);
}
var xx1 = names["name1"];
var xx2 = names["name2"];
var xx3 = names["name3"];
}
}
}
No. That is not possible. You should use an array instead:
name[i] = i;
In this case, your name+i is name[i].
Variable names should be known at compile time. If you intend to populate those names dynamically at runtime you could use a List<T>
var variables = List<Variable>();
variables.Add(new Variable { Name = inputStr1 });
variables.Add(new Variable { Name = inputStr2 });
here input string maybe any text or any list
try this one, user json to serialize and deserialize:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Script.Serialization;
namespace ConsoleApplication1
{
public class Program
{
static void Main(string[] args)
{
object newobj = new object();
for (int i = 0; i < 10; i++)
{
List<int> temp = new List<int>();
temp.Add(i);
temp.Add(i + 1);
newobj = newobj.AddNewField("item_" + i.ToString(), temp.ToArray());
}
}
}
public static class DynamicExtention
{
public static object AddNewField(this object obj, string key, object value)
{
JavaScriptSerializer js = new JavaScriptSerializer();
string data = js.Serialize(obj);
string newPrametr = "\"" + key + "\":" + js.Serialize(value);
if (data.Length == 2)
{
data = data.Insert(1, newPrametr);
}
else
{
data = data.Insert(data.Length-1, ","+newPrametr);
}
return js.DeserializeObject(data);
}
}
}
This is not possible, it will give you a compile time error,
You can use array for this type of requirement .
For your Reference :
http://msdn.microsoft.com/en-us/library/aa288453%28v=vs.71%29.aspx

Couchbase Lite 2 + JsonConvert

The following code sample writes a simple object to a couchbase lite (version 2) database and reads all objects afterwards. This is what you can find in the official documentation here
This is quite a lot of manual typing since every property of every object must be transferred to the MutableObject.
class Program
{
static void Main(string[] args)
{
Couchbase.Lite.Support.NetDesktop.Activate();
const string DbName = "MyDb";
var db = new Database(DbName);
var item = new Item { Name = "test", Value = 5 };
// Serialization HERE
var doc = new MutableDocument();
doc.SetString("Name", item.Name);
doc.SetInt("Value", item.Value);
db.Save(doc);
using (var qry = QueryBuilder.Select(SelectResult.All())
.From(DataSource.Database(db)))
{
foreach (var result in qry.Execute())
{
var resultItem = new Item
{
// Deserialization HERE
Name = result[DbName].Dictionary.GetString("Name"),
Value = result[DbName].Dictionary.GetInt("Value")
};
Console.WriteLine(resultItem.Name);
}
}
Console.ReadKey();
}
class Item
{
public string Name { get; set; }
public int Value { get; set; }
}
}
From my research Couchbase lite uses JsonConvert internally, so there might be a way to simplify all that with the help of JsonConvert.
Anything like:
var json = JsonConvert.SerializeObject(item);
var doc = new MutableDocument(json); // No overload to provide raw JSON
or maybe
var data = JsonConvert.SerializeToDict(item); // JsonConvert does not provide this
var doc = new MutableDocument(data);
Is there or is this some kind of optimization and the preferred approach is by intend?
People ask about this quite often, but Couchbase Lite does not actually store JSON strings in the database. They are stored in a different format so this would not give the benefit that you think (the JSON would need to be reparsed and then broken down into the other format). I'd been pushing for a way to serialize classes directly instead of going through dictionary objects (which seems like the ultimate goal here) but our priority is on things that enterprise clients want and this doesn't seem to be one of them. Note that for it to make it in, it needs to be implemented in C# Java and Objective-C / Swift.
I don't know about JsonConvert but there seems to be a constructor that takes IDictionary<string, object> as argument. So I would try something like this (brain-compiled):
MutableDocument CreateDocument(object data)
{
if (data == null) return null;
var propertyValues = new Dictionary<string, object>();
foreach (var property in data.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
propertyValues[property.Name] = property.GetValue(data);
}
return new MutableDocument(propertyValues);
}
See if this works.

How do I insert a statement into a block with normalized whitespace using Roslyn?

I'm new to Roslyn. I'm writing a code fix provider that transforms foreach blocks that iterate through the results of a Select, e.g.
foreach (var item in new int[0].Select(i => i.ToString()))
{
...
}
to
foreach (int i in new int[0])
{
var item = i.ToString();
...
}
To do this, I need to insert a statement at the beginning of the BlockSyntax inside the ForEachStatementSyntax that represents the foreach block. Here is my code for that:
var blockStatement = forEach.Statement as BlockSyntax;
if (blockStatement == null)
{
return document;
}
forEach = forEach.WithStatement(
blockStatment.WithStatements(
blockStatement.Statements.Insert(0, selectorStatement));
Unfortunately, doing that results in the whitespace being off:
foreach (int i in new int[0])
{
var item = i.ToString();
...
}
I Googled solutions for this. I came across this answer, which recommended using either Formatter.Format or SyntaxNode.NormalizeWhitespace.
I can't use Formatter.Format because that takes a Workspace parameter, and it looks I don't have access to a Workspace per Roslyn: Current Workspace in Diagnostic with code fix project.
I tried using NormalizeWhitespace() on the syntax root of the document, but that invasively formatted other code not related to the fix. I tried using it on just the ForEachStatementSyntax associated with the foreach block, and then calling syntaxRoot = syntaxRoot.ReplaceNode(oldForEach, newForEach), but that results in the entire foreach block not being properly indented.
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var array = new int[0];
int length = array.Length;
foreach (int i in array)
{
string item = i.ToString();
} }
}
}
So is it possible to simply insert the statement with the correct indentation in the first place, without having to format other code?
Thanks.
You can add the Formatter Annotation to the nodes that you want the formatter to run on using WithAdditionalAnnotations
blockStatement.Statements.Insert(0, selectorStatement.WithAdditionalAnnotations(Formatter.Annotation))

Why can't I assign a value to a decimal with a ternary operator?

I have a list of objects (using anonymous types) containing a decimal and an integer ID, and I'm building a function to take the sum of the decimals contained within all unique objects in the list.
This is the line of code I wrote:
var result = (objectList.Any()) ? objectList.Distinct().Sum(x => x.DecimalValue) : 0;
However, it always returns a decimal with the value 0, even though it should be returning the sum - objectList contains five identical objects in my example, so it should return the DecimalValue.
If I replace it with these two lines of code, however, it returns the DecimalValue:
decimal result = 0;
if (objectList.Any()) result = objectList.Distinct().Sum(x => x.DecimalValue);
Why does the second set of lines work, but the first line returns the wrong value? objectList.Any() is true, so it shouldn't be using the false assignment.
Update: I generated the list of anonymous types using this line of code:
var objectList = _ms.Read(cmd, x => new { DecimalValue = x.Field<decimal>("SomeColumnName"), Id = x.Field<int>("SomeColumnId") }).ToList();
_ms is a DAO for MS SQL, and it's running the lambda function that converts each datarow of the results to the anonymous typed object, which is returned in a list. I actually haven't been able to figure out how to build a list of those objects manually.
Another Update: Using Reed Copsey's reply below, I created the list manually in a way that duplicates the list that I'm getting, and I can reproduce the error with the manually created list:
var objectList = Enumerable.Range(0, 5).Select(i => new { DecimalValue = (decimal)31.45, Id = 3 }).ToList();
var result = (objectList.Any()) ? objectList.Distinct().Sum(x => x.DecimalValue) : 0;
I also tried removing the cast to decimal, and the issue is still present.
Another Update: After some further debugging I realized something I've never come across before. I set a breakpoint immediately after the line of code I mentioned that was having problems in each case. For the workaround, it returned 31.45. For the problem line, it returned 0. When I stepped into the next line, and looked at the variable again, it said 31.45. The line I set the breakpoint on did not access or modify the variable.
It would seem that both lines work, but that the debugger showed that it did not until I moved at least two lines after the code. I've never come across that before, but it shows that the code indeed does work.
There is no problem with your statement.
The issue is elsewhere, most likely in how you're constructing objectList.
For example, the following works exactly as expected, and prints "0.6" (as would be expected):
namespace Test
{
using System;
using System.Linq;
internal class TestCompile
{
private static void Main(string[] args)
{
var objectList = Enumerable.Range(0, 3).Select(i => new { ID = i, DecimalValue = i / 5.0m }).ToList();
decimal result = objectList.Any() ? objectList.Distinct().Sum(x => x.DecimalValue) : 0;
Console.WriteLine(result);
Console.ReadKey();
}
}
}
Update:
In response to your update, this also works perfectly:
private static void Main(string[] args)
{
var objectList = Enumerable.Range(0, 5).Select(i => new { DecimalValue = (decimal)31.45, Id = 3 }).ToList();
var result = (objectList.Any()) ? objectList.Distinct().Sum(x => x.DecimalValue) : 0;
Console.WriteLine(result);
Console.ReadKey();
}
It is using the exact code you pasted, and reports 31.45, as expected (due to the unique constraint). Note that, personally, I would use 31.45m instead of the cast, but it works even as written...
For what it's worth, your code works though I had to make up my own type.
[Test]
public void X()
{
var objectList = Enumerable.Range(1, 10).Select(d => new {DecimalValue = Convert.ToDecimal(d)});
var result = (objectList.Any()) ? objectList.Distinct().Sum(x => x.DecimalValue) : 0;
Assert.AreEqual(55, result);
Assert.AreEqual(typeof (decimal), result.GetType());
}
Too big for a comment so CW it is.
I just compiled a code
public class ObjectValue
{
public Decimal DecimalValue { get; set; }
}
class Program
{
static void Main(string[] args)
{
var objectList = new List<ObjectValue>() { new ObjectValue() { DecimalValue = 5 }, new ObjectValue() { DecimalValue = 5 } ,
new ObjectValue() { DecimalValue = 5 }};
var result = (objectList.Any()) ? objectList.Distinct().Sum(x => x.DecimalValue) : 0;
decimal resultDecimal = 0;
if (objectList.Any())
resultDecimal = objectList.Distinct().Sum(x => x.DecimalValue);
}
}
And result and resultDecimal are 15, in my case. I have coherent result in both cases provided, as I expected. So the error is somewhere else.

Categories