how to get TimeZoneInfo short name - c#

Is there any method to get the 3 char code from
System.TimeZoneInfo.Local
?
e.g. EDT instead of Eastern Daylight time etc.

Unfortunately, there is no easy built-in way of doing this that I know of. However, you could put something together yourself. Here's an example:
public static class TimeZoneInfoExtensions {
public static string Abbreviation(this TimeZoneInfo Source) {
var Map = new Dictionary<string, string>()
{
{"eastern standard time","est"},
{"mountain standard time","mst"},
{"central standard time","cst"},
{"pacific standard time","pst"}
//etc...
};
return Map[Source.Id.ToLower()].ToUpper();
}
}
Use as follows:
string CurrentTimeZoneAbbreviation = System.TimeZoneInfo.Local.Abbreviation();
If you need more conversions you could just plug them into the Map dictionary.
TimeZoneInfo.Id will be a string matching a given key in [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones]. If you can find a matching database online, containing the same Ids as well as the abbreviations, it would be possible to quickly extract and import the pairs (with regular expressions, for example) and drop those into the Map dictionary.

You can write something like:
var abbr = System.TimeZoneInfo.Local.TimeZoneAbbr();
And the helper for it:
public static class ConvertionHelper
{
public static String TimeZoneAbbr(this TimeZoneInfo zone)
{
var zoneName = zone.Id;/* zone.IsDaylightSavingTime(DateTime.UtcNow)
? zone.DaylightName
: zone.StandardName;*/
var zoneAbbr = zoneName.CapitalLetters();
return zoneAbbr;
}
public static String CapitalLetters(this String str)
{
return str.Transform(c => Char.IsUpper(c)
? c.ToString(CultureInfo.InvariantCulture)
: null);
}
private static String Transform(this String src, Func<Char, String> transformation)
{
if (String.IsNullOrWhiteSpace(src))
{
return src;
}
var result = src.Select(transformation)
.Where(res => res != null)
.ToList();
return String.Join("", result);
}
}

Related

is there a C# equivalent for Python's self-documenting expressions in f-strings?

Since Python 3.8 it is possible to use self-documenting expressions in f-strings like this:
>>> variable=5
>>> print(f'{variable=}')
variable=5
is there an equivalent feature in C#?
No, but you can use InterpolatedStringHandler and CallerArgumentExpression to write it yourself:
[InterpolatedStringHandler]
public ref struct SelfDocumentingStringHandler
{
StringBuilder builder;
public SelfDocumentingStringHandler(int literalLength, int formattedCount)
{
builder = new StringBuilder(literalLength);
}
public void AppendLiteral(string s)
{
builder.Append(s);
}
public void AppendFormatted<T>(T t, [CallerArgumentExpression(nameof(t))] string member = "")
{
builder.Append(member + "=");
builder.Append(t);
}
internal string GetFormattedText() => builder.ToString();
}
void Print(ref SelfDocumentingStringHandler stringHandler)
{
Console.WriteLine(stringHandler.GetFormattedText());
}
then you can use it like this:
var variable = 5;
Print($"{variable}"); // prints: variable=5
Yes.
int variable = 5;
Console.WriteLine($"variable={variable}");
That outputs:
variable=5
The key here is the $ that precedes the string literal.
To do what you want with the name coming dynamically, I'd suggest a more explicit approach of using an extension method. Try this:
public static class SelfDocExt
{
public static string SelfDoc<T>(
this T value,
[CallerArgumentExpression(nameof(value))] string name = "")
=> $"{name}={value}";
}
Then you can write this:
int variable = 5;
Console.WriteLine($"{variable.SelfDoc()}");
It solves your problem without breaking string interpolation.

Map [name,value] string values to class without reflection

