Required validation attribute and custom validators order - c#

I have 3 entities.
Let's say I have Event entity, and 2 "derived" entities: Accident, Repair.
They provide some additional fields over Event entity.
Event have StartDate and EndDate which are always required so I mark them with [Required] attribute. That's ok. But I have some additional validation logic that checks if the Event is Repair, then some other Event fields are also required. For this I provide custom validator.
The problem is that the properties marked with [Required] attribute are always checked before other validators.
What I want achieve:
If Event is Accident I want to make Event.SomeField required.
Validation summary should show now contain 3 validation errors at the very first validation attempt.
How it behaves now:
If Event is Accident first validation attempt shows 2 errors of 2 properties marked as [Required]. Only after I fill those, on the next validation attempt fires my custom validator which also states that 3rd Event.SomeField is also required.
I want all the required fields to validate at the same time.
Is this possible? How to achieve this?

For the case that a property is required based on a certain condition, i use a custom attribute, that was initially provided by Jeff Handley in his blog.
Here´s the code of the Attribute:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = true)]
public class ConditionallyRequiredAttribute : RequiredAttribute {
private MemberInfo _member;
/// <summary>
/// The name of the member that will return the state that indicates
/// whether or not the validated member is required.
/// </summary>
public string ConditionMember { get; private set; }
/// <summary>
/// The condition value under which this validator treats
/// the affected member as required.
/// </summary>
public object RequiredCondition { get; private set; }
/// <summary>
/// Comma-separated list of additional members to
/// add to validation errors. By default, the
/// <see cref="ConditionMember"/> is added.
/// </summary>
public string ErrorMembers { get; set; }
/// <summary>
/// Conditionally require a value, only when the specified
/// <paramref name="conditionMember"/> is <c>true</c>.
/// </summary>
/// <param name="conditionMember">
/// The member that must be <c>true</c> to require a value.
/// </param>
public ConditionallyRequiredAttribute(string conditionMember)
: this(conditionMember, true) { }
/// <summary>
/// Conditionally require a value, only when the specified
/// <paramref name="conditionMember"/> has a value that
/// exactly matches the <paramref name="requiredCondition"/>.
/// </summary>
/// <param name="conditionMember">
/// The member that will be evaluated to require a value.
/// </param>
/// <param name="requiredCondition">
/// The value the <paramref name="conditionMember"/> must
/// hold to require a value.
/// </param>
public ConditionallyRequiredAttribute(string conditionMember, object requiredCondition) {
this.ConditionMember = conditionMember;
this.RequiredCondition = requiredCondition;
this.ErrorMembers = this.ConditionMember;
}
/// <summary>
/// Override the base validation to only perform validation when the required
/// condition has been met. In the case of validation failure, augment the
/// validation result with the <see cref="ErrorMembers"/> as an additional
/// member names, as needed.
/// </summary>
/// <param name="value">The value being validated.</param>
/// <param name="validationContext">The validation context being used.</param>
/// <returns>
/// <see cref="ValidationResult.Success"/> if not currently required or if satisfied,
/// or a <see cref="ValidationResult"/> in the case of failure.
/// </returns>
protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
if (this.DiscoverMember(validationContext.ObjectType)) {
object state = this.InvokeMember(validationContext.ObjectInstance);
// We are only required if the current state
// matches the specified condition.
if (Object.Equals(state, this.RequiredCondition)) {
ValidationResult result = base.IsValid(value, validationContext);
if (result != ValidationResult.Success && this.ErrorMembers != null && this.ErrorMembers.Any()) {
result = new ValidationResult(result.ErrorMessage,
result.MemberNames.Union(this.ErrorMembers.Split(',').Select(s => s.Trim())));
}
return result;
}
return ValidationResult.Success;
}
throw new InvalidOperationException(
"ConditionallyRequiredAttribute could not discover member: " + this.ConditionMember);
}
/// <summary>
/// Discover the member that we will evaluate for checking our condition.
/// </summary>
/// <param name="objectType"></param>
/// <returns></returns>
private bool DiscoverMember(Type objectType) {
if (this._member == null) {
this._member = (from member in objectType.GetMember(this.ConditionMember).Cast<MemberInfo>()
where IsSupportedProperty(member) || IsSupportedMethod(member)
select member).SingleOrDefault();
}
// If we didn't find 1 exact match, indicate that we could not discover the member
return this._member != null;
}
/// <summary>
/// Determine if a <paramref name="member"/> is a
/// method that accepts no parameters.
/// </summary>
/// <param name="member">The member to check.</param>
/// <returns>
/// <c>true</c> if the member is a parameterless method.
/// Otherwise, <c>false</c>.
/// </returns>
private bool IsSupportedMethod(MemberInfo member) {
if (member.MemberType != MemberTypes.Method) {
return false;
}
MethodInfo method = (MethodInfo)member;
return method.GetParameters().Length == 0
&& method.GetGenericArguments().Length == 0
&& method.ReturnType != typeof(void);
}
/// <summary>
/// Determine if a <paramref name="member"/> is a
/// property that has no indexer.
/// </summary>
/// <param name="member">The member to check.</param>
/// <returns>
/// <c>true</c> if the member is a non-indexed property.
/// Otherwise, <c>false</c>.
/// </returns>
private bool IsSupportedProperty(MemberInfo member) {
if (member.MemberType != MemberTypes.Property) {
return false;
}
PropertyInfo property = (PropertyInfo)member;
return property.GetIndexParameters().Length == 0;
}
/// <summary>
/// Invoke the member and return its value.
/// </summary>
/// <param name="objectInstance">The object to invoke against.</param>
/// <returns>The member's return value.</returns>
private object InvokeMember(object objectInstance) {
if (this._member.MemberType == MemberTypes.Method) {
MethodInfo method = (MethodInfo)this._member;
return method.Invoke(objectInstance, null);
}
PropertyInfo property = (PropertyInfo)this._member;
return property.GetValue(objectInstance, null);
}
#if !SILVERLIGHT
/// <summary>
/// The desktop framework has this property and it must be
/// overridden when allowing multiple attributes, so that
/// attribute instances can be disambiguated based on
/// field values.
/// </summary>
public override object TypeId {
get { return this; }
}
#endif
}
Here´s an example:
public class Dummy{
public bool IsCondition {get; set;}
[ConditionallyRequired("IsCondition", true)]
public string SometimesRequired {get; set;}
}

