How to incorporate a variable into a URI? - c#

Basically I'm trying to put a result of a query into a file path, into the image source.
Updated code. Now I actually get the string I want but I still have trouble with interpolating it into the Uri constructor.
Random rand = new Random();
var nrand = rand.Next(1, 4);
using (var db = new DungeonContext())
{
var query = db.Rooms.Where(b => b.RoomId == nrand).Select(b => b.RoomName);
foreach(string name in query)
{
nameOfRoom = name;
}
}
Somehow I have trouble setting images dir as Resources since I'm missing advancent properties for this folder.
roomScreen.Source = new BitmapImage(new Uri($"/images/{nameOfRoom}.png", UriKind.Relative));
Making Uri relative seemed to stop throwing exceptions, but the intended effect does not happen.

The way you're creating the URI is correct. The trouble is that you're trying to use what is likely meant to be readable text (the name of a room) in a URI and as a flle name. You didn't specify what nameOfRoom is, so I can't tell exactly why it's invalid.
There are ways around it. You could look at this answer which explains how to remove invalid characters from a file name.
But the best solution is not to have the problem. Don't use the name of the room as a file name. Your question indicates that a room has an ID, which is apparently an integer. Using that instead would be much easier:
using (var db = new DungeonContext())
{
room = db.Rooms.Where(b => b.RoomId == nrand);
}
and then
roomScreen.Source = new BitmapImage( new Uri($"/images/Room{room.RoomId}.png"));
If you want to use your code as-is, that's possible. When you do this:
nameOfRoom = db.Rooms.Where(b => b.RoomId == nrand).ToString();
you're calling ToString() on what is presumably a Room class. Unless you override that method, the result will always be the name of the class. (So it would be the same for every room.)
If you overrode the ToString method:
public override string ToString()
{
return $"Room{RoomId}";
}
then calling room.ToString() would return "Room1" or "Room2", etc.
String interpolation calls ToString(), so in that case you could just do
roomScreen.Source = new BitmapImage( new Uri($"/images/{room}.png"));
That works. One downside is that it's not explicit. Someone reading that line of code might not know that Room.ToString() is also the value used to create a file name. You might also want to use the ToString() method differently.
If you wanted to be really explicit and clear, you could create an extension method like this:
public static class RoomFileNameExtensions
{
public static string GetImageFileName(this Room room)
{
return $"Room{room.RoomId}.png";
}
}
Now the Room class isn't responsible for knowing how file names are created. You could do this:
roomScreen.Source = new BitmapImage( new Uri($"/images/{room.GetImageFileName()}"));
You can use that both when creating the file and when reading the file. It ensures that both operations will determine the file name the same way.

Apparently all I needed to do was add UriKInd.RelativeOrAbsolute to the constructor.

If it's in the project directory, utilizing the System.IO.Path namespace comes to mind - something like
Path.Combine(Environment.CurrentDirectory, $"/images/{nameOfRoom}.png")

Related

C# - Best alternative to Shell32 object for retriving extended properties

