List<> of new, on-the-fly object - c#

I've got a bit of code that is giving me some trouble.
I'm trying to bundle a list of images into a zip file. The problem I'm having is that, on occasion, one of the images will be opened when it is accessed, causing an exception.
I'm pretty sure it's a timing issue, so I'm coding a 'second chance' loop to catch the images that fall through (as opposed to the existing behavior, where it halts on any error and gives back what it has thusfar).
I have the potentially erroring sections of code in a try block, as seen below
if (!Directory.Exists(physicalPath + "/" + fi.Description))
{
Directory.CreateDirectory(physicalPath + "/" + fi.Description);
}
wc.DownloadFile(source, physicalPath + "/" + fileName);
ze = zip.AddFile(physicalPath + "/" + fileName, path);
ze.FileName = fileName;
'ze' is a 'ZipEntry' from the Ionic.Zip library, and 'wc' is a WebClient.
In my catch, I need to store two pieces of information: 'source' and the string that results from 'physicalPath + "/" + filename'.
I know there's a way in .NET 4 to dynamically create a new object to hold this data, but I don't recall what it's called. This has greatly hampered my google-fu.
How can I create a dynamic object that will hold a pair of strings (preferably with property names on the variables) without creating a whole new class?

Are you referring to a Tuple?
http://msdn.microsoft.com/en-us/library/system.tuple.aspx

You can create an anonymous type like this:
var obj = new { str1 = "something", str2 = "something else" };
Console.WriteLine(string.Format("str1: {0}, str2: {1}", obj.str1, obj.str2));
Edit #1 To make an array of the anonymous type ...
var arr = new[] {
new { str1 = "a", str2 = "b" },
new { str1 = "c", str2 = "c" },
};
Edit #2 To make a Generic List out of this, you have to get a bit fancier ...
var obj = new { str1 = "", str2 = "" };
var type = typeof(List<>);
var listType = t.MakeGenericType(obj.GetType());
var list = (IList)Activator.CreateInstance(listType);
list.Add(new { str1 = "something", str2 = "something else" });
Console.WriteLine(((dynamic)list[0]).str1);

or a dynamic object? http://msdn.microsoft.com/en-us/library/dd264736.aspx

How can I create a dynamic object that will hold a pair of strings (preferably with property names on the variables) without creating a whole new class?
Use a Tuple.
// this uses a tuple to create a generic class that holds two string values.
// We also use string.Format instead of path + "/" + fileName to generate the second value.
var zipData = new Tuple<string, string>(source, string.Format("{0}/{1}", physicalPath, fileName));

Related

How do I create and manipulate a Table property for an IFC object using Xbim?