Related

Converter file section is null

I'm making a BRRES to BFRES converter. For some reason, the MDL0 section seems to be always null when debugging. Here is the code for the Brres file loader:
/// <summary>
/// Represents a set of properties controlling the load of a <see cref="Brres.BrresFile"/>.
/// </summary>
internal class BrresLoaderContext
{
// ---- CONSTRUCTORS -------------------------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the <see cref="BrresLoaderContext"/> class for the given <see cref="BrresFile"/>
/// instance using the given <see cref="BinaryDataReader"/>.
/// </summary>
/// <param name="BrresFile">The <see cref="BrresFile"/> instance to load the data in.</param>
/// <param name="reader">The <see cref="BinaryDataReader"/> to use for reading the data.</param>
internal BrresLoaderContext(BrresFile brresFile, BinaryDataReader reader)
{
BrresFile = brresFile;
Reader = reader;
Warnings = new List<string>();
}
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
/// <summary>
/// Gets the <see cref="BrresFile"/> instance which is loaded to.
/// </summary>
internal BrresFile BrresFile
{
get;
private set;
}
/// <summary>
/// Gets the <see cref="BinaryDataReader"/> which is used to read data from the input stream.
/// </summary>
internal BinaryDataReader Reader
{
get;
private set;
}
/// <summary>
/// Gets or sets the warnings raised after loading the Brres file.
/// </summary>
internal List<string> Warnings
{
get;
private set;
}
}
I mostly didn't update comments.
So why is it null?

Is it possible to create a ConfigurationElement from xml text?