I'm really getting stuck on how to design my program, in simple term, it needs to create a list of file in a given path and then sorts them for now by date creating the respective subdirectory. The problem arises since the files are uploaded by the phone in a NAS and their creation date gets modified when uploaded to this drive. Since we are talking about photos-video or audio I tried using metadata and the best way I found to retrieve some common date stored in the metadata based on this answer is this:
internal static class FileInSorting
{
private static List<string> arrHeaders;
private static List<int> date = new List<int>() { 197, 3, 4, 5, 12, 198, 287, 208 };
private static List<FileToSort> fileToSort = new List<FileToSort>();
public static List<FileToSort> GetFilesToSort(string path)
{
Folder objFolder;
LoadHeader(path, out arrHeaders, out objFolder);
//I search for each file inside his extended property for the true creation date
//If none is found I default to what FileInfo thinks is right
foreach (Shell32.FolderItem2 item in objFolder.Items())
{
List<DateTime> tmp = new List<DateTime>();
DateTime SelectedDate;
foreach (int h in date)
{
string l = objFolder.GetDetailsOf(item, h);
if (!string.IsNullOrEmpty(l))
{
string asAscii = Encoding.ASCII.GetString(
Encoding.Convert(
Encoding.UTF8,
Encoding.GetEncoding(
Encoding.ASCII.EncodingName,
new EncoderReplacementFallback(string.Empty),
new DecoderExceptionFallback()),
Encoding.UTF8.GetBytes(l)
)
);
tmp.Add(DateTime.Parse(asAscii.Substring(0, 11)));
}
}
if (tmp.Count == 0)
SelectedDate = File.GetCreationTime(item.Path);
else
SelectedDate = tmp.Min();
fileToSort.Add(new FileToSort(item.Name, item.Path, SelectedDate));
}
return fileToSort;
}
public static void LoadHeader(string path, out List<string> arrHeaders, out Folder objFolder)
{
arrHeaders = new List<string>();
Shell32.Shell shell = new Shell32.Shell();
objFolder = shell.NameSpace(path);
for (int i = 0; i < short.MaxValue; i++)
{
string header = objFolder.GetDetailsOf(null, i);
if (!String.IsNullOrEmpty(header))
arrHeaders.Add(header);
}
}
}
I made this class just for easy use during sort but it could be completely redundant
public class FileToSort
{
public string nome { get; set; }
public string path { get; set; }
public DateTime sortDate { get; set; }
public FileToSort(string nome,string path,DateTime data)
{
this.nome = nome;
this.path = path;
this.sortDate = data;
}
}
The problem using this COM object is that is slow and not so easy to handle(maybe I'm just not able to) and as turned out on another question of mine it's not thread-safe, blocking out the option for parallel operation on multiple folders after the first sort.
For example, i'm first sorting all files in a tree structure "[YEAR]/[Month]/[Full date]" but then I would have to recreate the COM object for each "Full date" folder and sort those by type. I'm aware that after the first date sort I could start using Directory.EnumerateFile() for each of the newly created folders but I would like to see if there is a better way to "design" the code so it can be reused without writing 2 separate methods for the date sort and for the type sort, so is there a way to avoid using the Com object entirely?
Quick edit I forgot another why I'm searching for another solution:
this is a WPF application and I would really like to use a ListView binded with a single collection perhaps a FileInfo collection
The problem arises since the files are in a network and their creation date gets modified when uploaded
That's your choice, and thus your problem. If you don't want file dates to change on upload, don't change them. Windows Explorer, for example, doesn't change them by default, you get the same creation date as the source. Your own code has full access over what dates to use.
I made this class just for easy use during sort but it could be completely redundant
You should look up record. And proper .Net naming conventions (public properties should be capitalized).
it's not thread-safe, blocking out the option for parallel operation on multiple folders after the first sort
You're jumping to assumptions here. It may not be thread-safe, but nothing stops you from creating multiple objects to query through, one for each thread. Look up thread-local variables and/or statics.
but then I would have to recreate the COM object for each "Full date" folder and sort those by type
That line is a little harder to understand, but if you're saying you "need" to requery the entire filesystem again just to sort items then you're dead wrong. Sorting is a view operation, the model doesn't care about it, and what you're writing here is the model. Sorting for the view can be handled any way you want, you have the data in memory already, sort as you wish.
And I don't wish to go through your code too deep, but holy wow what is this:
string asAscii = Encoding.ASCII.GetString(
Encoding.Convert(
Encoding.UTF8,
Encoding.GetEncoding(
Encoding.ASCII.EncodingName,
new EncoderReplacementFallback(string.Empty),
new DecoderExceptionFallback()),
Encoding.UTF8.GetBytes(l)
)
);
If I had to rate it you'd be fired by the time I counted to 0... Just use the original string, what are you doing, man?

How to cache reading .csv files in C#

This may be a noob question, but I need some help. I have written two simple methods in C#: ReadCsv_IT and GetTranslation. The ReadCsv_IT method reads from a csv file. The GetTransaltion method calls the ReadCsv_IT method and returns the translated input (string key).
My problem is that in the future I will have to request a lot of times GetTranslation, but I obviously don't want to read the .csv files every time. So I was thinking about ways to use cache Memory to optimize my program, so that I don't have to read the .csv file on every request. But I am not sure how to do it and what I could do to optimize my program. Can anyone please help ?
public string ReadCsv_IT(string key)
{
string newKey = "";
using (var streamReader = new StreamReader(#"MyResource.csv"))
{
CsvReader csv = new CsvReader(streamReader);
csv.Configuration.Delimiter = ";";
List<DataRecord> rec = csv.GetRecords<DataRecord>().ToList();
DataRecord record = rec.FirstOrDefault(a => a.ORIGTITLE == key);
if (record != null)
{
//DOES THE LOCALIZATION with the help of the .csv file.
}
}
return newKey;
}
Here is the GetTranslation Method:
public string GetTranslation(string key, string culture = null)
{
string result = "";
if (culture == null)
{
culture = Thread.CurrentThread.CurrentCulture.Name;
}
if (culture == "it-IT")
{
result = ReadCsv_IT(key);
}
return result;
}
Here is also the class DataRecord.
class DataRecord
{
public string ORIGTITLE { get; set; }
public string REPLACETITLE { get; set; }
public string ORIGTOOLTIP { get; set; }
}
}
Two options IMO:
Turn your stream into an object?
In other words:
Make a class stream so you can refer to that object of the class stream.
Second:
Initialize your stream in the scope that calls for GetTranslation, and pass it on as an attribute to GetTranslation and ReadCSV_IT.
Brecht C and Thom Hubers have already given you good advice. I would like to add one more point, though: using csv files for localization in .NET is not really a good idea. Microsoft recommends using a resource-based approach (this article is a good starting point). It seems to me that you are trying to write code for something that is already built into .NET.
From a translation point of view csv files are not the best possible format either. First of all, they are not really standardized: many tools have slightly different ways to handle commas, quotes, and line breaks that are part of the translated text. Besides, translators will be tempted to open them in Excel, and -unless handled with caution- Excel will write out translations in whatever encoding it deems best.
If the project you are working on is for learning please feel free to go ahead with it, but if you are developing software that will be used by customers, updated, translated into several target languages, and redeployed, I would recommend to reconsider your internationalization approach.
#Brecht C is right, use that answer to start. When a variable has to be cached to be used by multiple threads or instances: take a look at InMemoryCache or Redis when perfomance and distribution over several clients gets an issue.

How can I provide a tab completion list, with the values generated on powershell startup?

So I have a cmdlet written in c#: Get-LivingCharacter. I want users to use this like Get-LivingCharacter -Name "Bran", but I would like to allow for the list of available characters to change. Maybe today, "Bran" is a valid name to pass in for Get-LivingCharacter, but maybe in the future it will not be. Things happen.
For convenience I want to allow tab-completion of this field. However, I can't seem to get that to work for non-const data sets. Dynamic fields don't even auto-complete the field name, nevermind the value, and I don't know a way to implement this for a non-dynamic field. Conceptually, I could generate a .ps1 file on startup given the current data set, and then load that ps1 as the module, but this feels a bit like killing a pup with a greatsword - lots of overkill. Is there a better option?
I had already implemented a similar function to the DynamicParam helper function, as reference in the comments. However, tab completion wasn't working. I was writing a minimal reproduction example, when...my tab completion worked.
It turns out, it reproducibly works/breaks based on the inclusion of a WriteDebug statement:
[Cmdlet("Get", "LivingCharacter")]
public class GetLivingCharacter : Cmdlet, IDynamicParameters
{
protected override void ProcessRecord()
{
}
public object GetDynamicParameters()
{
WriteDebug("Getting names"); // Tab completion won't work with this here - comment it out and it works.
^^^^^^^^^^
var chars = new List<String>() { "Bran", "Arya" };
var dict = new RuntimeDefinedParameterDictionary();
var attributes = new Collection<Attribute>
{
new ParameterAttribute
{
HelpMessage = "Enter a valid open name",
Mandatory = true
},
new ValidateSetAttribute(chars.ToArray()),
};
dict.Add("Name", new RuntimeDefinedParameter("Name", typeof(string), attributes));
return dict;
}
}
After some digging, the WriteDebug statement is throwing (which I assume is because it can't output while I'm typing). It then recreates the GetLivingCharacter class after I've finished the command to validate. It took a while to find since, because of the issue, I can't write the error to the console, so I had to append to a temp file instead.

Use variable value to check if particular instance exists, if not - create it

I have a list of images like this:
public List<Image> imageList = new List<Image>();
I also have a picture class in order to collect and manipulate data about the images in the list:
public Class Pic {
// properties and stuff
}
And then I have a function that takes an integer as an argument. That integer corresponds to an image in the image list. What I want to do in the function is to check if an instance of the Pic class has been created for that particular image. If not, I want to create it, using the value of the variable passed into the function. The following code obviously doesn't work, but it shows what I want:
public void doStuffWithImage(int picNumber) {
// Check if instance called pic + picNumber exists
if(pic + picNumber.toString() == null) {
// Create an instance
Pic pic + picNumber.toString() = new Pic();
}
}
Suggestions on how to accomplish this?
It seems like you're trying to create individual variables pic1, pic2, etc. you'd be better off using a dictionary:
Dictionary<int, Pic> pics = new Dictionary<int, Pic>();
public void doStuffWithImage(int picNumber) {
// Check if instance called pic + picNumber exists
if(!pics.ContainsKey(picNumber)) {
// Create an instance
pics[picNumber] = new Pic();
}
}
You need to create a "registry" of known Pics. DIctionary<int,Pic> would be good collection to hold this registry. You need to store the registry itself somewhere - perhaps in the "factory" object that registers your pictures.
class PicFactory {
private readonly IDictionary<int,Pic> knownPics = new Dictionary<int,Pic>();
public Pic GetOrCreate(int id) {
Pic res;
if (knownPics.TryGetValue(id, out res)) {
return res;
}
res = new Pic(id.ToString()); // This assumes that Pic(string) exists
knownPics.Add(id, res);
return res;
}
}
This way of implementing a registry may be too primitive for your purposes - for example, if you need your registry to be concurrent, you would need to set up some sort if a locking scheme to protect the knownPics dictionary. The class that accesses pictures would need to create an instance of PicFactory, so that it could access pictures through the GetOrCreate(id) call.
If you are using .net 4.0 or more you can use Lazy type which:
Provides support for lazy initialization.
Which means that the object will be constructed not in the moment of declaration, but when first accessed.
So you can basically declare them like
List<Lazy<Pic>> ....
See Lazy<T> and the Lazy Loading Pattern in general - this is actually a common optimization technique as it defers what can add up to a lot at startup to microdelays during runtime.
Be wary about making sure the microdelays are worth it, and I advise leaving methods about which can force loading.
If you're grabbing from a list, preface with a .Any or .Contains check, and since you're looking up by name like that, consider using a Dictionary instead

Is it possible to execute C# code represented as string?

On my form I have a button click
private void button1_Click(object sender, EventArgs e)
{
do something
}
How on the click would I load my do something from a text file, for example my text file looks like this:
MessageBox.Show("hello");
label1.Text = "Hello";
on click it does everything in my text file, if possible.
Here is a very simple example, just to prove this is possible. Basically, you use CodeDomProvider to compile source at runtime, then execute using reflection.
var provider = CodeDomProvider.CreateProvider("C#");
string src=#"
namespace x
{
using System;
public class y
{
public void z()
{
Console.WriteLine(""hello world"");
}
}
}
";
var result = provider.CompileAssemblyFromSource(new CompilerParameters(), src);
if (result.Errors.Count == 0)
{
var type = result.CompiledAssembly.GetType("x.y");
var instance = Activator.CreateInstance(type);
type.GetMethod("z").Invoke(instance, null);
}
Edit
As #Agat points out, the OP seems to require a sort of scripting framework (it makes use of label1, a property of the current object), whereas my answer above obviously does not provide that. The best I can think of is a limited solution, which would be to require dependencies to be specified explicitly as parameters in the "script". Eg, write the scripted code like this:
string src = #"
namespace x
{
using System.Windows;
public class y
{
public void z(Label label1)
{
MessageBox.Show(""hello"");
label1.Text = ""Hello"";
}
}
}
";
Now you can have the caller examine the parameters, and pass them in from the current context, again using reflection:
var result = provider.CompileAssemblyFromSource(new CompilerParameters(), src);
if (result.Errors.Count == 0)
{
var type = result.CompiledAssembly.GetType("x.y");
var instance = Activator.CreateInstance(type);
var method = type.GetMethod("z");
var args = new List<object>();
// assume any parameters are properties/fields of the current object
foreach (var p in method.GetParameters())
{
var prop = this.GetType().GetProperty(p.Name);
var field = this.GetType().GetField(p.Name);
if (prop != null)
args.Add(prop.GetValue(this, null));
else if (field != null);
args.Add(field.GetValue(this));
else
throw new InvalidOperationException("Parameter " + p.Name + " is not found");
}
method.Invoke(instance, args.ToArray());
}
Like the other answers have stated, it isn't an easy thing to implement and can possibly be done through reflection depending on how advanced your scripts are.
But no one #BrankoDimitrijevic mentioned Roslyn and it is a great tool. http://msdn.microsoft.com/en-us/vstudio/roslyn.aspx
It hasn't been updated in quite awhile (Sept.2012) and doesn't have all of the features of C# implemented, however, it did have a lot of it implemented when I played around with this release.
By adding your assembly as a reference to the scripting session, you're able to gain access to all of your assembly's types and script against them. It also supports return values so you can return any data that a scripted method generates.
You can find what isn't implemented here.
Below is a quick and dirty example of Roslyn that I just wrote and tested. Should work right out of box after installing Roslyn from NuGet. The small bloat at the initialization of the script engine can easily be wrapped up in a helper class or method.
The key is passing in a HostObject. It can be anything. Once you do, your script will have full access to the properties. Notice that you just call the properties and not the host object in the script.
Basically, your host object will contain properties of the data you need for your script. Don't necessarily think of your host object as just a single data object, but rather a configuration.
public class MyHostObject
{
public string Value1 { get; set; }
public string Value2 { get; set; }
}
public class RoslynTest
{
public void Test()
{
var myHostObject = new MyHostObject
{
Value1 = "Testing Value 1",
Value2 = "This is Value 2"
};
var engine = new ScriptEngine();
var session = engine.CreateSession(myHostObject);
session.AddReference(myHostObject.GetType().Assembly.Location);
session.AddReference("System");
session.AddReference("System.Core");
session.ImportNamespace("System");
// "Execute" our method so we can call it.
session.Execute("public string UpdateHostObject() { Value1 = \"V1\"; Value2 = \"V2\"; return Value1 + Value2;}");
var s = session.Execute<string>("UpdateHostObject()");
//s will return "V1V2" and your instance of myHostObject was also changed.
}
}
No. You can not.
At least in any simple way.
The thing you want is something like eval('do something') from javascript.
That's not possible to do with C#. C# is a language which needs compilation before execution unlike javascript (for instance).
The only way to implement that is to build your own (pretty complicated as for beginner) parser and execute it in such way.
UPDATED:
Actually, as JDB fairly noticed, that's really not the only way. I love programming! There are so many ways to make a freakky (or even sometimes that really can be necessary for some custom interesting tasks (or even learning)!) code. he he
Another approach I've got in my mind is building some .cs file, then compiling it on-the-fly and working with it as some assembly or some other module. Right.

Categories

Resources