I am working off of the basic example for amending data (https://github.com/xBimTeam/XbimEssentials). The only thing I'm changing is within the code below, where I want to add an IfcPropertyTableValue instead of an IfcPropertySingleValue.
This code runs, but in XbimXplorer under the object's properties, nothing's there - it's blank.
To make sure, the example code, as well as other property types do work and do show up in Xplorer under properties.
var pSetRel = model.Instances.New<IfcRelDefinesByProperties>(r =>
{
r.GlobalId = Guid.NewGuid();
r.RelatingPropertyDefinition = model.Instances.New<IfcPropertySet>(pSet =>
{
pSet.Name = "Points";
// FOR EACH POINT i :
pSet.HasProperties.Add(model.Instances.New<IfcPropertyTableValue>(p =>
{
p.Name = "Points " + i;
// FOR EACH COORDINATE x :
p.DefiningValues.Add(new IfcText(x));
p.DefinedValues.Add(new IfcReal(-3.25));
}));
});
});
How can I make this work?
I have also tried using code to read the property, in case XbimXplorer just doesn't display tables. This code runs and prints zero lines (but works for other properties that are displayed in Xplorer):
// Try to read and print the new property
var nameObj = "my_object_name";
var checkObj = model.Instances.FirstOrDefault<IIfcBuildingElement>(d => d.Name == nameObj);
if (checkObj == null)
{
outputBox.AppendText(newLine + "Object: " + nameObj + " not found");
}
else
{
var properties = checkObj.IsDefinedBy
.Where(r => r.RelatingPropertyDefinition is IIfcPropertySet)
.SelectMany(r => ((IIfcPropertySet)r.RelatingPropertyDefinition).HasProperties)
.OfType<IIfcPropertySingleValue>();
foreach (var property in properties)
outputBox.AppendText($"Property: {property.Name}, Value: {property.NominalValue}");
}
It would also be convenient if I could add several defining/defined value pairs at once, for instance like this (similar to normal C# lists):
IEnumerable<IfcValue> definingValues = new IfcText() {"x", "y", "z", "k"};
p.DefinedValues.AddRange(definingValues);
IEnumerable<IfcValue> definedValues = new IfcReal() {0.0, 1.6, -2.5, 3.33};
p.DefinedValues.AddRange(definedValues);
However, {"x", "y", "z", "k"} is then marked with the error Cannot initialize type 'IfcText' with a collection initializer because it does not implement 'System.Collections.IEnumerable'.
I don't think xbim Xplorer displays IfcPropertyTableValues. Probably because very few BIM tools currently output TableValues so I guess it never got implemented (and you'd need to establish how to display the table in the Xplorer view - do you nest a table in a property grid?).
If you look at the Xplorer code you'll see it only supports SingleValues, ComplexValues and EnumeratedValues. You might be able to use Complex Values as a collection of multiple IfcPropertySingleValues as a workaround.
In your 2nd code sample to output the values, you're filtering out any IfcPropertyTableValues with the .OfType<IIfcPropertySingleValue>() clause so you'll never see the output. Take a look at the IFC specs for SimpleProperties: https://standards.buildingsmart.org/IFC/RELEASE/IFC4_1/FINAL/HTML/link/ifcsimpleproperty.htm
On the last question you're initializing the array with the wrong syntax. Try something like this:
var labels = new Ifc4.MeasureResource.IfcText[] { "x", "y" };
p.DefiningValues.AddRange(labels.Cast<IIfcValue>());

Interpolated strings stored in a variable [duplicate]

Can one store the template of a string in a variable and use interpolation on it?
var name = "Joe";
var template = "Hi {name}";
I then want to do something like:
var result = $template;
The reason is my templates will come from a database.
I guess that these strings will have always the same number of parameters, even if they can change. For example, today template is "Hi {name}", and tomorrow could be "Hello {name}".
Short answer: No, you cannot do what you have proposed.
Alternative 1: use the string.Format method.
You can store in your database something like this:
"Hi {0}"
Then, when you retrieve the string template from the db, you can write:
var template = "Hi {0}"; //retrieved from db
var name = "Joe";
var result = string.Format(template, name);
//now result is "Hi Joe"
With 2 parameters:
var name2a = "Mike";
var name2b = "John";
var template2 = "Hi {0} and {1}!"; //retrieved from db
var result2 = string.Format(template2, name2a, name2b);
//now result2 is "Hi Mike and John!"
Alternative 2: use a placeholder.
You can store in your database something like this:
"Hi {name}"
Then, when you retrieve the string template from the db, you can write:
var template = "Hi {name}"; //retrieved from db
var name = "Joe";
var result = template.Replace("{name}", name);
//now result is "Hi Joe"
With 3 parameters:
var name2a = "Mike";
var name2b = "John";
var template2 = "Hi {name2a} and {name2b}!"; //retrieved from db
var result2 = template2
.Replace("{name2a}", name2a)
.Replace("{name2b}", name2b);
//now result2 is "Hi Mike and John!"
Pay attention at which token you choose for your placeholders. Here I used surrounding curly brackets {}. You should find something that is unlikely to cause collisions with the rest of your text. And that depends entirely on your context.
This can be done as requested using dynamic compilation, such as through the Microsoft.CodeAnalysis.CSharp.Scripting package. For example:
var name = "Joe";
var template = "Hi {name}";
var result = await CSharpScript.EvaluateAsync<string>(
"var name = \"" + name + "\"; " +
"return $\"" + template + "\";");
Note that this approach is slow, and you'd need to add more logic to handle escaping of quotes (and injection attacks) within strings, but the above serves as a proof-of-concept.
No you can't do that since it needs name value at the time string is created (compile time). Consider using String.Format or String.Replace instead.
I just had the same need in my app so will share my solution using String.Replace(). If you're able to use LINQ then you can use the Aggregate method (which is a reducing function, if you're familiar with functional programming) combined with a Dictionary that provides the substitutions you want.
string template = "Hi, {name} {surname}";
Dictionary<string, string> substitutions = new Dictionary<string, string>() {
{ "name", "Joe" },
{ "surname", "Bloggs" },
};
string result = substitutions.Aggregate(template, (args, pair) =>
args.Replace($"{{{pair.Key}}}", pair.Value)
);
// result == "Hi, Joe Bloggs"
This works by starting with the template and then iterating over each item in the substitution dictionary, replacing the occurrences of each one. The result of one Replace() call is fed into the input to the next, until all substitutions are performed.
The {{{pair.Key}}} bit is just to escape the { and } used to find a placeholder.
This is pretty old now, but as I've just come across it it's new to me!
It's a bit overkill for what you need, but I have used Handlebars.NET for this sort of thing.
You can create quite complex templates and merge in hierarchical data structures for the context. There's rules for looping and conditional sections, partial template compositing and even helper function extension points. It also handles many data types gracefully.
There's way too much to go into here, but a short example to illustrate...
var source = #"Hello {{Guest.FirstName}}{{#if Guest.Surname}} {{Guest.Surname}}{{/if}}!";
var template = Handlebars.Compile(source);
var rec = new {
Guest = new { FirstName = "Bob", Surname = null }
};
var resultString = template(rec);
In this case the surname will only be included in the output if the value is not null or empty.
Now admittedly this is more complicated for users than simple string interpolation, but remember that you can still just use {{fieldName}} if you want to, just that you can do lots more as well.
This particular nuGet is a port of HandlebarsJs so it has a high degree of compatibility. HandlebarsJs is itself a port of Mustache - there are direct dotNet ports of Mustache but IMHO HandlebarsNET is the business.