We have a custom class that inherits from ConfigurationElement called SignalConfigurationElement and defines a bunch of properties using the ConfigurationProperty attribute.
The SignalConfigurationElement class is part of a much larger hierarchy of configuration elements and does not have a constructor. I'm able to retrieve the entire configuration through the ConfigurationManager.GetSection() call, providing the root element's name which is SystemConfiguration, which is defined in the <configSections> of the app.config file.
I don't have control over the custom configuration elements so I can't alter them to provide constructors. I also can't modify the app.config because it's used by a much larger application.
Is it possible to create instances or collections of SignalConfigurationElement given an XML string of <signals> entries? The class doesn't have a constructor so I'm assuming the ConfigurationManager.GetSection call used by our other applications to retrieve the entire configuration (not what I want) uses reflection to create its instances.
Code (Can't change any of this at all.):
App.Config
<configSections>
<section name="SystemConfiguration" type="Fully.Qualified.Namespace.SystemConfiguration, SystemConfiguration"/>
</configSections>
<SystemConfiguration id="System1" name="System 1">
<equipments>
<clear />
<add id="Equipment11" equipmentType="EQ">
<signals>
<clear />
<add id="EQ11Signal1" signalId="EQ11Signal1" type="Version" />
<add id="EQ11Signal2" signalId="EQ11Signal2" type="Status" />
<add id="EQ11Signal3" signalId="EQ11Signal3" type="Status" />
</signals>
</add>
<add id="Equipment21" equipmentType="EQ">
<signals>
<clear />
<add id="EQ21Signal1" signalId="EQ21Signal1" type="Version" />
<add id="EQ21Signal2" signalId="EQ21Signal2" type="Status" />
<add id="EQ21Signal3" signalId="EQ21Signal3" type="Status" />
</signals>
</add>
</equipments>
<!-- And a whole lot more. Somewhere in the avenue of 30,000 <signals> entries.-->
</SystemConfiguration>
Classes:
public class SystemConfigurationSection : ConfigurationSection
{
/// <summary>
/// Determines the XML tag that will contain this Configuration Section in an .config file.
/// </summary>
public const string SystemConfigurationSectionName = "SystemConfiguration";
/// <summary>
/// Instance factory method for an instance of the Configuration Section creation.
/// </summary>
/// <returns>
/// Instance of the System Configuration section created based on the .config file of the application.
/// </returns>
public static SystemConfigurationSection GetSystemConfigurationSection()
{
SystemConfigurationSection result =
(SystemConfigurationSection) ConfigurationManager.GetSection(SystemConfigurationSectionName);
return result;
}
/// <summary>
/// Represents the XML attribute used to store ID of the System.
/// </summary>
[ConfigurationProperty(IdConfigurationElementName, IsRequired = true)]
public string Id
{
get { return (string) this[IdConfigurationElementName]; }
set { this[IdConfigurationElementName] = value; }
}
/// <summary>
/// Determines name of the XML attribute that will contain ID of the System.
/// </summary>
public const string IdConfigurationElementName = "id";
/// <summary>
/// Represents the XML attribute used to store Name of the System.
/// </summary>
[ConfigurationProperty(NameConfigurationElementName, IsRequired = true)]
public string Name
{
get { return (string) this[NameConfigurationElementName]; }
set { this[NameConfigurationElementName] = value; }
}
/// <summary>
/// Determines name of the XML attribute that will contain Name of the System.
/// </summary>
public const string NameConfigurationElementName = "name";
/// <summary>
/// Represents the XML attribute used to store Description of the System
/// </summary>
[ConfigurationProperty(DescriptionConfigurationElementName, IsRequired = false, DefaultValue = "")]
public string Description
{
get { return (string) this[DescriptionConfigurationElementName]; }
set { this[DescriptionConfigurationElementName] = value; }
}
/// <summary>
/// Determines name of the XML attribute that will contain Name of the System.
/// </summary>
public const string DescriptionConfigurationElementName = "description";
/// <summary>
/// Represents the collection of the System's Equipments as they are described in the .config file.
/// </summary>
[ConfigurationProperty(EquipmentsConfigurationElementCollectionName, IsDefaultCollection = false)]
[ConfigurationCollection(typeof (EquipmentConfigurationElementCollection), AddItemName = "add",
ClearItemsName = "clear", RemoveItemName = "remove")]
public EquipmentConfigurationElementCollection Equipments
{
get { return (EquipmentConfigurationElementCollection) base[EquipmentsConfigurationElementCollectionName]; }
}
/// <summary>
/// Determines name of the XML tag that will contain collection of the System's Equipments.
/// </summary>
public const string EquipmentsConfigurationElementCollectionName = "equipments";
}
/// <summary>
/// Extends the standard .Net ConfigurationElementCollection re-definind commectio manipulation members and making them strongly-typed.
/// </summary>
/// <typeparam name="TElementType">Type of the Configuration Elements that can be included into the collection.</typeparam>
public class ConfigurationElementCollectionBase<TElementType> : ConfigurationElementCollection, IEnumerable<TElementType>
where TElementType : ConfigurationElement, new()
{
/// <summary>
/// Makes the addition operation public.
/// </summary>
/// <param name="customElement">Configuration element to add to the collection.</param>
public virtual void Add(TElementType customElement)
{
BaseAdd(customElement);
}
/// <summary>
/// Overrides the base implementation of the overloaded method masking an exception throwing.
/// </summary>
/// <param name="element">Configuration element to add.</param>
protected override void BaseAdd(ConfigurationElement element)
{
BaseAdd(element, false);
}
/// <summary>
/// Overrides the base property hardcoding the returned value.
/// </summary>
public override ConfigurationElementCollectionType CollectionType
{
get { return ConfigurationElementCollectionType.AddRemoveClearMap; }
}
/// <summary>
/// Overrides the base implementation of the instance factory method.
/// </summary>
/// <returns>A new instance of the Configuration Element type determined by the type parameter.</returns>
protected override ConfigurationElement CreateNewElement()
{
return new TElementType();
}
/// <summary>
/// Overrides the base implementation of the method determining the indexing algorithm used in the collection.
/// </summary>
/// <param name="element">The configuration element to get index of.</param>
/// <returns></returns>
protected override object GetElementKey(ConfigurationElement element)
{
return ((TElementType)element);
}
/// <summary>
/// Collection's element accessor by index property.
/// </summary>
/// <param name="index">Index of the desired element in the collection.</param>
/// <returns>The requested collection element if exists.</returns>
public TElementType this[int index]
{
get { return (TElementType)BaseGet(index); }
set
{
if (BaseGet(index) != null)
{
BaseRemoveAt(index);
}
BaseAdd(index, value);
}
}
/// <summary>
/// Overrides the collection's element accessor by key property.
/// </summary>
/// <param name="id">Key of the desired collection element.</param>
/// <returns>The requested collection element if exists</returns>
public new TElementType this[string id]
{
get { return (TElementType)BaseGet(id); }
}
/// <summary>
/// Implements a standard collection method looking for an element in the collection and returning the element's index if found.
/// </summary>
/// <param name="element">The element to look for.</param>
/// <returns>Index of the element in the collection if exists.</returns>
public int GetIndexOf(TElementType element)
{
return BaseIndexOf(element);
}
/// <summary>
/// Implements a standard collection method removing an element from the collection.
/// </summary>
/// <param name="url">The element to be removed from the collection.</param>
public void Remove(TElementType url)
{
if (BaseIndexOf(url) >= 0)
BaseRemove(url);
}
/// <summary>
/// Implements the standard collection member removing an element from the collection by the element's index.
/// </summary>
/// <param name="index">Index of the element to be removed from the collection.</param>
public void RemoveAt(int index)
{
BaseRemoveAt(index);
}
/// <summary>
/// Implements a standard collection method removing an element by the element's key.
/// </summary>
/// <param name="id">Key of the element to be removed from the collection.</param>
public void Remove(string id)
{
BaseRemove(id);
}
/// <summary>
/// Implements the standard collection method that clears the collection.
/// </summary>
public void Clear()
{
BaseClear();
}
public new IEnumerator<TElementType> GetEnumerator()
{
for (int i = 0; i < Count; i++)
{
yield return this[i];
}
}
public class EquipmentConfigurationElement : ConfigurationElement
{
/// <summary>
/// Represents the collection of the Equipment Unit's Signals as they are described in the .config file.
/// </summary>
[ConfigurationProperty(signalsConfigurationElementCollectionName, IsDefaultCollection = false)]
[ConfigurationCollection(typeof(SignalConfigurationElementCollection), AddItemName = "add", ClearItemsName = "clear", RemoveItemName = "remove")]
public SignalConfigurationElementCollection Signals
{
get
{
return (SignalConfigurationElementCollection)base[signalsConfigurationElementCollectionName];
}
}
/// <summary>
/// Determines name of the XML tag that will contain collection of the Equipment Unit's Signals.
/// </summary>
private const string signalsConfigurationElementCollectionName = "signals";
}
/// <summary>
/// Represents a type-safe collection of Equipment Unit Configuration Elements.
/// </summary>
public class EquipmentConfigurationElementCollection : ConfigurationElementCollectionBase<EquipmentConfigurationElement>
{
}
/// <summary>
/// Represensts a Signal's configuration element
/// </summary>
/// <remarks>
/// As the class is derived from ConfigurationElementBase, a Signal's configuration element will expect "is", "name", and "description" XML attributes defined in the configuration file.
/// </remarks>
public sealed class SignalConfigurationElement : ConfigurationElement
{
/// <summary>
/// Represents an XML attribute used to determine type of the Signal.
/// </summary>
/// <remarks>
/// The attribute is expected to have a string value which is equal to one of SignalType enumeration member names: "Status" or "Command".
/// </remarks>
[ConfigurationProperty(typeConfigurationElementName, IsRequired = false, DefaultValue = "status")]
public string Type
{
get { return (string) this[typeConfigurationElementName]; }
set { this[typeConfigurationElementName] = value; }
}
/// <summary>
/// Determines name of the XML attribute that will contain type of the Signal.
/// </summary>
private const string typeConfigurationElementName = "type";
}
/// <summary>
/// Represents a type-safe collection of Signal Configuration Elements.
/// </summary>
public class SignalConfigurationElementCollection : ConfigurationElementCollectionBase<SignalConfigurationElement>
{
}
This is possible through the dark arts of reflection. Given the XML shown in your question, you can do this:
var configXml = GetAppConfigXml(); // Returns the XML shown in your question.
var config = (SystemConfigurationSection)Activator.CreateInstance(typeof(SystemConfigurationSection), true);
using (var sr = new StringReader(configXml))
using (var xmlReader = XmlReader.Create(sr))
{
if (xmlReader.ReadToDescendant("SystemConfiguration"))
using (var subReader = xmlReader.ReadSubtree())
{
var dynMethod = config.GetType().GetMethod("DeserializeSection", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(XmlReader) }, null);
dynMethod.Invoke(config, new object[] { subReader });
}
}
Debug.Assert(config.Equipments.Count == 2); // No assert
Reference: source for ConfigurationSection.DeserializeSection.
Incidentally, the configuration classes attached to your question don't match the XML. Some properties are missing, and the properties that correspond to attributes in the XML must have IsKey = true. If the XML doesn't match the configuration classes, DeserializeSection will throw an exception.

ASP.NET MVC Transfer HTML Table Contents to Another View

Is this do-able?
Here's my situation and could you suggest an easier or more efficient way if what I'm trying to do isn't advisable.
We're talking about report generation page here.First, I have a stored procedure that takes a REALLY long time to finish executing if no filters/condition is set. Meaning it is a view all, this stored proc returns a list. This list then populates a table in my view. It could be just 10 up to thousands records but the execution is pretty long because it computes this and that against thousands of record, to make it short, I won't alter my stored procedure.
Now from this first view, I have a printable version button which calls another page with the same contents but print-friendly page. I dont want to execute the painful stored proc to get the same list, I want to re-use what is already generated. How can I do this?
From what I understand you are thinking of implementing some way of caching the data that you calculated through a painfully slow stored procedure?
One option would be to implement a CacheManager and cache the results for a certain period of time:
/// <summary>
/// Cache Manager Singleton
/// </summary>
public class CacheManager
{
/// <summary>
/// The instance
/// </summary>
private static MemoryCache instance = null;
/// <summary>
/// Gets the instance of memoryCache.
/// </summary>
/// <value>The instance of memoryCache.</value>
public static MemoryCache Instance
{
get
{
if (instance == null)
{
instance = new MemoryCache();
}
return instance;
}
}
}
/// <summary>
/// Cache Manager
/// </summary>
public class MemoryCache
{
/// <summary>
/// Gets the expiration date of the object
/// </summary>
/// <value>The no absolute expiration.</value>
public DateTime NoAbsoluteExpiration
{
get { return DateTime.MaxValue; }
}
/// <summary>
/// Retrieve the object in cache
/// If the object doesn't exist in cache or is obsolete, getItemCallback method is called to fill the object
/// </summary>
/// <typeparam name="T">Object Type to put or get in cache</typeparam>
/// <param name="httpContext">Http Context</param>
/// <param name="cacheId">Object identifier in cache - Must be unique</param>
/// <param name="getItemCallback">Callback method to fill the object</param>
/// <param name="slidingExpiration">Expiration date</param>
/// <returns>Object put in cache</returns>
public T Get<T>(string cacheId, Func<T> getItemCallback, TimeSpan? slidingExpiration = null) where T : class
{
T item = HttpRuntime.Cache.Get(cacheId) as T;
if (item == null)
{
item = getItemCallback();
if (slidingExpiration == null)
{
slidingExpiration = TimeSpan.FromSeconds(30);
}
HttpRuntime.Cache.Insert(cacheId, item, null, this.NoAbsoluteExpiration, slidingExpiration.Value);
}
return item;
}
/// <summary>
/// Retrieve the object in cache
/// If the object doesn't exist in cache or is obsolete, null is returned
/// </summary>
/// <typeparam name="T">Object Type to put or get in cache</typeparam>
/// <param name="httpContext">Http Context</param>
/// <param name="cacheId">Object identifier in cache - Must be unique</param>
/// <returns>Object put in cache</returns>
public T Get<T>(string cacheId) where T : class
{
T item = HttpRuntime.Cache.Get(cacheId) as T;
if (item == null)
{
return null;
}
return item;
}
/// <summary>
/// Delete an object using his unique id
/// </summary>
/// <param name="httpContext">Http Context</param>
/// <param name="cacheId">Object identifier in cache</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
public bool Clear(string cacheId)
{
var item = HttpRuntime.Cache.Get(cacheId);
if (item != null)
{
HttpRuntime.Cache.Remove(cacheId);
return true;
}
return false;
}
/// <summary>
/// Delete all object in cache
/// </summary>
/// <param name="httpContext">Http Context</param>
public void ClearAll(string filter = null)
{
var item = HttpRuntime.Cache.GetEnumerator();
while (item.MoveNext())
{
DictionaryEntry entry = (DictionaryEntry)item.Current;
var key = entry.Key.ToString();
if (filter != null && (key.ToLower().Contains(filter.ToLower()) || filter == "*" )) //if filter, only delete if the key contains the filter value
{
HttpRuntime.Cache.Remove(entry.Key.ToString());
}
else if (filter == null) //no filter, delete everything
{
HttpRuntime.Cache.Remove(entry.Key.ToString());
}
}
}
}
Note: I didn't write this myself but can't find the original source.
This is how you use it:
// Retrieve object from cache if it exist, callback is performed to set and return object otherwise
UserObject userObject = CacheManager.Instance.Get(httpContext, "singleId", ()=> {
return new UserObject() {
Id = Guid.NewGuid()
};
});
// Retrieve object from cache if it exist, return null otherwise
UserObject userObjectRetrieved = CacheManager.Instance.Retrieve<UserObject>(httpContext, "singleId");
// Remove object from cache
CacheManager.Instance.Clear(httpContext, "singleId");
// Remove all object from cache
CacheManager.Instance.ClearAll(httpContext);
Not sure how your View design is meant to be but you could populate the printable version at the same time and hide it until the button is clicked.

Nullable Types as Generics

I'm trying to create a linked list for my personal library that can handle EVERYTHING. I'm trying to write it so that it can 'equally' easily hand int, null,DateTime or Class and I wanted it to be easily extendable, so that if I wanted to quickly make stack out of it, I can just write push, pop, and peek methods and so forth.
Currently, my code looks like this. Note that I use 'Base' as my generic type.
namespace ClassLibrary1
{
public class LinkedList<Base> where Base : class
{
public class Node
{
private Node next;
private Node prev;
private Base value;
/// <summary>
/// Constructor for Nodes of Circular Linked List class.
/// Calls overloaded constructor for no previous or next provided.
/// O(1)
/// </summary>
/// <param name="value">The value to be stored. Can use tuple for associations</param>
public Node(Base value)
{
new Node(null, null, value);
}
/// <summary>
/// Constructor for nodes of Circular Linked List class.
/// O(1)
/// </summary>
/// <param name="prev">The previous node in the linked list</param>
/// <param name="next">The next node in the linked list</param>
/// <param name="value">The value to be stored</param>
public Node(Node prev, Node next, Base value)
{
this.prev = prev;
this.next = next;
this.value = value;
}
/// <summary>
/// Sets the 'next' attribute of the node to the passed value.
/// O(1)
/// Chainable
/// </summary>
/// <param name="next">The new value of the 'next' attribute.</param>
/// <returns>Chainable(Node, this)</returns>
public Node setNext(Node next)
{
this.next = next;
return this;
}
/// <summary>
/// Sets the 'prev' attribute of the node to the passed value
/// O(1)
/// Chainable
/// </summary>
/// <param name="prev">The new value of the 'prev' attribute to denote the previous node</param>
/// <returns>Chainable(Node, this)</returns>
public Node setPrev(Node prev)
{
this.prev = prev;
return this;
}
/// <summary>
/// Changes the stored value of type Base to the passed value.
/// O(1)
/// Chainable
/// </summary>
/// <param name="value">The new value to be stored with the node</param>
/// <returns>Chainable(Node, this)</returns>
public Node setVal(Base value)
{
this.value = value;
return this;
}
/// <summary>
/// Returns the next node in the linked list.
/// O(1)
/// </summary>
/// <returns>The next node in the linked list.(Node)</returns>
public Node getNext()
{
return this.next;
}
/// <summary>
/// Returns the previous node in the linked list.
/// O(1)
/// </summary>
/// <returns>The previous node in the linked list.(Node)</returns>
public Node getPrev()
{
return this.prev;
}
/// <summary>
/// Returns the value stored at this node.
/// O(1)
/// </summary>
/// <returns>The value stored at this node.(Base)</returns>
public Base getVal()
{
return this.value;
}
}
public Node head;
public bool duplicates;
public bool hasNullValues;
public bool throwNullError;
/// <summary>
/// Constructor for the LinkedList. Creates a null head node.
/// Duplication defaulted to false
/// O(1)
/// </summary>
public LinkedList()
{
this.head = new Node(null);
this.head.setNext(this.head).setPrev(this.head);
this.duplicates = false;
this.hasNullValues = false;
this.throwNullError = false;
}
/// <summary>
/// Allows duplication for the linked list.
/// O(1)
/// Chainable attribute.
/// </summary>
/// <returns>Chainable.(LinkedList<Base>, this)</returns>
public LinkedList<Base> hasDuplicates()
{
this.duplicates = true;
return this;
}
/// <summary>
/// Allows the structure to store null values in nodes.
/// O(1)
/// Chainable.
/// </summary>
/// <returns>Chainable.(LinkedList<Base>, this)</returns>
public LinkedList<Base> hasNulls()
{
this.hasNullValues = true;
return this;
}
/// <summary>
/// Causes the structure to throw a null error when a null value is inserted.
/// If hasNulls is off, turns it on.
/// O(1)
/// Chainable.
/// </summary>
/// <returns>Chainable.(LinkedList<Base>, this)</returns>
public LinkedList<Base> throwsNulls()
{
if (!this.hasNullValues)
{
this.hasNullValues = true;
}
this.throwNullError = true;
return this;
}
/// <summary>
/// Iff duplicates not allowed, searches for value in list. Throws error if duplicate found.
/// Creates a new node at the end of the list, then links it to the head node.
/// O(length) [if hasDuplicates()]
/// O(1) [if else]
/// Chainable
/// </summary>
/// <param name="value">Value stored at the new node in the list</param>
/// <returns>Chainable.(LinkedList<Base>, this)</returns>
public LinkedList<Base> add(Base value)
{
if (!duplicates)
{
if (search(value) != null)
{
throw new Exception("Value already exists in the linked list.");
}
}
if (!this.hasNullValues && value != null)
{
if (this.throwNullError)
{
throw new Exception("Cannot insert null values");
}
else
{
return this;
}
}
Node newNode = new Node(value);
this.head.getPrev().setNext(newNode);
this.head.setPrev(newNode);
return this;
}
/// <summary>
/// Iterates through the list until first such node for with a matching value is found.
/// Returns null if no matches found.
/// Use searchAll to find duplicates.
/// O(length)
/// </summary>
/// <param name="value">The value to be searched for.</param>
/// <returns>First node with the desired value(Node?)</returns>
public Node search(Base value)
{
Node temp = this.head.getNext();
while (!temp.getVal().Equals(value))
{
if (temp.Equals(this.head))
{
return null;
}
temp = temp.getNext();
}
return temp;
}
/// <summary>
/// If value doesn't exist in the list, throws an exception.
/// Deletes the first node found with the chosen value.
/// Use DeleteAll to delete all instances.
/// Chainable.
/// O(length)
/// </summary>
/// <param name="value">Value to be removed from the list.</param>
/// <returns>Chainable.(LinkedList<Base>, this)</returns>
public LinkedList<Base> delete(Base value)
{
try{
return delete(search(value));
}
catch(Exception e){
throw new Exception("Node to be deleted not found");
}
}
/// <summary>
/// Removes all pointers to the passed node.
/// O(1)
/// </summary>
/// <param name="tbd">The node to be deleted.</param>
/// <returns>Chainable.(LinkedList<Base>, this)</returns>
public LinkedList<Base> delete(Node tbd)
{
if (tbd.Equals(this.head))
{
throw new Exception("Cannot delete head node");
}
else
{
tbd.getPrev().setNext(tbd.getNext());
tbd.getNext().setPrev(tbd.getPrev());
}
return this;
}
/// <summary>
/// Returns a LinkedList of all nodes containing the desired value.
/// O(length)
/// </summary>
/// <param name="value">The value to be found.</param>
/// <returns>A LinkedList of Nodes with matching values.(LinkedList<Node>)</returns>
public LinkedList<Node> searchAll(Base value)
{
LinkedList<Node> returnList = new LinkedList<Node>();
Node temp = this.head.getNext();
while (!temp.Equals(this.head))
{
if (temp.getVal().Equals(value))
{
returnList.add(temp);
}
temp = temp.getNext();
}
return returnList;
}
/// <summary>
/// Returns the first Node in the Linked List.
/// O()
/// </summary>
/// <returns>First non-head node in the list.(Node)</returns>
public Node firstOrDefault()
{
return this.head.getNext();
}
/// <summary>
/// Returns the value of the first node in the list.
/// O(1)
/// </summary>
/// <returns>FIrst non-head </returns>
public Base firstVal()
{
return this.head.getNext().getVal();
}
/// <summary>
/// Gets the last node in the linked list.
/// O(1)
/// </summary>
/// <returns>The last node in the linked list.(Node)</returns>
public Node tail()
{
return this.head.getPrev();
}
/// <summary>
/// Returns the value of the last node in the linked list.
/// O(1)
/// </summary>
/// <returns>VThe value of the tail node.(Base)</returns>
public Base tailVal()
{
return this.head.getPrev().getVal();
}
public static void Main()
{
LinkedLis t<Int32> mine = new LinkedList<Int32>();
}
}
}
However, it gives Red Text under the Int32, saying "The type 'int' must be a reference type in order to use it as a parameter 'Base' in the generic type or method ---this---.
Tell me if you would like me to remove the comments, I'm not sure if that makes it harder or easier to solve.
Because you declared a constraint on a Base type to be a class (a reference type):
public class LinkedList<Base> where Base : class
It exactly forbids using Int32, because it's a value type and is different from a required reference type.
new LinkedList<Int32>()
So, to fix this particular problem, you would need to to create a wrapper class for your integer values.
Before you do this though, check your intentions to store any type in your linked list. Doing so you will strip you off all advantages of C# as a strongly typed language.
And as it was mentioned before, unless you write this code as a pure academic exercise, you should use an existing .NET LinkedList and possibly extend/inherit it, if you need more functionality.
Update: I assumed it went without saying, but to make it crystal clear don't forget that Nullable is a struct, not a class, so you cannot use "cheats" like int?.

Thread-Safe collection with no order and no duplicates

I need a thread-safe collection to hold items without duplicates. ConcurrentBag<T> allows non-unique items and HashSet<T> is not thread-safe. Is there a collection like this in .NET Framework 4.5?
I suggest you use a ConcurrentDictionary and just use a dummy value for each entry. It's annoying in terms of efficiency (having all those dummy values) but I suspect in the majority of applications that would be insignificant.
You may well want to wrap this in your own ConcurrentSet implementation, which just does enough for your purposes, so that you don't need to see the abstraction leakiness in much of your code.
Here's some code I just wrote that does this ConcurrentSet construct:
public class ConcurrentSet<T> : IEnumerable<T>, ISet<T>, ICollection<T>
{
private readonly ConcurrentDictionary<T, byte> _dictionary = new ConcurrentDictionary<T, byte>();
/// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <returns>
/// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
/// </returns>
public IEnumerator<T> GetEnumerator()
{
return _dictionary.Keys.GetEnumerator();
}
/// <summary>
/// Returns an enumerator that iterates through a collection.
/// </summary>
/// <returns>
/// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
/// </returns>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
/// </summary>
/// <returns>
/// true if <paramref name="item"/> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false. This method also returns false if <paramref name="item"/> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1"/>.
/// </returns>
/// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</exception>
public bool Remove(T item)
{
return TryRemove(item);
}
/// <summary>
/// Gets the number of elements in the set.
/// </summary>
public int Count
{
get { return _dictionary.Count; }
}
/// <summary>
/// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.
/// </summary>
/// <returns>
/// true if the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only; otherwise, false.
/// </returns>
public bool IsReadOnly { get { return false; } }
/// <summary>
/// Gets a value that indicates if the set is empty.
/// </summary>
public bool IsEmpty
{
get { return _dictionary.IsEmpty; }
}
public ICollection<T> Values
{
get { return _dictionary.Keys; }
}
/// <summary>
/// Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1"/>.
/// </summary>
/// <param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</exception>
void ICollection<T>.Add(T item)
{
if(!Add(item))
throw new ArgumentException("Item already exists in set.");
}
/// <summary>
/// Modifies the current set so that it contains all elements that are present in both the current set and in the specified collection.
/// </summary>
/// <param name="other">The collection to compare to the current set.</param><exception cref="T:System.ArgumentNullException"><paramref name="other"/> is null.</exception>
public void UnionWith(IEnumerable<T> other)
{
foreach (var item in other)
TryAdd(item);
}
/// <summary>
/// Modifies the current set so that it contains only elements that are also in a specified collection.
/// </summary>
/// <param name="other">The collection to compare to the current set.</param><exception cref="T:System.ArgumentNullException"><paramref name="other"/> is null.</exception>
public void IntersectWith(IEnumerable<T> other)
{
var enumerable = other as IList<T> ?? other.ToArray();
foreach (var item in this)
{
if (!enumerable.Contains(item))
TryRemove(item);
}
}
/// <summary>
/// Removes all elements in the specified collection from the current set.
/// </summary>
/// <param name="other">The collection of items to remove from the set.</param><exception cref="T:System.ArgumentNullException"><paramref name="other"/> is null.</exception>
public void ExceptWith(IEnumerable<T> other)
{
foreach (var item in other)
TryRemove(item);
}
/// <summary>
/// Modifies the current set so that it contains only elements that are present either in the current set or in the specified collection, but not both.
/// </summary>
/// <param name="other">The collection to compare to the current set.</param><exception cref="T:System.ArgumentNullException"><paramref name="other"/> is null.</exception>
public void SymmetricExceptWith(IEnumerable<T> other)
{
throw new NotImplementedException();
}
/// <summary>
/// Determines whether a set is a subset of a specified collection.
/// </summary>
/// <returns>
/// true if the current set is a subset of <paramref name="other"/>; otherwise, false.
/// </returns>
/// <param name="other">The collection to compare to the current set.</param><exception cref="T:System.ArgumentNullException"><paramref name="other"/> is null.</exception>
public bool IsSubsetOf(IEnumerable<T> other)
{
var enumerable = other as IList<T> ?? other.ToArray();
return this.AsParallel().All(enumerable.Contains);
}
/// <summary>
/// Determines whether the current set is a superset of a specified collection.
/// </summary>
/// <returns>
/// true if the current set is a superset of <paramref name="other"/>; otherwise, false.
/// </returns>
/// <param name="other">The collection to compare to the current set.</param><exception cref="T:System.ArgumentNullException"><paramref name="other"/> is null.</exception>
public bool IsSupersetOf(IEnumerable<T> other)
{
return other.AsParallel().All(Contains);
}
/// <summary>
/// Determines whether the current set is a correct superset of a specified collection.
/// </summary>
/// <returns>
/// true if the <see cref="T:System.Collections.Generic.ISet`1"/> object is a correct superset of <paramref name="other"/>; otherwise, false.
/// </returns>
/// <param name="other">The collection to compare to the current set. </param><exception cref="T:System.ArgumentNullException"><paramref name="other"/> is null.</exception>
public bool IsProperSupersetOf(IEnumerable<T> other)
{
var enumerable = other as IList<T> ?? other.ToArray();
return this.Count != enumerable.Count && IsSupersetOf(enumerable);
}
/// <summary>
/// Determines whether the current set is a property (strict) subset of a specified collection.
/// </summary>
/// <returns>
/// true if the current set is a correct subset of <paramref name="other"/>; otherwise, false.
/// </returns>
/// <param name="other">The collection to compare to the current set.</param><exception cref="T:System.ArgumentNullException"><paramref name="other"/> is null.</exception>
public bool IsProperSubsetOf(IEnumerable<T> other)
{
var enumerable = other as IList<T> ?? other.ToArray();
return Count != enumerable.Count && IsSubsetOf(enumerable);
}
/// <summary>
/// Determines whether the current set overlaps with the specified collection.
/// </summary>
/// <returns>
/// true if the current set and <paramref name="other"/> share at least one common element; otherwise, false.
/// </returns>
/// <param name="other">The collection to compare to the current set.</param><exception cref="T:System.ArgumentNullException"><paramref name="other"/> is null.</exception>
public bool Overlaps(IEnumerable<T> other)
{
return other.AsParallel().Any(Contains);
}
/// <summary>
/// Determines whether the current set and the specified collection contain the same elements.
/// </summary>
/// <returns>
/// true if the current set is equal to <paramref name="other"/>; otherwise, false.
/// </returns>
/// <param name="other">The collection to compare to the current set.</param><exception cref="T:System.ArgumentNullException"><paramref name="other"/> is null.</exception>
public bool SetEquals(IEnumerable<T> other)
{
var enumerable = other as IList<T> ?? other.ToArray();
return Count == enumerable.Count && enumerable.AsParallel().All(Contains);
}
/// <summary>
/// Adds an element to the current set and returns a value to indicate if the element was successfully added.
/// </summary>
/// <returns>
/// true if the element is added to the set; false if the element is already in the set.
/// </returns>
/// <param name="item">The element to add to the set.</param>
public bool Add(T item)
{
return TryAdd(item);
}
public void Clear()
{
_dictionary.Clear();
}
public bool Contains(T item)
{
return _dictionary.ContainsKey(item);
}
/// <summary>
/// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection`1"/> to an <see cref="T:System.Array"/>, starting at a particular <see cref="T:System.Array"/> index.
/// </summary>
/// <param name="array">The one-dimensional <see cref="T:System.Array"/> that is the destination of the elements copied from <see cref="T:System.Collections.Generic.ICollection`1"/>. The <see cref="T:System.Array"/> must have zero-based indexing.</param><param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.</param><exception cref="T:System.ArgumentNullException"><paramref name="array"/> is null.</exception><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="arrayIndex"/> is less than 0.</exception><exception cref="T:System.ArgumentException"><paramref name="array"/> is multidimensional.-or-The number of elements in the source <see cref="T:System.Collections.Generic.ICollection`1"/> is greater than the available space from <paramref name="arrayIndex"/> to the end of the destination <paramref name="array"/>.-or-Type <paramref name="T"/> cannot be cast automatically to the type of the destination <paramref name="array"/>.</exception>
public void CopyTo(T[] array, int arrayIndex)
{
Values.CopyTo(array, arrayIndex);
}
public T[] ToArray()
{
return _dictionary.Keys.ToArray();
}
public bool TryAdd(T item)
{
return _dictionary.TryAdd(item, default(byte));
}
public bool TryRemove(T item)
{
byte donotcare;
return _dictionary.TryRemove(item, out donotcare);
}
}

Categories

Resources