using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using DTS.Common.Utilities.Logging;
// ReSharper disable InconsistentNaming
// ReSharper disable once CheckNamespace
namespace DTS.Common.Classes
{
public class Tags
{
///
/// represents a single tag, which is composed of an ID and a string of text
/// we don't currently let you obsolete, delete, or edit tags, just add
///
public class Tag: ICloneable
{
public Tag(string tagText, int tagId)
{
ID = tagId;
Text = tagText;
IsObsolete = false;
}
public const int INVALID_ID = -1;
public int ID { get; set; }
public string Text { get; set; }
public bool IsObsolete { get; set; }
public Tag(Tag copy)
{
ID = copy.ID;
Text = copy.Text;
IsObsolete = copy.IsObsolete;
}
public Tag(IDataRecord reader)
{
try
{
ID = Convert.ToInt32(reader["TagId"]);
IsObsolete = Convert.ToBoolean(reader["Obsolete"]);
Text = Convert.ToString(reader["TagText"]);
}
catch (Exception ex)
{
APILogger.Log(ex);
}
}
public Tag(DataRow dr)
{
try
{
IsObsolete = Convert.ToBoolean(dr["Obsolete"]);
ID = Convert.ToInt32(dr["TagId"]);
Text = Convert.ToString(dr["TagFields"]);
}
catch (Exception ex) { APILogger.Log(ex); }
}
public object Clone()
{
return new Tag(this);
}
}
private static Tags _tagsInstance;
//public static Tags TagsInstance
//{
// get
// {
// if (null == _tagsInstance) { _tagsInstance = new Tags(); }
// return _tagsInstance;
// }
//}
public static Tags GetTagsInstance(GetSqlCommandDelegate getSqlCommand)
{
if( null == _tagsInstance)
{
_tagsInstance = new Tags(getSqlCommand);
}
return _tagsInstance;
}
private static readonly object LOCK_OBJECT = new object();
public Tags(GetSqlCommandDelegate getSqlCommand)
{
_tagsLookup = new Dictionary();
UpdateList(getSqlCommand);
}
///
/// holds a cached collection of tags. This collection however is currently only populated on startup
/// and not updated except explicitly when a user adds a tag
///
// ReSharper disable once RedundantDefaultMemberInitializer
private readonly Dictionary _tagsLookup = null;
///
/// Adds a tag if not present in memory cache to db
///
///
///
public static bool AddTag(string tagText, GetSqlCommandDelegate getSqlCommand)
{
if (string.IsNullOrEmpty(tagText)) { return false; }
// is it already in the dictionary?
var tags = Tags.GetTagsInstance(getSqlCommand);
if (tags.ContainsTag(tagText)) return false;
tags.Commit(new Tag(tagText, Tag.INVALID_ID), getSqlCommand);
return true;
}
///
/// Changes the ID of a tag during database migration
///
///
///
public static bool MigrateTag(string tagText, GetSqlCommandDelegate getSqlCommand)
{
if (string.IsNullOrEmpty(tagText)) { return false; }
Tags.GetTagsInstance(getSqlCommand).Commit(new Tag(tagText, Tag.INVALID_ID), getSqlCommand);
return true;
}
///
/// commits a tag to db if doesn't already exist in db
///
///
private void Commit(Tag tag, GetSqlCommandDelegate getSqlCommand)
{
try
{
if (-1 == GetIDFromTagText(tag.Text, getSqlCommand))
{
Insert(tag, getSqlCommand);
}
else { UpdateAll(tag, getSqlCommand); }
lock (LOCK_OBJECT)
{
_tagsLookup[tag.Text] = tag;
}
}
catch (Exception ex)
{
APILogger.Log(ex);
}
}
private void UpdateAll(Tag tag, GetSqlCommandDelegate getSqlCommand)
{
//nothing to do currently? (we don't let you rename or edit, or obsolete, or delete ...)
tag.ID = GetTagIdFromText(tag.Text, getSqlCommand);
}
public delegate SqlCommand GetSqlCommandDelegate(bool bNewConnection);
///
/// inserts a tag into the db
///
///
private void Insert(Tag tag, GetSqlCommandDelegate getSqlCommand)
{
using (var cmd = getSqlCommand(true))
{
try
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "sp_TagsInsert";
#region params
cmd.Parameters.Add(new SqlParameter("@TagText", SqlDbType.NVarChar, 255) {Value = tag.Text});
cmd.Parameters.Add(new SqlParameter("@Obsolete", SqlDbType.Bit) {Value = tag.IsObsolete});
var newIdParam = new SqlParameter("@new_id", SqlDbType.Int) {Direction = ParameterDirection.Output};
cmd.Parameters.Add(newIdParam);
var errorNumberParam =
new SqlParameter("@errorNumber", SqlDbType.Int) {Direction = ParameterDirection.Output};
cmd.Parameters.Add(errorNumberParam);
var errorMessageParam =
new SqlParameter("@errorMessage", SqlDbType.NVarChar, 250)
{
Direction = ParameterDirection.Output
};
cmd.Parameters.Add(errorMessageParam);
#endregion params
cmd.ExecuteNonQuery();
if (int.Parse(errorNumberParam.Value.ToString()) != 0)
{
//errorMessageParam.Value
}
tag.ID = int.Parse(newIdParam.Value.ToString());
}
finally
{
cmd.Connection.Dispose();
}
}
}
///
/// retrieves a string text associated with an ID FROM CACHED copies
///
///
///
private string GetTagTextFromId(int id)
{
lock (LOCK_OBJECT)
{
if (_tagsLookup == null) return null;
var e = _tagsLookup.GetEnumerator();
while (e.MoveNext())
{
if (e.Current.Value.ID != id) continue;
var returnText = e.Current.Value.Text;
e.Dispose();
return returnText;
}
}
return null;
}
///
/// Gets an ID for a given tag text FROM DB
/// returns InvalidID if not found
///
///
///
private int GetTagIdFromText(string text, GetSqlCommandDelegate getSqlCommand)
{
using (var cmd = getSqlCommand(true))
{
try
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "sp_TagsGetId";
#region params
cmd.Parameters.Add(new SqlParameter("@TagText", SqlDbType.NVarChar, 255) {Value = text});
var tagIdParam = new SqlParameter("@TagId", SqlDbType.Int) {Direction = ParameterDirection.Output};
cmd.Parameters.Add(tagIdParam);
#endregion params
cmd.ExecuteNonQuery();
if (DBNull.Value.Equals(tagIdParam.Value))
{
return Tag.INVALID_ID;
}
var tagIdtemp = int.Parse(tagIdParam.Value.ToString());
return tagIdtemp == 0 ? Tag.INVALID_ID : tagIdtemp;
}
finally
{
cmd.Connection.Dispose();
}
}
}
///
/// returns true if a given tag text is contained in cached in memory tags
///
///
///
public bool ContainsTag(string text)
{
lock (LOCK_OBJECT)
{
return _tagsLookup.ContainsKey(text);
}
}
///
/// adds multiple tags at once
/// note that tags will have their start trimmed before commiting
///
///
///
public static bool[] AddRange(string[] tagText, GetSqlCommandDelegate getSqlCommand)
{
List rv = new List();
if (null == tagText || 0 == tagText.Length) { return null; }
foreach (string s in tagText)
{
var tag = s.TrimStart();
rv.Add(AddTag(tag, getSqlCommand));
}
return rv.ToArray();
}
///
/// gets an ID for a given tag text FROM DB
///
///
///
public static int GetIDFromTagText(string tagText, GetSqlCommandDelegate getSqlCommand)
{
return GetTagsInstance(getSqlCommand).GetTagIdFromText(tagText, getSqlCommand);
}
///
/// gets an array of ids given an array of tag texts
///
///
///
public static int[] GetIDsFromTagText(string[] tagText, GetSqlCommandDelegate getSqlCommand)
{
if (null == tagText || 0 == tagText.Length) { return null; }
return tagText.Select(s => s.TrimStart()).Select(text => GetIDFromTagText(text, getSqlCommand)).Where(id => id != Tag.INVALID_ID).ToArray();
}
///
/// returns a string for a given id from memory cache
/// returns null if it doesn't exist or is an invalid id
///
///
///
public static string GetTagTextFromID(int tagID, GetSqlCommandDelegate getSqlCommand)
{
if (0 > tagID || tagID == Tag.INVALID_ID)
{
// Not a valid ID
return null;
}
return GetTagsInstance(getSqlCommand).GetTagTextFromId(tagID);
}
///
/// returns an array of tag text given an array of tag ids.
/// skips invalid tags or tag text
///
///
///
public static string[] GetTagTextFromIDs(int[] tagId, GetSqlCommandDelegate getSqlCommand)
{
if (null == tagId || 0 == tagId.Length) { return new string [0]; }
return tagId.Where(i => i != Tag.INVALID_ID).Select(i => GetTagTextFromID(i,getSqlCommand)).Where(tag => !string.IsNullOrWhiteSpace(tag)).ToArray();
}
///
/// retrieves all tags and updates the cached dictionary of tags
///
public void UpdateList(GetSqlCommandDelegate getSqlCommand)
{
lock (LOCK_OBJECT)
{
_tagsLookup.Clear();
using (var cmd = getSqlCommand(true))
{
try
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "sp_TagsGet";
cmd.Parameters.Add(new SqlParameter("@TagId", SqlDbType.Int) {Value = null});
var reader = cmd.ExecuteReader();
while (reader.Read())
{
var t = new Tag(reader);
if (t.ID == 0) continue;
_tagsLookup[t.Text] = t;
}
reader.Close();
}
finally
{
cmd.Connection.Dispose();
}
}
}
}
}
}