How to call ManagementObject with more than one parameter?

What's the right syntax to get a BcdObject using ManagementObject? for single parameters I use:
var obj = new ManagementObject(#"root\WMI", string.Format("BcdObject.Id = '{0}'"), null);
But I'm not sure how to add additional parameters (is it AND, or ,, or something else?), something like:
var bcdObj = new ManagementObject(#"root\WMI",
string.Format("BcdObject.Id = '{0}' AND BcdObject.StoreFilePath = '{1}'",
"{current}", ""),
null);
This should be the way to go:
var bcdId = "{current}";
var sfp = "";
var obj = new ManagementObject(
"root\\WMI:BcdObject.Id=\"" + bcdId + "\",StoreFilePath=\"" + sfp + "\"");
Note that even if you merely put a space after the comma it won't work. Good luck!
You can pass the filter string as a second parameter in the constructor (like in your original code) but same rules apply - no spaces.

Querying a list of strings with a query string?

I have a dictionary:
<string,List<string>>
The key is the product code say "product1" then the list is a list of properties:
"Brand","10.40","64","red","S"
Then I 'can' have a list of rules/filters e.g.
var tmpFilter = new customfilters();
tmpFilter.Field = "2";
tmpFilter.Expression = ">";
tmpFilter.Filter = "10";
So for the above example this would pass because at index 2 (tmpFilter.Field) it is more than 10; then I have another object which defines which fields within the list I want to write to file. For that dictionary item I just want to write the product brand and price where the filters match.
At the moment without the filter I have:
var tmp = new custom();
tmp.Columns = "0,1";
tmp.Delimiter = ",";
tmp.Extention = ".csv";
tmp.CustomFilters = new List<customfilters>() {new customfilters(){ Field = "2", Expression = ">", Filter = "10"} };
public static void Custom(custom custom)
{
foreach (var x in Settings.Prods)
{
//Get Current Product Code
var curprod = Settings.ProductInformation[x];// the dictionary value
foreach (var column in custom.Columns)
{
var curVal = curprod[Convert.ToInt32(column)];
tsw.Write(curVal + custom.Delimiter);
}
Settings.Lines++;
tsw.WriteLine();
}
tsw.Close();
}
I only want to write the curprod if all the filters pass for that list of strings.
How I can do this?
There's a really nice Nuget package based on an example published by Microsoft, that they have decided to make really hard to find for some reason, that allows dynamic linq queries:
https://www.nuget.org/packages/System.Linq.Dynamic/1.0.2
Source:
https://github.com/kahanu/System.Linq.Dynamic
Using that you can do stuff like this very easily (note: I used strings here because the OP states they have a List<string>):
List<string> stuff = new List<string> { "10.40", "64", "5", "56", "99", "2" };
var selected = stuff.Select(s => new { d = double.Parse(s) }).Where("d > 10");
Console.WriteLine(string.Join(", ", selected.Select(s => s.d.ToString()).ToArray()));
Outputs:
10.4, 64, 56, 99
That may give you a place to start. One thing you are going to have to tackle is identifying which of your fields are numeric and should be converted to a numeric type before trying to apply your filter. Otherwise you are going to comparing as strings.

Change SSIS variable with reference in C#?

I access a read write a "SSIS variable" with C# in script task (Inside SSIS, an ETL tool. Don't worry about the SSIS part. Lets look at the C# only). Its Dts.Variables["strRope"].Value = "onFire"; Is there any way I could refer
to a SSIS variable without using this big name ? I was thinking of -
Object var = Dts.Variables["strRope"].REFERENCE_TO_VARIABLE;
Now, if I want assign a new value to Dts.Variables["strRope"], can I simply say var = (String) "Ten thousand thundering typhoons"; .
Is there any way I can do such a thing ?
EDIT Code example -
public void Main()
{
object var = Dts.Variables["strRope"].Value;
MessageBox.Show("original value = " + Dts.Variables["strRope"].Value.ToString());//original value = "Hello World"
//Try to change the value of Dts.Variables["strRope"].Value using var ???
var = (object)"Hello cruel world !";
MessageBox.Show("new value = " + Dts.Variables["strRope"].Value.ToString());//new value = ???
Dts.TaskResult = (int)ScriptResults.Success;
}
Note that var is a C# 3.0 (and above) keyword, and you can use it instead of object to save on typing and to avoid explicitly casting the type. Try using it along the following lines,
var strRope = Dts.Variables["strRope"];
if (strRope.Value == "onFire") { ... }
strRope.Value = "Ten thousand thundering typhoons";

Categories