I am trying to learn to use DynamoDB with C#
I have Partial updates, Put, Delete working.
Conditional update works (If the Attributes is part of root object).
I have the following model created.
Person with a lot of attributes.
The following works:
Expression expr = new Expression();
expr.ExpressionStatement = "Age = :age";
expr.ExpressionAttributeValues[":age"] = 26;
UpdateItemOperationConfig config = new UpdateItemOperationConfig
{
ConditionalExpression = expr,
ReturnValues = ReturnValues.AllNewAttributes
};
Document updatedPerson2 = personCatalog.UpdateItem(doc, config);
But what if my condition was on the Pet Name?
I have tried a couple of approaches with no luck eg:
expr.ExpressionStatement = "Pet.Name = :name";
expr.ExpressionAttributeValues[":name"] = "Lilleper";
Hope someone can help :) Or just nudge me in the right direction.
Try externalizing the Name sub-attribute name to ExpressionAttributeNames.
Expression expr = new Expression();
expr.ExpressionStatement = "Pet.#name = :name";
expr.ExpressionAttributeNames["#name"] = "Name";
expr.ExpressionAttributeValues[":name"] = "Lilleper";
Related
I am new to using Roslyn and the truth is that I am finding it quite interesting, but when using lambda expressions I am blocked. I would like to generate a property with getters and setters with lambda, so it looks like this:
private string uiDescription;
private string uiDescription;
public override string UiDescription {
get => uiDescription ?? Name;
set => uiDescription = value;
}
Would it be possible? Thanks in advance
I already found it, so I'll leave it here in case anyone is interested. Surely it can be done better:
var variableDeclarationDescription = SyntaxFactory.VariableDeclaration(SyntaxFactory.ParseTypeName("string"))
.AddVariables(SyntaxFactory.VariableDeclarator("uiDescription"));
var fieldDeclarationDescription = SyntaxFactory.FieldDeclaration(variableDeclarationDescription)
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword));
var descriptionBody = variableDeclarationDescription.Variables.ToString()+ " ?? Name;"
var resourceGet = SyntaxFactory.IdentifierName(descriptionBody);
descriptionBody = variableDeclarationDescription.Variables.ToString() + " = value ;";
var resourceSet = SyntaxFactory.IdentifierName(descriptionBody);
var propertyGetLambda = SyntaxFactory.ArrowExpressionClause(resourceGet);
var propertySetLambda = SyntaxFactory.ArrowExpressionClause(resourceSet);
var propertyDeclarationDescription = SyntaxFactory.PropertyDeclaration(SyntaxFactory.ParseTypeName("string"), "UiDescription")
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.OverrideKeyword))
.AddAccessorListAccessors(
SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).
WithExpressionBody(propertyGetLambda),
SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration).
WithExpressionBody(propertySetLambda)
);
I hope it can serve someone in the future. Thanks
I really have tried with this but just can't quite get it to work.
I have a bunch of files that I am using Linq to parse, but some of them have a field that the others do not have. There is no way of knowing by the file name.
Here is the code that runs against the file:
var update = from d in document.Descendants("Update")
select new
{
OrderNumber = d.Element("OrderNumber").Value,
StopID = d.Element("StopID").Value,
TransmissionTime = d.Element("TransmissionTime").Value,
EventTime = d.Element("PODTime").Value,
recordCreated = d.Element("EventTime").Value,
EventType = d.Element("EventType").Value,
EventCode = d.Element("EventCode").Value,
POD = d.Element("POD").Value,
Note = d.Element("Note").Value,
CustomerID = d.Element("CustomerID").Value,
OrderID = d.Element("OrderID").Value,
StopRef = d.Element("StopRef").Value,
PieceCount = d.Element("PieceCount").Value,
TotalWeight = d.Element("TotalWeight").Value,
DriverID = d.Element("DriverID").Value
};
Now the problem is that some of them do not have the Element("PODTime") so I need a differnet linq query. I was just going to do a try/catch and if it fails run the other one (cludgy I know).
But obviously as soon as I put update in a try/catch I can no longer access it outside of it.
Normally I would just define it before the try/catch - but I can't get that to work here.
I have tried:
System.Linq.Enumerable update = new System.Linq.Enumerable();
But that is not right. If somebody could point me the right direction I would apprecaite it.
Solution:
Using Sledgehammers nudge combined with a Let:
var update = from d in document.Descendants("Update")
let elName = d.Element("PODTime")
select new
{
OrderNumber = d.Element("OrderNumber").Value,
StopID = d.Element("StopID").Value,
TransmissionTime = d.Element("TransmissionTime").Value,
EventTime = (elName != null) ? elName.Value : string.Empty,
You can use the new ?. operator for this:
d.Element("CustomerID")?.Value
Or if you can't use the newest C#, you can write a "Safe" method to wrap all your calls in:
string SafeGetValue(XElement elem)
{
if (elem == null)
return null;
return elem.Value;
}
I know how to create a local variable inside a method, for example this:
LocalDeclarationStatement(VariableDeclaration(IdentifierName("MyClass"))
.WithVariables(SingletonSeparatedList(VariableDeclarator(Identifier("nameOfvariable"))
.WithInitializer(
EqualsValueClause(
ObjectCreationExpression(IdentifierName("MyClass")).WithArgumentList(arguments)
.WithNewKeyword(Token(SyntaxKind.NewKeyword)))))));
would give me:
MyClass nameOfvariable = new MyClass();
But say that I already created a field and now I simply want to initialize it (in a method, constructor or anything) like this:
nameOfVariable = new MyClass();
How do I do this? My guess it have to do with the VariableDeclerator but I can't find a way to get it right so I can add it to a list that contains StatementSyntaxes. I can change the VariableDecleration to "VariableDeclaration(IdentifierName(""))" too but that gives me an ugly extra space infront of the statement.
It seems like I struggle with some really basic stuff of Roslyn and I try to check http://roslynquoter.azurewebsites.net/ but that feels like the forced way to do it (feels like it create a lot more code than necessary).
Update: Should clarify that I know how to create method/constructors. I'm only looking for a way to initialize a field when I only have access to the field name and field type. So the only code I want to generate is this:
myField = new MyField();
Well you're almost there, you just need to create all that. This should do what you're interested in:
const string source = #"
using System;
class MyClass
{
void Method()
{
MyClass nameOfVariable;
}
}
";
var tree = CSharpSyntaxTree.ParseText(source);
var compilation = CSharpCompilation.Create("MyCompilation", new[] { tree }, new[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location) });
var semanticModel = compilation.GetSemanticModel(tree);
var root = tree.GetRoot();
var local = root.DescendantNodes().OfType<LocalDeclarationStatementSyntax>().First();
var declaration = local.Declaration;
var declarator = declaration.Variables.First();
var identifier = SyntaxFactory.IdentifierName("MyClass");
var objectCreationExpression = SyntaxFactory.ObjectCreationExpression(identifier, SyntaxFactory.ArgumentList(), null);
var equalsValueClause = SyntaxFactory.EqualsValueClause(objectCreationExpression);
var newDeclarator = declarator.WithInitializer(equalsValueClause).WithAdditionalAnnotations(Formatter.Annotation);
var newRoot = root.ReplaceNode(declarator, newDeclarator);
var formattedRoot = Formatter.Format(newRoot, Formatter.Annotation, new AdhocWorkspace());
Console.WriteLine(formattedRoot.GetText());
Console.Read();
Some explanation: you create a new identifier MyClass which will be used in your ObjectCreationExpression. Then you wrap all that in an EqualsValueClause and you set that as an initializer to your declarator. We also add the Formatter annotation to this node so we can format it later and don't end up with whitespace issues.
All that's left then is replacing the node in your original tree, formatting it and you're done:
-------------------------------------------------------------------------------
If you instead mean that you want to put the assignment on its own separately from the declaration then you have to create a new AssignmentExpression and wrap it inside a ExpressionStatement. Typically expressions and statements are distinct concepts but this ExpressionStatement allows us to treat an expression as a statement which is important because a method's body only accepts statements.
In code, it looks like this:
internal static void Execute()
{
const string source = #"
using System;
class MyClass
{
void Method()
{
MyClass nameOfVariable, another;
}
}
";
var tree = CSharpSyntaxTree.ParseText(source);
var compilation = CSharpCompilation.Create("MyCompilation", new[] { tree }, new[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location) });
var semanticModel = compilation.GetSemanticModel(tree);
var root = tree.GetRoot();
var local = root.DescendantNodes().OfType<LocalDeclarationStatementSyntax>().First();
var method = local.Ancestors().OfType<MethodDeclarationSyntax>().First();
var variableIdentifier = SyntaxFactory.IdentifierName("nameOfVariable");
var classIdentifier = SyntaxFactory.IdentifierName("MyClass");
var objectCreationExpression = SyntaxFactory.ObjectCreationExpression(classIdentifier, SyntaxFactory.ArgumentList(), null);
var assignment = SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, variableIdentifier, objectCreationExpression);
var expressionStatement = SyntaxFactory.ExpressionStatement(assignment).WithAdditionalAnnotations(Formatter.Annotation);
var newMethod = method.AddBodyStatements(expressionStatement);
var newRoot = root.ReplaceNode(method.Body, newMethod.Body);
var formattedRoot = Formatter.Format(newRoot, Formatter.Annotation, new AdhocWorkspace());
Console.WriteLine(formattedRoot.GetText());
Console.Read();
}
Result:
After some more trying and looking I found the answer. There is something called "AssignmentExpression" that you can use.
Here is an example how to use it:
ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, IdentifierName("myField"),
ObjectCreationExpression(IdentifierName("MyClass")).WithArgumentList(arguments)
.WithNewKeyword(Token(SyntaxKind.NewKeyword))));
This would give you:
myField = new Myclass();
So now it's easy to seperate creation and assignment/initialization to two different statements.
Note that I'm using "using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;" so I don't have to write SyntaxFactory all the time.
Or you can goto "http://roslynquoter.azurewebsites.net/" and paste your code in the small little textbox and click "Get Roslyn API calls to generate this code".
(I can generate the code you posted above, but it is kinda long. so i use a simple example.
For example, let's say you paste "DateTime mydate2 = new DateTime()", the tool will generate the following code :-
LocalDeclarationStatement(
VariableDeclaration(
IdentifierName("DateTime"))
.WithVariables(
SingletonSeparatedList<VariableDeclaratorSyntax>(
VariableDeclarator(
Identifier("mydate2"))
.WithInitializer(
EqualsValueClause(
ObjectCreationExpression(
IdentifierName("DateTime"))
.WithArgumentList(
ArgumentList())))))).WithSemicolonToken(
MissingToken(SyntaxKind.SemicolonToken)).NormalizeWhitespace()
Then you just have to fix up the code using SyntaxFactory, for example :-
var myDeclaratyion = SyntaxFactory.LocalDeclarationStatement(
SyntaxFactory.VariableDeclaration(
SyntaxFactory.IdentifierName("DateTime")).
WithVariables(
SyntaxFactory.SingletonSeparatedList<VariableDeclaratorSyntax>(
SyntaxFactory.VariableDeclarator(
SyntaxFactory.Identifier("mydate2")).
WithInitializer(
SyntaxFactory.EqualsValueClause(
SyntaxFactory.ObjectCreationExpression(
SyntaxFactory.IdentifierName("DateTime"))
.WithArgumentList(
SyntaxFactory.ArgumentList())))))).WithSemicolonToken(SyntaxFactory.MissingToken(SyntaxKind.SemicolonToken)).NormalizeWhitespace();
There is, for example, the ViewBag property of ControllerBase class and we can dynamically get/set values and add any number of additional fields or properties to this object, which is cool. I want to use something like that, beyond MVC application and Controller class in other types of applications. When I tried to create dynamic object and set its property like this:
1. dynamic MyDynamic = new { A="a" };
2. MyDynamic.A = "asd";
3. Console.WriteLine(MyDynamic.A);
I've got RuntimeBinderException with message Property or indexer '<>f__AnonymousType0.A' cannot be assigned to -- it is read only in line 2. Also, I suspect it's not quite what I'm looking for. Maybe is there some class which allows me to do something like:
??? MyDynamic = new ???();
MyDynamic.A = "A";
MyDynamic.B = "B";
MyDynamic.C = DateTime.Now;
MyDynamic.TheAnswerToLifeTheUniverseAndEverything = 42;
with dynamic adding and setting properties.
dynamic MyDynamic = new System.Dynamic.ExpandoObject();
MyDynamic.A = "A";
MyDynamic.B = "B";
MyDynamic.C = "C";
MyDynamic.Number = 12;
MyDynamic.MyMethod = new Func<int>(() =>
{
return 55;
});
Console.WriteLine(MyDynamic.MyMethod());
Read more about ExpandoObject class and for more samples: Represents an object whose members can be dynamically added and removed at run time.
I recently had a need to take this one step further, which was to make the property additions in the dynamic object, dynamic themselves, based on user defined entries. The examples here, and from Microsoft's ExpandoObject documentation, do not specifically address adding properties dynamically, but, can be surmised from how you enumerate and delete properties. Anyhow, I thought this might be helpful to someone. Here is an extremely simplified version of how to add truly dynamic properties to an ExpandoObject (ignoring keyword and other handling):
// my pretend dataset
List<string> fields = new List<string>();
// my 'columns'
fields.Add("this_thing");
fields.Add("that_thing");
fields.Add("the_other");
dynamic exo = new System.Dynamic.ExpandoObject();
foreach (string field in fields)
{
((IDictionary<String, Object>)exo).Add(field, field + "_data");
}
// output - from Json.Net NuGet package
textBox1.Text = Newtonsoft.Json.JsonConvert.SerializeObject(exo);
dynamic myDynamic = new { PropertyOne = true, PropertyTwo = false};
ExpandoObject is what are you looking for.
dynamic MyDynamic = new ExpandoObject(); // note, the type MUST be dynamic to use dynamic invoking.
MyDynamic.A = "A";
MyDynamic.B = "B";
MyDynamic.C = "C";
MyDynamic.TheAnswerToLifeTheUniverseAndEverything = 42;
var data = new { studentId = 1, StudentName = "abc" };
Or value is present
var data = new { studentId, StudentName };
You can use ExpandoObject Class which is in System.Dynamic namespace.
dynamic MyDynamic = new ExpandoObject();
MyDynamic.A = "A";
MyDynamic.B = "B";
MyDynamic.C = "C";
MyDynamic.SomeProperty = SomeValue
MyDynamic.number = 10;
MyDynamic.Increment = (Action)(() => { MyDynamic.number++; });
More Info can be found at
ExpandoObject MSDN
dynamic MyDynamic = new ExpandoObject();
I have the following code and it is working fine. However I am new to using "IEnumerable code" and it would seem obvious that it could be done better.
Basically I want all Region nodes in the XML and then the data I want to output in my Asp:repeater is nested quite deeply in the XML, but the 4 fields are all at the same level.
var xDoc = xmlDoc.ToXDocument();
var jobs = xDoc.Descendants("Region")
.Select(x => new {
jobName = x.Element("Location").Element("Department").Element("Brand").Element("Jobs").Element("Job").Element("JobName").Value,
jobType = x.Element("Location").Element("Department").Element("Brand").Element("Jobs").Element("Job").Element("JobType").Value,
jobURL = x.Element("Location").Element("Department").Element("Brand").Element("Jobs").Element("Job").Element("URL").Value,
jobClose = x.Element("Location").Element("Department").Element("Brand").Element("Jobs").Element("Job").Element("JobCLDate").Value
}
);
if (jobs.Count() > 0)
{
careersListing.DataSource = jobs;
careersListing.DataBind();
careersListing.Visible = true;
}
I would be very grateful of any feedback with respect to making it more succinct
Thanks
Nigel
You're right; this can be inefficient.
You can simplify it like this:
var jobs = from x in xDoc.Descendants("Region")
let job = x.Element("Location").Element("Department").Element("Brand").Element("Jobs").Element("Job")
select new {
jobName = job.Element("JobName").Value,
...
};
If you prefer to use method call syntax, you can pass a statement lambda expression that declares a temporary variable.
If there is just one Job element for each region (which seems to be the case), why not just query for it directly?
var jobs = xDoc.Descendants("Job")
.Select(x => new {
jobName = x.Element("JobName").Value,
jobType = x.Element("JobType").Value,
jobURL = x.Element("URL").Value,
jobClose = x.Element("JobCLDate").Value
}
);
Another minor optimization: Use Any() instead of Count():
if (jobs.Any())
{
careersListing.DataSource = jobs;
careersListing.DataBind();
careersListing.Visible = true;
}
if (jobs.Count() > 0) can be rewritten as if (jobs.Any()).