I'm having a huge performance issue about mapping string property names and string property values to classes using reflection.
My issue now:
public class Person
{
public string Property1 { get; set; }
public string Property2 { get; set; }
public string Property3 { get; set; }
public string Property4 { get; set; }
// My class has around 100 properties
public string Property100 { get; set; }
}
I am mapping a key value pair collection to the class using reflection
[{"Property1": "some value"}, {"Property2": "something else"},{"Property3","Property4","value" }.....{"Property100","val"}]
It got to the point that I am now mapping around 10 000 class instances using reflection and the performance is to say it lightly bad.
Any ideas for eliminating the reflection would be greatly appreciated.
I see two options, if you need to avoid reflection for tasks like this(when code could be programatically generated).
First is Expressions I use it often, e.g. I saw some people write something like this
public class A
{
public Prop1 ...
....
public Prop100
public override ToString() => $"{nameof(Prop1)}={Prop1};...";
and so for all 100 properties, and always doing this manually.
And with Expression it can be easily automated, you just need to generate Expression for String.Concat and pass list of properties and names there.
For your example, it is not clear what are your data. How do you do lookup in the list?
Let's assume there is a dictionary<string,string>(you can transform your list of tuples to a dictionary), and all properties are strings as well.
Then we would need to generate a list assignment expressions like this
if(data.ContainsKey("Prop1")) result.Prop1 = data["Prop1"];
And the code would be complicated, anyway it would look like this
private static class CompiledDelegate<T>
{
public static Action<T, Dictionary<string, string>> initObject;
static CompiledDelegate()
{
var i = Expression.Parameter(typeof(Dictionary<string, string>), "i");
var v = Expression.Parameter(typeof(T), "v");
var propertyInfos = typeof(T).GetProperties().ToArray();
var t = new Dictionary<string, string>();
var contains = typeof(Dictionary<string, string>).GetMethod(nameof(Dictionary<string, string>.ContainsKey));
var getter = typeof(Dictionary<string, string>).GetProperties().First(x => x.GetIndexParameters().Length > 0);
var result = new List<Expression>();
foreach (var propertyInfo in propertyInfos)
{
var cst = Expression.Constant(propertyInfo.Name);
var assignExpression =
Expression.IfThen(Expression.Call(i, contains, cst),
Expression.Assign(Expression.PropertyOrField(v, propertyInfo.Name), Expression.MakeIndex(i, getter, new[] { cst })));
result.Add(assignExpression);
}
var block = Expression.Block(result);
initObject = Expression.Lambda<Action<T, Dictionary<string, string>>>(block, new ParameterExpression[] { v, i }).Compile();
}
}
It is an example, it would fail if there were non-string properties.
And it could be used like this
static void Main(string[] args)
{
var tst = new Test();
CompiledDelegate<Test>.initObject(tst, new Dictionary<string, string>
{
{ "S3", "Value3" },
{ "S2", "Value2" },
});
CompiledDelegate<Test>.initObject(tst, new Dictionary<string, string>
{
{ "S3", "Value3" },
{ "S1", "Value1" },
});
Console.ReadKey();
}
The second option is, actually, what it should be ideally imlemented like Using source generators I think such things do have to be done just in build time.
There is a lot of articles on msdn, for instance with samples. But it turned out to be not very easy to implement, even just a sample.
I can say, it didn't work for me, while I tried to do it according to samples.
In order to get it work I had to change TargetFramework to netstandard2.0, do something else...
But after all, when build was green, Visual Studio still showed an error.
Ok, it disappeared after VS restart, but still, that doesn't look very usable.
So, this is a generator, that creates a converter for every class with attribute.
It is again a sample, it doesn't check many things.
[Generator]
public class ConverterGenerator : ISourceGenerator
{
private static string mytemplate = #"using System.Collections.Generic;
using {2};
namespace GeneratedConverters
{{
public static class {0}Converter
{{
public static {0} Convert(Dictionary<string, string> data)
{{
var result = new {0}();
{1}
return result;
}}
}}
}}";
public static string GetNamespaceFrom(SyntaxNode s)
{
if (s.Parent is NamespaceDeclarationSyntax namespaceDeclarationSyntax)
{
return namespaceDeclarationSyntax.Name.ToString();
}
if (s.Parent == null)
return "";
return GetNamespaceFrom(s.Parent);
}
public void Execute(GeneratorExecutionContext context)
{
GetMenuComponents(context, context.Compilation);
}
private static void GetMenuComponents(GeneratorExecutionContext context, Compilation compilation)
{
var allNodes = compilation.SyntaxTrees.SelectMany(s => s.GetRoot().DescendantNodes());
var allClasses = allNodes.Where(d => d.IsKind(SyntaxKind.ClassDeclaration)).OfType<ClassDeclarationSyntax>();
var classes = allClasses
.Where(c => c.AttributeLists.SelectMany(a => a.Attributes).Select(a => a.Name).Any(s => s.ToString().Contains("DictionaryConverter")))
.ToImmutableArray();
foreach (var item in classes.Distinct().Take(1))
{
context.AddSource(item.Identifier.Text + "Converter", String.Format(mytemplate, item.Identifier.Text, SourceText.From(GenerateProperties(item)), GetNamespaceFrom(item)));
}
}
private static string GenerateProperties(ClassDeclarationSyntax s)
{
var properties = s.Members.OfType<PropertyDeclarationSyntax>();
return String.Join(Environment.NewLine,
properties.Select(p =>
{
var name = p.Identifier.Text;
return $"if(data.ContainsKey(\"{name}\")) result.{name} = data[\"{name}\"];";
}));
}
public void Initialize(GeneratorInitializationContext context)
{
}
}
and
static void Main(string[] args)
{
var t1 = GeneratedConverters.TestConverter.Convert(new Dictionary<string, string>
{
{ "S3", "Value3" },
{ "S2", "Value2" },
});
}
Best performance without reflection would be manual mapping.
It seems your key/value pair collection is regular JSON. So you could use the JSONTextReader from JSON.NET and read the string. Then manually map the JSON properties to the class properties.
Like so:
JsonTextReader reader = new JsonTextReader(new StringReader(jsonString));
while (reader.Read())
{
if (reader.Value != null)
{
// check reader.Value.ToString() and assign to correct class property
}
}
More info can be found on the JSON.NET website : https://www.newtonsoft.com/json/help/html/ReadingWritingJSON.htm

SharePoint CAML Query - How to read MetaInfo field values programmatically?

I am using C# and SharePoint Client Object Model to query recursively across folders to get file properties in the collection returned.
I successfully can read the value from the ListItemCollection by specifying the field name like this:
listItem["Title"]
but is there a way to read the individual values in
listItem["MetaInfo"]
which appears to have multiple custom meta data values, I suppose set by Microsoft office applications??? Can I cast it to a type to get the individual values?
I do not want to be parsing the string....
The data in the debugger for the MetaInfo feild looks like this:
display_urn\\:schemas-microsoft-com\\:office\\:office#Editor:SW|System Account
\nvti_parserversion:SR|14.0.0.7149
\nvti_folderitemcount:IR|0
ContentTypeId:SW|0x0101008B5F2095338FE647A7F89B5275681D66
vti_title:SW|Mr Foo Howe 26-03-2014
vti_author:SW|MYDOMAIN\\\\jblogs
Document Type:SW|Contract Note
vti_modifiedby:SR|SHAREPOINT\\\\system
vti_foldersubfolderitemcount:IR|0
display_urn\\:schemas-microsoft-com\\:office\\:office#Author:SW|Blogs, Jo
Thank for your help and please excuse my ignorance - it is the first time I am dealing with SharePoint :)
SharePoint CSOM API does not contain builtin method for parsing MetaInfo field value which returns string representation of metadata information that is associated with the specified client object.
You could consider the below method for parsing MetaInfo field value:
/// <summary>
/// Parse MetaInfo field value
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
private static Dictionary<string, string> ParseMetaInfo(string value)
{
return value.Split(new[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Split(new[] {":"}, StringSplitOptions.RemoveEmptyEntries))
.ToDictionary(x => x[0], x => x[1]);
}
Usage
var list = ctx.Web.Lists.GetByTitle(targetListTitle);
var items = list.GetItems(CamlQuery.CreateAllItemsQuery());
ctx.Load(items, icol => icol.Include( i => i["MetaInfo"]));
//ctx.Load(items);
ctx.ExecuteQuery();
foreach (var item in items)
{
var metaInfo = (string) item["MetaInfo"];
var metaInfoValue = ParseMetaInfo(metaInfo);
//..
}
I have marked Vadim's solution as an answer however I thought I'll post my solution, which I did before I saw his response.
The implementation below is using Extension methods and Regular Expression Match to extract the values - it is useful if you already know the fields you will be reading i.e. they are predefined - if you do not know what you'll be dealing with and they are dynamic then the answer above may be more useful to you.
Extension methods and class defining the meta info properties in the MetaInfo field you may need to retrieve:
public static class Extensions
{
public static string[] DocMetaInfoFields =
{
MetaInfoFields.Title, MetaInfoFields.Author,
MetaInfoFields.DocumentType, MetaInfoFields.ModifiedBy
};
public static string GetDocumentType(this object metaInfo)
{
var match = GetRegexMatch(metaInfo as string, MetaInfoFields.DocumentType);
return (match.Groups.Count > 1)
? match.Groups[1].Value
: string.Empty;
}
public static Dictionary<string, string> GetDocMetaProperties(this object metaInfo)
{
var properties = new Dictionary<string, string>();
foreach (var field in DocMetaInfoFields)
{
var match = GetRegexMatch(metaInfo as string, field);
properties.Add(field,
(match.Groups.Count > 1)
? match.Groups[1].Value
: string.Empty);
}
return properties;
}
public static StringBuilder FormatCamlValues(this StringBuilder sb, string valueTag, string listName,
IEnumerable<string> clientReferences)
{
foreach (var clientRef in clientReferences)
{
sb.AppendFormat(valueTag, listName, clientRef);
}
return sb;
}
public static List<ClientDocumentListItem> ToClientDocumentList(this ListItemCollection files)
{
return files.ToList().ConvertAll(ListItemToClientDocItem);
}
private static Match GetRegexMatch(string searchString, string fieldName)
{
string regexCapture = string.Format(#"^{0}:\w{{2}}\|([^.(\r|\n)]*)[\r\n|\n\r]+\w", fieldName);
return Regex.Match(searchString, regexCapture, RegexOptions.Multiline);
}
}
/// <summary>
/// Defines the field names inside the MetaInfo composite field returned while using the SharePoint client object CamlQuery() method
/// </summary>
public static class MetaInfoFields
{
public static string MetaInfoFieldName = "MetaInfo";
public static string Title = "vti_title";
public static string Author = "vti_author";
public static string DocumentType = "Document Type";
public static string ModifiedBy = "vti_modifiedby";
}
Usage
var list = ctx.Web.Lists.GetByTitle(targetListTitle);
var items = list.GetItems(CamlQuery.CreateAllItemsQuery());
ctx.Load(items, icol => icol.Include( i => i[MetaInfoFields.MetaInfoFieldName]));
//ctx.Load(items);
ctx.ExecuteQuery();
var docProperties = GetDocMetaProperties(listItem[MetaInfoFields.MetaInfoFieldName]);
var title = docProperties[MetaInfoFields.Title];
var createdBy = docProperties[MetaInfoFields.Author];
var modifiedBy = docProperties[MetaInfoFields.ModifiedBy];
var type = docProperties[MetaInfoFields.DocumentType];

What is the proper way to return two char arrays as a single string?

I have a public property in a static class which I want to use as a means of validating the user's input against illegal file and path names.
At present I have this:
private static string invalidFileNames = new string(Path.GetInvalidFileNameChars());
private static string invalidPathNames = new string(Path.GetInvalidPathChars());
private static string invalidUserInput = (invalidFileNames + invalidPathNames);
public static string InvalidUserInput
{
get { return invalidUserInput; }
}
Based on Microsoft's documentation here I am expecting to get back "<>|"<>| But all I am getting is the first "<>|.
Can anyone shed some light as to what is happening here? How do I ensure that I get both strings returned?
You could just make it a single string
using System.Linq;
public static string InvalidUserInput
{
get
{
return new string(Path.GetInvalidFileNameChars()
.Concat(Path.GetInvalidPathChars())
.Distinct()
.ToArray());
}
}
You wont see them all in a TextBox because of the terminator type chars in InvalidUserInput in your case its the \0(null teminator) that its stopping the display.
if you want to display just the ones that make sense to the user you can strip out the ones that cause the issue using Char.IsControl
Here is a static class to wrap it all up
public static class StringExtentions
{
private static string _invalidUserInput = string.Empty;
private static string _PrinatbleInvalidUserInput = string.Empty;
public static string InvalidUserInput
{
get
{
if (_invalidUserInput == string.Empty)
{
_invalidUserInput = new string(Path.GetInvalidFileNameChars()
.Concat(Path.GetInvalidPathChars())
.Distinct()
.ToArray());
}
return _invalidUserInput;
}
}
public static string GetPrinatbleInvalidUserInput
{
get
{
if (_PrinatbleInvalidUserInput == string.Empty)
{
_PrinatbleInvalidUserInput = new string(InvalidUserInput.Where(x => !char.IsControl(x)).ToArray());
}
return _PrinatbleInvalidUserInput;
}
}
public static bool IsValidUserInput(this string str)
{
return !str.Any(c => InvalidUserInput.Contains(c));
}
}
usage:
public MainWindow()
{
InitializeComponent();
string myString = "C:\\InvalidPath<";
if (!myString.IsValidUserInput())
{
MessageBox.Show(string.Format("String cannot contain {0}", StringExtentions.GetPrinatbleInvalidUserInput));
}
}
You can't see them in debugger, but you can output them to the file and see them with some better editor than notepad, like notepad++
File.WriteAllText("tmp.txt", invalidUserInput, UTF8Encoding.GetEncoding("UTF-8"));
In this case, because you are presumably going to be using the data numerous times, you would want to only record characters that are unique between both character arrays. To do this, you can use the Union method as follows:
private static string invalidUserInput = new string(Path.GetInvalidFileNameChars().Union(Path.GetInvalidPathChars()).ToArray());
Running your code gives a number of unicode characters returned:
"<>|□□□□□□□□□
□□
□□□□□□□□□□□□□□□□□□:*?\/"<>|□□□□□□□□□
□□
□□□□□□□□□□□□□□□□□□
Are you sure that you are not loosing anything due to the unicode characters?
Also, is that your exact code? If you switch the ordering of the variable initalizations it will no longer work as invalidUserInput must be evaluated after the other two (they are evaluated in the order they are defined in the code).

Enumerate regex match names/values

What is the C# equivalent of this pseudo-code?
var pattern = ...;
var lookup = new Dictionary<string, string>();
foreach (var group in new Regex(pattern).Matches())
{
lookup[group.Name] = group.Value;
}
I don't see any System.Text.RegularExpressions group-related object that exposes the group name.
What am I missing?
What I'm actually trying to do is convert a file with lines in this format:
eventName|message|date
To an IEnumerable<EventLogLine>, with EventLogLine being:
public struct EventLogLine
{
public string EventName { get; set; }
public string Message { get; set; }
public DateTime Date { get; set; }
}
And put those lines into a IDictionary<string /*EventName*/, IEnumerable<EventLogLine>>.
I just knocked this up in using LINQ. It relies on the List<string> to be filled with the lines in the file.
var lines = new List<string>();
var dict = lines.Select(l =>
{
var sp = l.Split('|');
return new EventLogLine { EventName = sp[0], Message = sp[1], Date = DateTime.Parse(sp[2]) };
})
.GroupBy(e => e.EventName)
.ToDictionary(grp => grp.Key, grp => grp.AsEnumerable());
Basically you convert each line to an EventLogLine, using the Select(), then use the GroupBy() to create your grouping based on EventName, then using the ToDictionary() to run the query and create your dictionary in the format required!
See the example in the Match.Groups MSDN article. I think you should look at Alastair's answer though, seeing as your input is so simple it would probably be easier to read the code later if you just use ReadLine and Split.
Consider using ToLookup rather than ToDictionary. Lookups work naturally with linq and generic code in general by being immutable and by exposing a a very simple API. Also, I would encapsulate the parsing into the EventLogLine struct.
As a result, the code would look like this:
IEnumerable<string> lines;
ILookup<string, EventLogLine> lookup =
lines.Select(EventLogLine.Parse).ToLookup(evtLine => evtLine.EventName);
An example consumer:
if(lookup["HorribleEvent"].Any())
Console.WriteLine("OMG, Horrible!");
foreach(var evt in lookup["FixableEvent"])
FixIt(evt);
var q = from evtName in relevantEventNames
from evt in lookup[evtName]
select MyProjection(evt);
Note that you do not need to check for key-existance, unlike for a Dictionary:
if(dictionary.ContainsKey("HorribleEvent")) //&& dictionary["HorribleEvent"].Any() sometimes needed
Console.WriteLine("OMG, Horrible!");
if(dictionary.ContainsKey("FixableEvent"))
foreach(var evt in lookup["FixableEvent"])
FixIt(evt);
var q = from evtName in relevantEventNames.Where(dictionary.ContainsKey)
from evt in dictionary[evtName]
select MyProjection(evt);
As you may notice, working with a dictionary containing IEnumerable values introduces subtle friction - ILookup is what you want!
Finally, the modified EventLogLine:
public struct EventLogLine {
public string EventName { get; private set; }
public string Message { get; private set; }
public DateTime Date { get; private set; }
public static EventLogLine Parse(string line) {
var splitline = line.Split('|');
if(splitline.Length != 3) throw new ArgumentException("Invalid event log line");
return new EventLogLine {
EventName = splitline[0],
Message = splitline[1],
Date = DateTime.Parse(splitline[2]),
};
}
}
To answer this part of your question:
I don't see any
System.Text.RegularExpressions
group-related object that exposes the
group name. What am I missing?
I have adapted Eamon Nerbonne's struct to use regular expressions:
public struct EventLogLine
{
public string EventName { get; private set; }
public string Message { get; private set; }
public DateTime Date { get; private set; }
private static Regex expectedLineFormat = new Regex(
#"^(?<eventName>[^|]*)\|(?<message>[^|]*)\|(?<date>[^|]*)$",
RegexOptions.Singleline | RegexOptions.Compiled
);
public static EventLogLine Parse(string line) {
Match match = expectedLineFormat.Match(line);
if (match.Success) {
return new EventLogLine {
EventName = match.Groups["eventName"].ToString(),
Message = match.Groups["message"].ToString(),
Date = DateTime.Parse(match.Groups["date"].ToString()
};
}
else {
throw new ArgumentException("Invalid event log line");
}
}
}
To more directly answer your original question (without commenting on your approach), as I had a similar problem...
According to Mono source code, the enumeration for the Groups indexer is based on the private Match.regex field, so you'll need to still have the Regex. But if you do, like you had above...
public static Dictionary<string, string> ToDictionary(
Regex regex, GroupCollection groups)
{
var groupDict = new Dictionary<string, string>();
foreach (string name in regex.GetGroupNames()){ //the only way to get the names
Group namedGroup = groups[name]; //test for existence
if (namedGroup.Success)
groupDict.Add(name, namedGroup.Value);
}
return groupDict;
}
or, as Linq,
regex.GetGroupNames()
.Where(name => groups[name].Success)
.ToDictionary(name => name, name => groups[name].Value)

Categories

Resources