I use a object based on Dictionary in my application.
When debugging (and only when i inspect the Dictionary) i would like to wiew the content of the dictionary but sorted on key.
I know that i could use SortedDictionary instead of Dictionary but the performance is poor compare to Dictionary and i don't want to affect performance.
I don't want to have a "#if debug" condition either.
Is it possible ?
You can specify a DebuggerTypeProxyAttribute() on your class that is used when/if you debug. This proxy has to sort the data for you.
Article: Enhancing Debugging with the Debugger Display Attributes
Example using a (senseless) child of a Dictionary<string,int>:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
internal class Program
{
/// <summary>
/// Derives from a Dictionary that is not sorted
/// </summary>
[DebuggerTypeProxy(typeof(DictDebugView))]
public class MyDictionary : Dictionary<string, int>
{
/// <summary>
/// Prepares unsorted dummy data
/// </summary>
public void PopulateDemoData()
{
foreach (char t in "GERZWIQSFHIWE")
this[new string(t, t / 10)] = t;
}
/// <summary>
/// Is used as proxy for display
/// </summary>
internal class DictDebugView
{
private readonly SortedDictionary<string, int> sorted;
public DictDebugView(Dictionary<string, int> data)
=> sorted = new SortedDictionary<string, int>(data);
/// <summary>
/// Create the displayed KeyValuePairs
/// </summary>
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
public IList<KeyValuePair<string,int>> Keys
{
get => sorted.Select(kvp => kvp).ToList();
}
}
}
public static MyDictionary MyProp { get; } = new MyDictionary();
public static void Main(string[] args)
{
var md = new MyDictionary();
md.PopulateDemoData();
var k = new Dictionary<string,int>(md);
Console.ReadLine();
}
}
If you place a breakpoint and debug you get a sorted output for your class with internal DebuggerTypeProxy:
and unsorted output for the "normal" dictionary that does not use any proxy to display its data:
Using this in watch :
new SortedDictionary<string,object>(dictionary)
or a method Debug with that return the sortedDictionary
Try to put following line of code into watch during debugging you will find desire result.
l_oDictionary.OrderBy(key => key.Key);
Related
I have a solution with several projects, including an ASP.NET MVC project and a WPF application. In the DB, I have some general settings which I want to use in both applications. To do that, I've created a class library Foo which loads the settings into a dictionary and provides a Get(string key) method for accessing specific settings out of the dictionary.
Since the settings can be overridden by user, I've added a property containing the UserId. The Get() method automatically takes care of checking and using the UserId property. This way, I don't need to pass the UserId as a param each time I call the Get() method.
For the WPF application, this works just fine, since there is just one instance running. However for the web project, I'd like to have the dictionary filled only once (in Application_Start()) and be accessible to all users visiting the site. This works fine if I make the class instance static. However, that does not allow me to have different UserIds, as this would be overridden for everyone with every user that accesses the site. What's the best way to solve this?
Here's what I tried so far (very simplified):
Class Library:
public class Foo ()
{
private Dictionary<string, string> Res;
private int UserId;
public Foo ()
{
Res = DoSomeMagicAndGetMyDbValues();
}
public void SetUser (int userId)
{
UserId = userId;
}
public string Get(string key)
{
var res = Res[key];
// do some magic stuff with the UserId
return res;
}
}
Global.asax:
public static Foo MyFoo;
protected void Application_Start()
{
MyFoo = new Foo();
}
UserController.cs:
public ActionResult Login(int userId)
{
MvcApplication.MyFoo.SetUser(userId); // <-- this sets the same UserId for all instances
}
What about storing the settings in a Dictionary<int<Dictionary<string, string>>, where the Key of the outer dictionary is the UserId, with key 0 saved for the default settings? Of course this means you'd have to pass the user id to the Get and Set methods...
Then, you could possibly do something like this:
public static class Foo
{
private static Dictionary<int, Dictionary<string, string>> settings;
/// <summary>
/// Populates settings[0] with the default settings for the application
/// </summary>
public static void LoadDefaultSettings()
{
if (!settings.ContainsKey(0))
{
settings.Add(0, new Dictionary<string, string>());
}
// Some magic that loads the default settings into settings[0]
settings[0] = GetDefaultSettings();
}
/// <summary>
/// Adds a user-defined key or overrides a default key value with a User-specified value
/// </summary>
/// <param name="key">The key to add or override</param>
/// <param name="value">The key's value</param>
public static void Set(string key, string value, int userId)
{
if (!settings.ContainsKey(userId))
{
settings.Add(userId, new Dictionary<string, string>());
}
settings[userId][key] = value;
}
/// <summary>
/// Gets the User-defined value for the specified key if it exists,
/// otherwise the default value is returned.
/// </summary>
/// <param name="key">The key to search for</param>
/// <returns>The value of specified key, or empty string if it doens't exist</returns>
public static string Get(string key, int userId)
{
if (settings.ContainsKey(userId) && settings[userId].ContainsKey(key))
{
return settings[userId][key];
}
return settings[0].ContainsKey(key) ? settings[0][key] : string.Empty;
}
}
I am trying to access some specific variable that are only available in a child class. But the problem is that I recieve the parent of this class by parameter. Even with casting I can't seem to be able to access the members. Can it be done?
public class ENUMTranslator : ITranslate<RedisData>
{
public string Translate(RedisData message)
{
string bitMask = message.AssociatedParam.ParamDictionary["Bitmask"];
var enumerations = (EnumParams)message.AssociatedParam.EnumDictionary
}
}
The thing is that the data is not in message itself but inside AssociatedParam Which is the parent class of EnumParams.
The EnumDictionary is what I am trying to access that should be in EnumParams, but I just can't access it.
EDIT : Here is the EnumParam class.
message.AssociatedParams
is a GAPParam
public class EnumParams : GAPParam
{
#region Class Members
/// <summary>
/// Dictionary for the enums linking name with hex value
/// </summary>
private Dictionary<string, string> _enumDictionary;
#endregion // Class Members
#region Properties
/// <summary>
/// Dictionary for the enums linking name with hex value
/// </summary>
public Dictionary<string, string> EnumDictionary
{
get { return _enumDictionary; }
set { _enumDictionary = value; }
}
#endregion // Properties
#region Constructor
/// <summary>
/// Initialise the dictionaries
/// </summary>
public EnumParams()
{
_enumDictionary = new Dictionary<string, string>();
}
#endregion // Constructor
}
I cannot see it with intellisense and it would not compile either.
Well you could cast message.AssociatedParam to an EnumParams:
var enumerations = ((EnumParams)message.AssociatedParam).EnumDictionary
but if message.AssociatedParam is not castable to an EnumParams then it will fail at runtime. Some way to mitigate the risk:
should EnumDictionary be on GAPParam instead? Even if it's virtual or abstract?
should message.AssociatedParam be an EnumDictionary instead of a GAPParam?
do a check before casting to make sure message.AssociatedParam is an EnumParams - but then what do you do if its not?
Although I know this is an easy issue and a gap in my knowledge - please keep in mind I'm writing this code to learn (and detailed explanations or best practice suggestions would greatly help in this).
Firstly, here's my class:
namespace CCQ.Crawler._2010
{
public class MSSQL
{
public MSSQL(string connectionString)
{
ConnectionString = connectionString;
}
public static string ConnectionString { get; private set; }
/// <summary>
/// Class to house statements that insert or update data into the database
/// </summary>
public class Upserts
{
/// <summary>
/// Add or update a new entry on the site collection table
/// </summary>
/// <param name="siteCollectionName"></param>
public void SiteCollection(string siteCollectionName)
{
const string queryString =
#"INSERT INTO [dbo].[SiteCollections]
([SnapShotDate]
,[SiteCollectionName]
,[SiteWebCount]
,[ContentDatabase]
,[SiteWebApplication])
VALUES
(#snapShotDate, #siteCollectionName, #siteWebCount, #contentDatabase, #siteWebApplication)";
using (var connection = new SqlConnection(ConnectionString))
{
using (var cmd = new SqlCommand(queryString, connection))
{
connection.Open();
cmd.Parameters.AddWithValue("snapShotDate", DateTime.Today.Date);
cmd.Parameters.AddWithValue("siteCollectionName", siteCollectionName);
cmd.ExecuteNonQuery();
}
}
}
}
}
}
And here's my main program file:
namespace CCQ.Crawler._2010
{
internal class Program
{
private static string _connectionString;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public static string ConnectionString
{
get
{
if (string.IsNullOrEmpty(_connectionString))
{
_connectionString = AES.DecryptFromBase64String(ConfigurationManager.AppSettings["DatastoreConnection"]);
}
return _connectionString;
}
}
static void Main(string[] args)
{
string version = Assembly.GetAssembly(typeof(Program)).GetName().Version.ToString();
Console.WriteLine("[{0}] SharePoint Crawler started with version '{1}'.", DateTime.Now.ToShortTimeString(), version);
Logger.Info(string.Format("[{0}] SharePoint Crawler started with version '{1}'.", DateTime.Now.ToShortTimeString(), version));
try
{
var query = new MSSQL(ConnectionString);
}
catch (Exception ex)
{
Logger.Error(String.Format("[{0}] {1}", DateTime.Now.ToShortTimeString(), ex.GetBaseException()));
Console.WriteLine("[{0}] {1}", DateTime.Now.ToShortTimeString(), ex.GetBaseException());
}
}
}
}
Now what I thought I could do with this class, was this:
var query = new MSSQL(ConnectionString).Upserts;
query.SiteCollection("testing");
But, well, that doesn't work. The error I'm getting when I try to declare the class is:
Class name is not valid at this point
I know there's a huge gap somewhere in my knowledge / class construction which is why, but I don't quite know where to begin - where's the error in my thinking?
In this line of code:
var query = new MSSQL(ConnectionString).Upserts;
you are actually constructing a new instance of the MSSQL class then using the syntax as if Upserts was a property on it that you are trying to access.
Instead, to instantiate your nested class, you should do something like this:
var mssql = new MSSQL("your connection string");
var query = new MSSQL.Upserts();
BUT...
The way you are storing the conection string in a static property of the enclosing class is a bit strange, and initializing the static propery in a non-static constructor is strange too.
You probably don't need to have Upserts be a nested class. Try making it a method instead.
Also, your connection string can be stored in an instance field/property rather than being static.
'query' is of type MSSQL. The SiteCollection property belongs to the Upserts type. Although you have defined the Upserts type within MSSQL as a nested type, you aren't actually using the Upserts type in your MSSQL object (for example, as a property).
The quickest way I can see to get this up and running is to add an Upserts property to your MSSQL class and instantiate it in your constructor.
public Upserts Upserts { get; private set; }
You will then be able to access the method by the following means:
query.Upserts.SiteCollection("...");
Alternatively, you could just modify Upserts to receive a connection string and instantiate it directly... this is a step toward removing MSSQL from the equation though.
I realise you're writing this to wrap your head around classes and properties, so I wont comment on whether this is or isn't a good approach to DAL.
I think there is no need to use nested class in this case. You could Try this:
/// <summary>
/// <para>MSSQL class</para>
/// </summary>
public class MSSQL
{
#region Class field declaration
private string f_connectionString;
#endregion
#region Public method
/// <summary>
/// <para>Static method for getting the class instance.</para>
/// </summary>
/// <param name="p_connectionString">MSSQL connection string</param>
/// <returns><see cref="MSSQL"/></returns>
public static MSSQL Create(string p_connectionString)
{
return new MSSQL(p_connectionString);
}
public void SiteCollection(string p_siteCollectionName)
{
//Your Logic here.
}
#endregion
#region Constructor
/// <summary>
/// <para>Hide the default constructor</para>
/// </summary>
private MSSQL()
{
}
/// <summary>
/// <para>Private constructor for Static method</para>
/// </summary>
/// <param name="p_connectionString">MSSQL connection string</param>
private MSSQL(string p_connectionString)
{
this.f_connectionString = p_connectionString;
}
#endregion
}
By this code, you could create the instance of this class by:
var instance = MSSQL.Create("ConnectionString");
Get the SiteCollection by:
var siteCollection = instance.SiteCollection("Testing");
How can I document the key usage in a Dictionary so that it shows in Visual studio when coding using that object?
I'm looking for something like:
/// <param name="SpecialName">That Special Name</param>
public Dictionary<string,string> bar;
So far the best attempt has been to write my own class:
public class SpecialNameDictionary : IDictionary<string, string>
{
private Dictionary<string, string> data = new Dictionary<string, string>();
/// <param name="specialName">That Special Name</param>
public string this[string specialName]
{
get
{
return data[specialName];
}
}
}
But It adds a lot of code that doesn't do anything. Additionally I must retype every Dictionary method to make it compile.
Is there a better way to achive the above?
You can define, dictionary like this:
public class SpecialNameDictionary : Dictionary<string, string>
{
/// <param name="specialName">That Special Name</param>
public new string this[string specialName]
{
get
{
return base[specialName];
}
}
}
Instead of deriving from IDictionary derive from Dictionary, and make new implementation of indexer.
Document the field like this:
/// <summary>
/// A map from the special name to the frongulator.
/// </summary>
public Dictionary<string,string> bar;
(I assume that in reality it's either not public or not a field - but the same would apply for private fields or public properties.)
You won't get IntelliSense on the indexer itself, but any usage of bar should make it reasonably clear.
Three other alternatives:
Use types which make the usage clearer (a string could be anything, but a FrongulatorSpecialName is clearer)
Make the name of the field/property itself clearer
Hide the dictionary, but add a method such as "GetFrongulatorBySpecialName"
You could inherit directly from Dictionary<> instead of IDictionary<>, that way you only need to re-implement the indexer.
I have a dictionary of strings that i want the user to be able to add/remove info from then store it for them so it they can access it the next time the program restarts
I am unclear on how i can store a dictionary as a setting. I see that under system.collections.special there is a thing called a stringdictionary but ive read that SD are outdated and shouldn't be used.
also in the future i may have need to store a dictionary that is not strings only (int string)
how would you store a dictionary in the settings file for a .net application?
You can use this class derived from StringDictionary. To be useful for application settings it implements IXmlSerializable.
Or you can use similar approach to implement your own XmlSerializable class.
public class SerializableStringDictionary : System.Collections.Specialized.StringDictionary, System.Xml.Serialization.IXmlSerializable
{
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
while (reader.Read() &&
!(reader.NodeType == System.Xml.XmlNodeType.EndElement && reader.LocalName == this.GetType().Name))
{
var name = reader["Name"];
if (name == null)
throw new FormatException();
var value = reader["Value"];
this[name] = value;
}
}
public void WriteXml(System.Xml.XmlWriter writer)
{
foreach (System.Collections.DictionaryEntry entry in this)
{
writer.WriteStartElement("Pair");
writer.WriteAttributeString("Name", (string)entry.Key);
writer.WriteAttributeString("Value", (string)entry.Value);
writer.WriteEndElement();
}
}
}
Resulting XML fragment will look similar to:
...
<setting name="PluginSettings" serializeAs="Xml">
<value>
<SerializableStringDictionary>
<Pair Name="property1" Value="True" />
<Pair Name="property2" Value="05/01/2011 0:00:00" />
</SerializableStringDictionary>
</value>
</setting>
...
The simplest answer would be to use a row & column delimiter to convert your dictionary to a single string. Then you just need to store 1 string in the settings file.
If you don't need to use the settings designer or edit your settings with a text editor, you can create a simple class that derives from ApplicationSettingsBase:
namespace MyNamespace
{
using System.Collections.Generic;
using System.Configuration;
/// <summary>
/// Persistent store for my parameters.
/// </summary>
public class MySettings : ApplicationSettingsBase
{
/// <summary>
/// The instance lock.
/// </summary>
private static readonly object InstanceLock = new object();
/// <summary>
/// The instance.
/// </summary>
private static MySettings instance;
/// <summary>
/// Prevents a default instance of the <see cref="MySettings"/> class
/// from being created.
/// </summary>
private MySettings()
{
// don't need to do anything
}
/// <summary>
/// Gets the singleton.
/// </summary>
public static MySettings Instance
{
get
{
lock (InstanceLock)
{
if (instance == null)
{
instance = new MySettings();
}
}
return instance;
}
}
/// <summary>
/// Gets or sets the parameters.
/// </summary>
[UserScopedSetting]
[SettingsSerializeAs(SettingsSerializeAs.Binary)]
public Dictionary<string, string> Parameters
{
get
{
return (Dictionary<string, string>)this["Parameters"];
}
set
{
this["Parameters"] = value;
}
}
}
}
The real trick is the [SettingsSerializeAs(SettingsSerializeAs.Binary)] attribute. Most (all?) classes can get serialized this way where SettingsSerializeAs.String or SettingsSerializeAs.Xml wont work for a Dictionary.
Use this in your code as you would normal settings:
// this code untested...
MySettings.Instance.Parameters["foo"] = "bar";
MySettings.Instance.Parameters.Save();
MySettings.Instance.Parameters.Reload();
string bar;
if (!MySettings.Instance.Parameters.TryGetValue("foo", out bar))
{
throw new Exception("Foobar");
}
If you want the Dictionary to serialize into something user editable, you must derive from Dictionary and play with TypeConverter (see Using Custom Classes with Application Settings).
Other than doing something like David's suggests, I would look into alternate storage for the Dictionary. Ultimately the Settings object serializes to disk.
Have you considered using XML to store your dictionary? That would provide a certain amount of extensibility if in the future you decide you want to be able to store other types of dictionaries. You might do something like:
<dictionary>
<entry key="myKey">
[whatever data you like]
</entry>
</dictionary>
Might be overkill, but you'd also be prepared in the case that you wanted to store more complex data, like custom objects.
You can also use a System.Collections.Specialized.StringCollection by putting key on even index and values on odd index.
/// <summary>
/// Emulate a Dictionary (Serialization pb)
/// </summary>
private static string getValue(System.Collections.Specialized.StringCollection list, string key)
{
for (int i = 0; i * 2 < list.Count; i++)
{
if (list[i] == key)
{
return list[i + 1];
}
}
return null;
}
/// <summary>
/// Emulate a Dictionary (Serialization pb)
/// </summary>
private static void setValue(System.Collections.Specialized.StringCollection list, string key, string value)
{
for (int i = 0; i * 2 < list.Count; i++)
{
if (list[i] == key)
{
list[i + 1] = value;
return;
}
}
list.Add(key);
list.Add(value);
}
You could create a custom class that exposes a Dictionary as a public property. Then you can specify this custom type as the type for your setting.
Edit:
I have just read that, for some reason, a generic dictionary cannot be XML-serialized, so my solution will probably not work (I haven't tested it though...). That's strange, because a generic list can be serialized without any problem.
You could still create a custom class that can be set as a user setting, but you will need to have a list exposed as a property instead of a dictionary.
Edit: This will return a Hashtable (for whatever reason, despite being a 'DictionarySectionHandler'). However, being that Hashtables and Dictionaries are so similar, it shouldn't be a large issue (though I realize Dictionaries are newer, parameterized, etc; I would have preferred dicitonaries myself, but this is what .NET gives us).
The best answer I just found for this is here. It returns a typesafe collection witout any muddling in code to transform it, and you create an obvious (and simple) collection in your .config file. I'm using this and it's quite straight forward for any future programmer (including yourself). It allows for stronger typing and more flexibility, without any overly-complicated and unnecessary parsing.
You can store a StringCollection. It is similar to this solution.
I made 2 extension methods to convert between StringCollection and a Dictionary. This is the easiest way I could think of.
public static class Extender
{
public static Dictionary<string, string> ToDictionary(this StringCollection sc)
{
if (sc.Count % 2 != 0) throw new InvalidDataException("Broken dictionary");
var dic = new Dictionary<string, string>();
for (var i = 0; i < sc.Count; i += 2)
{
dic.Add(sc[i], sc[i + 1]);
}
return dic;
}
public static StringCollection ToStringCollection(this Dictionary<string, string> dic)
{
var sc = new StringCollection();
foreach (var d in dic)
{
sc.Add(d.Key);
sc.Add(d.Value);
}
return sc;
}
}
class Program
{
static void Main(string[] args)
{
//var sc = new StringCollection();
//sc.Add("Key01");
//sc.Add("Val01");
//sc.Add("Key02");
//sc.Add("Val02");
var sc = Settings.Default.SC;
var dic = sc.ToDictionary();
var sc2 = dic.ToStringCollection();
Settings.Default.SC = sc2;
Settings.Default.Save();
}
}