C# Polymorphism with Dictionaries not working as intended - c#

I've have a a abstract Base class
public abstract class absTerminalStrategy
{
//....
}
and two child class lets call them Class A and Class B
Class A
{
//Some Params
public A (/*Some Params*/)
}
Class B
{
//Some Params
Public B (/*Some Params*/)
}
in "upper" layer class I'll call Class Control
I've those objects
{
//..
public static absTerminalStrategy terminal = null;
/// <summary>
/// stores all instencese of A_tarategy to be used --> string A_IP
/// </summary>
public static Dictionary<string, A> terminal_A_Dictionary = new Dictionary<string, A_Strategy>();
/// <summary>
/// stores all instencese of B_Scritping to be used --> string B_Port
/// </summary>
public static Dictionary<string, B> terminal_B_Dictionary = new Dictionary<string, B_trategy>();
//..
}
Now in run time I add some instances to the dictionaries and later I need to "jump" between
various instances using this method
public static bool Terminalset(string terminalName)
{
string InterfaceType = terminalName.Split(':')[0];//cutting the root name for switch
switch(InterfaceType)
{
case "A":
{
A New_A =null;
if (terminal_A_Dictionary.TryGetValue(terminalName, out New_A))//return bool
terminal = New_A;
else return false;
}
break;
case "B":
{
B New_B =null;
if (terminal_B_Dictionary.TryGetValue(terminalName, out New_B))//return bool
terminal = New_B;
else return false;
}
break;
}
return true;
}
My problem is , when I change between class A to class B everything works fine
when I change between instances of class A using the dictionary it seems to work fine
But when I do the same with Class B I doesn't work and stays on it last property to entered to he dictionary (the last new instance to entered)
what can be the problem ?
Also this is how add to the dictionary
public static bool TermialCheckCreate (TerminalType terminalType , GlobalParam.A a = null , GlobalParam.B b= null)
{
switch (terminalType)
{
case TerminalType.A:
{
if (terminal_A_Dictionary.ContainsKey(string.Format("A:{0}", _A_Param.AIp))) break;
if (a == null) return false;
A_Strategy terminal = new A_Strategy(_A_Param.AIp, GlobalParam.A_PORT, 60);
terminalTelnetDictionary.Add(string.Format("A:{0}",_A_Param.AIp), terminal);
}
break;
case TerminalType.B:
{
if (terminal_B_Dictionary.ContainsKey(string.Format("B:{0}", _B_Param.Bcom))) break;
if (b == null) return false;
B_strategy terminal = new B_Strategy(GlobalParam.AppDirectory, _B_Param.BCom, _B_Param.BRate);
terminal_B_Dictionary.Add(string.Format("B:{0}",_B_Param.BCom), terminal);
}
break;
}
return true;
}
EDIT small corrections
copy of the classes involved
class to control all the dictionaries
public enum TerminalType {serial , Telent };
public static class TerminalControl
{
/// <summary>
/// stores all instencese of TelnetStarategy to be used --> string telnetIP
/// </summary>
public static Dictionary<string, TelnetStrategy> terminalTelnetDictionary = new Dictionary<string, TelnetStrategy>();
/// <summary>
/// stores all instencese of SerialScritping to be used --> string SerailPort
/// </summary>
public static Dictionary<string, SerialStrategy> terminalSerialDictionary = new Dictionary<string, SerialStrategy>();
/// <summary>
/// abstract instance , chooses between serial and telent
/// </summary>
public static absTerminalStrategy terminal = null;
/// <summary>
/// static constructor
/// </summary>
static TerminalControl()
{
}
/// <summary>
/// Inherits from serial/telnet strategy will run this class of commands
/// </summary>
/// <param name="terminalType"></param>
/// <param name="TelnetIP"></param>
/// <param name="SerialPort"></param>
/// <param name="SerialBaudRate"></param>
/// <param name="Command"></param>
/// <returns></returns>
public static List<string> TerminalSendAndWaitForList(string Command,string terminalName,string fullpathLog="")
{
if (!Terminalset(terminalName)) return new List<string>(new string[] { "ERROR : Device Not Found !!! Check it and add" });
return terminal.SendAndWaitForList(Command);
}
public static bool Terminalset(string terminalName)
{
string InterfaceType = terminalName.Split(':')[0];
switch(InterfaceType)
{
case "Telnet":
{
TelnetStrategy NewTelnet =null;
terminal = NewTelnet;
if (terminalTelnetDictionary.TryGetValue(terminalName, out NewTelnet))//return bool
terminal = NewTelnet;
else return false;
}
break;
case "Serial":
{
SerialStrategy NewSerial =null;
terminal = NewSerial;
if (terminalSerialDictionary.TryGetValue(terminalName, out NewSerial))//return bool
terminal = NewSerial;
else return false;
}
break;
}
return true;
}
/// <summary>
/// added new terminal to inner dictionary
/// </summary>
/// <param name="terminalType"></param>
/// <param name="telentParam"></param>
/// <param name="serialParam"></param>
/// <returns></returns>
public static bool TermialCheckCreate (TerminalType terminalType , GlobalParam.TelentParams telentParam = null , GlobalParam.SerialParams serialParam= null)
{
switch (terminalType)
{
case TerminalType.Telent:
{
if (terminalTelnetDictionary.ContainsKey(string.Format("Telnet:{0}", telentParam.telnetIp))) break;
if (telentParam == null) return false;
TelnetStrategy terminal = new TelnetStrategy(telentParam.telnetIp, GlobalParam.TELNET_PORT, 60);
terminalTelnetDictionary.Add(string.Format("Telnet:{0}",telentParam.telnetIp), terminal);
}
break;
case TerminalType.serial:
{
if (terminalSerialDictionary.ContainsKey(string.Format("Serial:{0}", serialParam.SerialCom))) break;
if (serialParam == null) return false;
SerialStrategy terminal = new SerialStrategy(GlobalParam.AppDirectory, serialParam.SerialCom, serialParam.SerialBaudRate);
terminalSerialDictionary.Add(string.Format("Serial:{0}",serialParam.SerialCom), terminal);
}
break;
}
return true;
}
}
abstract base and child showing only there names and constructors (if needed i"ll publish the entire code....)
public abstract class absTerminalStrategy
{
public abstract List<string> SendAndWaitForList(string Command);
public abstract bool WaitForOutPut(string Blocker, int secTimeOut);//implement Wait for Output string before releasing lock (Monitor.wait/Pulse)
}
public class SerialStrategy : absTerminalStrategy
{
public ScriptingSerial serailAgent = null;//Infrastructure
public SerialStrategy(string fullPathLog , string PortName , int Baudrate)
{
serailAgent = new ScriptingSerial(fullPathLog, PortName, Baudrate);
}
//....
}
public class TelnetStrategy : absTerminalStrategy
{
public static event SerialDataInput_EventHandler onDataInput;
public static ScriptingTelnet telnetAgent = null;//Infrastructure
public string TelnetIp = string.Empty;
public TelnetStrategy(string Ip, int Port, int CommandTimeOut)
{
TelnetIp = Ip;
int port = Port;
telnetAgent = new ScriptingTelnet(Ip, port, CommandTimeOut);
}
}

Related

Convert string to IMemoryOwner / Copy string into a rented buffer

I have a custom memory sequence OwnedMemorySequence. I want to put a string inside. The issue is that I cannot get it to compile. Usually something like Encoding.UTF8.GetBytes and then copy that buffer into the rented buffer should work but in my case there are compile time errors.
var message = "Hello";
var buffer = MemoryPool<byte>.Shared.Rent(Encoding.UTF8.GetByteCount(message));
Encoding.UTF8.Convert(message, buffer.Memory, true, out _, out _, out _); // no such overload
var seq = new OwnedMemorySequence<byte>();
seq.Append(buffer);
Code
public sealed class OwnedMemorySequence<T> : IDisposable
{
private readonly CollectionDisposable _disposable = new();
private readonly MemorySequence<T> _sequence = new();
public ReadOnlySequence<T> ReadOnlySequence => _sequence.ReadOnlySequence;
public void Dispose()
{
_disposable.Dispose();
}
public OwnedMemorySequence<T> Append(IMemoryOwner<T> memoryOwner)
{
_disposable.Add(memoryOwner);
_sequence.Append(memoryOwner.Memory);
return this;
}
public ReadOnlySequence<T> CreateReadOnlySequence(int firstBufferStartIndex, int lastBufferEndIndex)
{
return _sequence.CreateReadOnlySequence(firstBufferStartIndex, lastBufferEndIndex);
}
}
public sealed class MemorySequence<T>
{
private MemorySegment? _head;
private MemorySegment? _tail;
public ReadOnlySequence<T> ReadOnlySequence => CreateReadOnlySequence(0, _tail?.Memory.Length ?? 0);
public MemorySequence<T> Append(ReadOnlyMemory<T> buffer)
{
if (_tail == null)
_head = _tail = new MemorySegment(buffer, 0);
else
_tail = _tail.Append(buffer);
return this;
}
public ReadOnlySequence<T> CreateReadOnlySequence(int firstBufferStartIndex, int lastBufferEndIndex)
{
return _tail == null ? new ReadOnlySequence<T>(Array.Empty<T>()) : new ReadOnlySequence<T>(_head!, firstBufferStartIndex, _tail, lastBufferEndIndex);
}
private sealed class MemorySegment : ReadOnlySequenceSegment<T>
{
public MemorySegment(ReadOnlyMemory<T> memory, long runningIndex)
{
Memory = memory;
RunningIndex = runningIndex;
}
public MemorySegment Append(ReadOnlyMemory<T> nextMemory)
{
var next = new MemorySegment(nextMemory, RunningIndex + Memory.Length);
Next = next;
return next;
}
}
}
public static class MemoryOwnerExtensions
{
/// <summary>
/// Rent a buffer from a pool with an exact length.
/// </summary>
/// <param name="pool">The <see cref="MemoryPool{T}" /> instance.</param>
/// <param name="exactBufferSize">The exact size of the buffer.</param>
public static IMemoryOwner<T> RentExact<T>(this MemoryPool<T> pool, int exactBufferSize)
{
if (pool == null) throw new ArgumentNullException(nameof(pool));
var rented = pool.Rent(exactBufferSize);
if (exactBufferSize == rented.Memory.Length)
return rented;
return new SliceOwner<T>(rented, 0, exactBufferSize);
}
/// <summary>
/// Wrap an existing <see cref="IMemoryOwner{T}" /> instance in a lightweight manner, but allow
/// the <see cref="IMemoryOwner{T}.Memory" /> member to have a different length.
/// </summary>
/// <param name="owner">The original instance.</param>
/// <param name="start">The starting offset of the slice.</param>
/// <param name="length">The length of the slice.</param>
public static IMemoryOwner<T> Slice<T>(this IMemoryOwner<T> owner, int start, int length)
{
if (owner == null) throw new ArgumentNullException(nameof(owner));
if (start == 0 && length == owner.Memory.Length)
return owner;
if ((uint)start >= (uint)owner.Memory.Length) throw new ArgumentOutOfRangeException(nameof(start));
if ((uint)length > (uint)(owner.Memory.Length - start)) throw new ArgumentOutOfRangeException(nameof(length));
return new SliceOwner<T>(owner, start, length);
}
/// <summary>
/// Wrap an existing <see cref="IMemoryOwner{T}" /> instance in a lightweight manner, but allow
/// the <see cref="IMemoryOwner{T}.Memory" /> member to have a different length.
/// </summary>
/// <param name="owner">The original instance.</param>
/// <param name="start">The starting offset of the slice.</param>
public static IMemoryOwner<T> Slice<T>(this IMemoryOwner<T> owner, int start)
{
if (owner == null) throw new ArgumentNullException(nameof(owner));
if (start == 0)
return owner;
if ((uint)start >= (uint)owner.Memory.Length) throw new ArgumentOutOfRangeException(nameof(start));
return new SliceOwner<T>(owner, start);
}
private sealed class SliceOwner<T> : IMemoryOwner<T>
{
private IMemoryOwner<T> _owner;
public SliceOwner(IMemoryOwner<T> owner, int start, int length)
{
_owner = owner;
Memory = _owner.Memory.Slice(start, length);
}
public SliceOwner(IMemoryOwner<T> owner, int start)
{
_owner = owner;
Memory = _owner.Memory.Slice(start);
}
public Memory<T> Memory { get; private set; }
public void Dispose()
{
if (_owner != null)
{
_owner.Dispose();
_owner = null;
}
Memory = default;
}
}
}
Indeed, no such overload exists. Where did you look in the documentation to set that argument?
Encoding.Convert Method
Encoder have a similar method.
Encoder.Convert Method
EncodingExtensions.Convert Method
using System;
using System.Buffers;
using System.Text;
public class Program
{
public static void Main()
{
var message = "Hello";
var buffer = MemoryPool<byte>.Shared.Rent(Encoding.UTF8.GetByteCount(message));
Encoding.UTF8.GetEncoder().Convert(message, buffer.Memory.Span, true, out _, out _, out _);
Console.WriteLine(Encoding.UTF8.GetString(buffer.Memory.Span));
}
}

How to add extension points in my library?

I've started writing DataFilters a while ago after I discovered and used elastic search with it's wonderful lucene syntax.
The idea behind that project was first to learn new stuff but I was also wondering if I could create something similar to work with other datasources.
Long story short, I now have something that work pretty well (I think) with the BCL classes and I now want to extend it to support third party libraries like NodaTime.
The main parts are IFilter interface
using System;
namespace DataFilters
{
/// <summary>
/// Defines the basic shape of a filter
/// </summary>
public interface IFilter : IEquatable<IFilter>
{
/// <summary>
/// Gets the JSON representation of the filter
/// </summary>
/// <returns></returns>
string ToJson();
/// <summary>
/// Computes a new <see cref="IFilter"/> instance which is the exact opposite of the current instance.
/// </summary>
/// <returns>The exact opposite of the current instance.</returns>
IFilter Negate();
#if NETSTANDARD2_1
public virtual void ToString() => ToJson();
#endif
}
}
with two implementations :
Filter
using DataFilters.Converters;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Schema;
using static Newtonsoft.Json.DefaultValueHandling;
using static Newtonsoft.Json.Required;
using System.Text.RegularExpressions;
#if !NETSTANDARD1_3
using System.Text.Json.Serialization;
#endif
namespace DataFilters
{
/// <summary>
/// An instance of this class holds a filter
/// </summary>
#if NETSTANDARD1_3
[JsonObject]
[JsonConverter(typeof(FilterConverter))]
#else
[System.Text.Json.Serialization.JsonConverter(typeof(FilterConverter))]
#endif
public class Filter : IFilter, IEquatable<Filter>
{
/// <summary>
/// Filter that always returns <c>true</c>
/// </summary>
public static Filter True => new Filter(default, default);
/// <summary>
/// Pattern that field name should respect.
/// </summary>
/// <returns></returns>
public const string ValidFieldNamePattern = #"[a-zA-Z_]+((\[""[a-zA-Z0-9_]+""]|(\.[a-zA-Z0-9_]+))*)";
/// <summary>
/// Regular expression used to validate
/// </summary>
/// <returns></returns>
public static readonly Regex ValidFieldNameRegex = new Regex(ValidFieldNamePattern, RegexOptions.IgnoreCase, TimeSpan.FromSeconds(1));
/// <summary>
/// Name of the json property that holds the field name
/// </summary>
public const string FieldJsonPropertyName = "field";
/// <summary>
/// Name of the json property that holds the operator
/// </summary>
public const string OperatorJsonPropertyName = "op";
/// <summary>
/// Name of the json property that holds the value
/// </summary>
public const string ValueJsonPropertyName = "value";
/// <summary>
/// <see cref="FilterOperator"/>s that required <see cref="Value"/> to be null.
/// </summary>
public static IEnumerable<FilterOperator> UnaryOperators { get; } = new[]{
FilterOperator.IsEmpty,
FilterOperator.IsNotEmpty,
FilterOperator.IsNotNull,
FilterOperator.IsNull
};
/// <summary>
/// Generates the <see cref="JSchema"/> for the specified <see cref="FilterOperator"/>.
/// </summary>
/// <param name="op"></param>
/// <returns></returns>
public static JSchema Schema(FilterOperator op)
{
JSchema schema;
switch (op)
{
case FilterOperator.Contains:
case FilterOperator.StartsWith:
case FilterOperator.EndsWith:
schema = new JSchema
{
Type = JSchemaType.Object,
Properties =
{
[FieldJsonPropertyName] = new JSchema { Type = JSchemaType.String },
[OperatorJsonPropertyName] = new JSchema { Type = JSchemaType.String },
[ValueJsonPropertyName] = new JSchema { Type = JSchemaType.String }
},
Required = { FieldJsonPropertyName, OperatorJsonPropertyName }
};
break;
case FilterOperator.IsEmpty:
case FilterOperator.IsNotEmpty:
case FilterOperator.IsNotNull:
case FilterOperator.IsNull:
schema = new JSchema
{
Type = JSchemaType.Object,
Properties =
{
[FieldJsonPropertyName] = new JSchema { Type = JSchemaType.String },
[OperatorJsonPropertyName] = new JSchema { Type = JSchemaType.String }
},
Required = { FieldJsonPropertyName, OperatorJsonPropertyName }
};
break;
default:
schema = new JSchema
{
Type = JSchemaType.Object,
Properties =
{
[FieldJsonPropertyName] = new JSchema { Type = JSchemaType.String, },
[OperatorJsonPropertyName] = new JSchema { Type = JSchemaType.String },
[ValueJsonPropertyName] = new JSchema {
Not = new JSchema() { Type = JSchemaType.Null }
}
},
Required = { FieldJsonPropertyName, OperatorJsonPropertyName, ValueJsonPropertyName }
};
break;
}
schema.AllowAdditionalProperties = false;
return schema;
}
/// <summary>
/// Name of the field the filter will be applied to
/// </summary>
#if NETSTANDARD1_3
[JsonProperty(FieldJsonPropertyName, Required = Always)]
#else
[JsonPropertyName(FieldJsonPropertyName)]
#endif
public string Field { get; }
/// <summary>
/// Operator to apply to the filter
/// </summary>
#if NETSTANDARD1_3
[JsonProperty(OperatorJsonPropertyName, Required = Always)]
[JsonConverter(typeof(CamelCaseEnumTypeConverter))]
#else
[JsonPropertyName(OperatorJsonPropertyName)]
//[System.Text.Json.Serialization.JsonConverter(typeof(FilterOperatorConverter))]
#endif
public FilterOperator Operator { get; }
/// <summary>
/// Value of the filter
/// </summary>
#if NETSTANDARD1_3
[JsonProperty(ValueJsonPropertyName,
Required = AllowNull,
DefaultValueHandling = IgnoreAndPopulate,
NullValueHandling = NullValueHandling.Ignore)]
#else
[JsonPropertyName(ValueJsonPropertyName)]
#endif
public object Value { get; }
/// <summary>
/// Builds a new <see cref="Filter"/> instance.
/// </summary>
/// <param name="field">name of the field</param>
/// <param name="operator"><see cref="Filter"/> to apply</param>
/// <param name="value">value of the filter</param>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="field"/> does not conform with <see cref="ValidFieldNamePattern"/></exception>
public Filter(string field, FilterOperator #operator, object value = null)
{
if (!string.IsNullOrEmpty(field) && !ValidFieldNameRegex.IsMatch(field))
{
throw new ArgumentOutOfRangeException(nameof(field), field, $"field name is not valid ({ValidFieldNamePattern}).");
}
Field = field;
switch (#operator)
{
case FilterOperator.EqualTo when value is null:
Operator = FilterOperator.IsNull;
break;
case FilterOperator.NotEqualTo when value is null:
Operator = FilterOperator.IsNotNull;
break;
default:
Operator = #operator;
Value = value;
break;
}
}
#if NETSTANDARD1_3
public string ToJson()
{
return this.Jsonify(new JsonSerializerSettings());
}
#else
public string ToJson() => this.Jsonify();
#endif
public override string ToString() => ToJson();
public bool Equals(Filter other)
=> other != null
&& (ReferenceEquals(other, this)
|| (Equals(other.Field, Field) && Equals(other.Operator, Operator) && Equals(other.Value, Value)));
public override bool Equals(object obj) => Equals(obj as Filter);
#if NETSTANDARD1_3 || NETSTANDARD2_0
public override int GetHashCode() => (Field, Operator, Value).GetHashCode();
#else
public override int GetHashCode() => HashCode.Combine(Field, Operator, Value);
#endif
public IFilter Negate()
{
FilterOperator #operator = Operator switch
{
FilterOperator.EqualTo => FilterOperator.NotEqualTo,
FilterOperator.NotEqualTo => FilterOperator.EqualTo,
FilterOperator.IsNull => FilterOperator.IsNotNull,
FilterOperator.IsNotNull => FilterOperator.IsNull,
FilterOperator.LessThan => FilterOperator.GreaterThan,
FilterOperator.GreaterThan => FilterOperator.LessThan,
FilterOperator.GreaterThanOrEqual => FilterOperator.LessThanOrEqualTo,
FilterOperator.StartsWith => FilterOperator.NotStartsWith,
FilterOperator.NotStartsWith => FilterOperator.StartsWith,
FilterOperator.EndsWith => FilterOperator.NotEndsWith,
FilterOperator.NotEndsWith => FilterOperator.EndsWith,
FilterOperator.Contains => FilterOperator.NotContains,
FilterOperator.IsEmpty => FilterOperator.IsNotEmpty,
FilterOperator.IsNotEmpty => FilterOperator.IsEmpty,
FilterOperator.LessThanOrEqualTo => FilterOperator.GreaterThanOrEqual,
_ => throw new ArgumentOutOfRangeException(nameof(Operator), "Unknown operator"),
};
return new Filter(Field, #operator, Value);
}
public bool Equals(IFilter other) => Equals(other as Filter)
;
public void Deconstruct(out string field, out FilterOperator #operator, out object value)
{
field = Field;
#operator = Operator;
value = Value;
}
}
}
MultiFilter
using DataFilters.Converters;
using Newtonsoft.Json;
using Newtonsoft.Json.Schema;
using System;
using System.Collections.Generic;
using System.Linq;
using static Newtonsoft.Json.DefaultValueHandling;
using static Newtonsoft.Json.Required;
#if !NETSTANDARD1_3
using System.Text.Json.Serialization;
#endif
namespace DataFilters
{
/// <summary>
/// An instance of this class holds combination of <see cref="IFilter"/>
/// </summary>
[JsonObject]
#if NETSTANDARD1_3
[JsonConverter(typeof(MultiFilterConverter))]
#else
[System.Text.Json.Serialization.JsonConverter(typeof(MultiFilterConverter))]
#endif
public class MultiFilter : IFilter, IEquatable<MultiFilter>
{
/// <summary>
/// Name of the json property that holds filter's filters collection.
/// </summary>
public const string FiltersJsonPropertyName = "filters";
/// <summary>
/// Name of the json property that holds the composite filter's logic
/// </summary>
public const string LogicJsonPropertyName = "logic";
public static JSchema Schema => new JSchema
{
Type = JSchemaType.Object,
Properties =
{
[FiltersJsonPropertyName] = new JSchema { Type = JSchemaType.Array, MinimumItems = 2 },
[LogicJsonPropertyName] = new JSchema { Type = JSchemaType.String, Default = "and"}
},
Required = { FiltersJsonPropertyName },
AllowAdditionalProperties = false
};
/// <summary>
/// Collections of filters
/// </summary>
#if NETSTANDARD1_3
[JsonProperty(PropertyName = FiltersJsonPropertyName, Required = Always)]
#else
[JsonPropertyName(FiltersJsonPropertyName)]
#endif
public IEnumerable<IFilter> Filters { get; set; } = Enumerable.Empty<IFilter>();
/// <summary>
/// Operator to apply between <see cref="Filters"/>
/// </summary>
#if NETSTANDARD1_3
[JsonProperty(PropertyName = LogicJsonPropertyName, DefaultValueHandling = IgnoreAndPopulate)]
[JsonConverter(typeof(CamelCaseEnumTypeConverter))]
#else
[JsonPropertyName(LogicJsonPropertyName)]
#endif
public FilterLogic Logic { get; set; }
public virtual string ToJson() => this.Jsonify();
public IFilter Negate()
{
MultiFilter filter = new MultiFilter
{
Logic = Logic switch
{
FilterLogic.And => FilterLogic.Or,
FilterLogic.Or => FilterLogic.And,
_ => throw new ArgumentOutOfRangeException($"Unsupported {Logic}")
},
Filters = Filters.Select(f => f.Negate())
#if DEBUG
.ToArray()
#endif
};
return filter;
}
#if NETSTANDARD1_3 || NETSTANDARD2_0
public override int GetHashCode() => (Logic, Filters).GetHashCode();
#else
public override int GetHashCode()
{
HashCode hash = new HashCode();
hash.Add(Logic);
foreach (IFilter filter in Filters)
{
hash.Add(filter);
}
return hash.ToHashCode();
}
#endif
public bool Equals(IFilter other) => Equals(other as MultiFilter);
public override bool Equals(object obj) => Equals(obj as MultiFilter);
public bool Equals(MultiFilter other)
=> Logic == other?.Logic
&& Filters.Count() == other?.Filters?.Count()
&& Filters.All(filter => other?.Filters?.Contains(filter) ?? false)
&& (other?.Filters.All(filter => Filters.Contains(filter)) ?? false);
}
}
DataFilters.Expressions and DataFilters.Queries are two libraries that I also wrote and that allow to create C# Expressions or WHERE SQLs given an IFilter instance as a input (extension methods).
What i'm trying to do now is to provide an extension point so that I could write a new library (called DataFilters.NodaTime for example) that could handle NodaTime types somehow while deferring everything else to DataFilters.Expressions (a library that I already released).
That extension point should add ability to handle nodatime type but I have no clue how to get started on this
For now, I'm thinking about something like this :
create a new library DataFilters.Expressions.NodaTime
create a new IFilter extension method in it : it will be tailored to handle NodaTime types.
The goal is to be able to handle NodaTime types both with DataFilters.Expressions and DataFilters.Queries for example.
Could it be a good approach or is there a better way to handle this ?
Thanks in advance to anyone who could help me on this

Redis Publish Subscribe does not work properly

I am not able to get advantage from the pub/sub StackExchange.Redis features.
This is my code.
Console application
using StackExchange.Redis;
using System;
using System.Data;
namespace RedisHashSets
{
class Program
{
static void Main(string[] args)
{
#region Prepare Data
var exampleData = new DataTable();
exampleData.Columns.Add("session_guid", typeof(String));
exampleData.Columns.Add("status_code", typeof(String));
exampleData.Columns.Add("date_open", typeof(DateTime));
exampleData.Columns.Add("date_expiry", typeof(DateTime));
exampleData.Columns.Add("extra_id", typeof(long));
exampleData.Columns.Add("current_real_balance", typeof(Decimal));
DataRow row = exampleData.NewRow();
row[0] = Guid.NewGuid();
row[1] = 1;
row[2] = DateTime.Now;
row[3] = DateTime.Now.AddMinutes(30);
row[4] = 44567;
row[5] = 10.34;
exampleData.Rows.Add(row);
#endregion
var redisClient = RedisProvider.Instance;
string guid = Guid.NewGuid().ToString();
// Add an Entry
redisClient.Set(guid, RedisDataMapper.GetHashSet(exampleData)[0]);
// Modify an Entry
HashEntry entry = new HashEntry("exrta_id", 338349);
redisClient.Set(guid, entry);
// Get an Entry
HashSet val = redisClient.Get(guid);
//Invalidate the key
redisClient.redisPubSub.Publish("InvalidateBalances", guid);
}
}
}
Redis Singletone Client
namespace RedisHashSets
{
/// <summary>
/// Provide access to the Redis capabilites used to store sesion. Used as a Singleton
/// </summary>
public class RedisProvider
{
#region Private Fields
private readonly static Lazy<RedisProvider> lazyObj = new Lazy<RedisProvider>(() => new RedisProvider());
private ConnectionMultiplexer redisClient;
private IDatabase redisDatabase;
private IServer currentServer;
private EndPoint[] endPoints;
#endregion Private Fields
#region Public Fields
public static RedisProvider Instance { get { return lazyObj.Value; } }
public ISubscriber redisPubSub;
public bool IsRedisEnableByConfig { get; set; }
#endregion Public Fields
#region Public Methods
private RedisProvider()
{
if (!lazyObj.IsValueCreated)
{
var conf = RedisConfig.GetConfiguration();
if (conf is null)
{
IsRedisEnableByConfig = false;
}
else
{
IsRedisEnableByConfig = true;
redisClient = ConnectionMultiplexer.Connect(conf);
redisDatabase = redisClient.GetDatabase(0);
endPoints = redisClient.GetEndPoints();
currentServer = redisClient.GetServer(endPoints.First());
ManageSubscriber();
}
}
}
private void ManageSubscriber()
{
redisPubSub = redisClient.GetSubscriber();
redisPubSub.Subscribe(new RedisChannel("InvalidateBalances", RedisChannel.PatternMode.Pattern), (channel, message) => MessageAction(message));
}
private void MessageAction(RedisValue message)
{
HashEntry updatedEntry = new HashEntry(HashSet.IsUpdatedFlag, false);
Set(message, updatedEntry);
}
public void Set(string key, HashSet value)
{
redisDatabase.HashSet(key, value.Entries);
Set(key, value.IsUpdated);
}
public void Set(string key, HashEntry value)
{
redisDatabase.HashSet(key, value.Name, value.Value);
}
/// <summary>
/// Get a value from Redis.
/// </summary>
/// <param name="key">The key of the vaule to retrive</param>
/// <returns>Cast the value and retrurn a strongly typed object</returns>
public HashSet Get(string key)
{
HashEntry[] value = redisDatabase.HashGetAll(key);
return new HashSet(value.Length) { Entries = value };
}
/// <summary>
/// Check if a value is in stored or not expired. Time consuming. Use onky if necessary.
/// </summary>
/// <param name="key">The key of the value</param>
public bool IsInCache(string key)
{
return redisDatabase.KeyExists(key);
}
/// <summary>
/// Remove a value from the cache.
/// </summary>
/// <param name="key">The key to remove</param>
public bool Remove(string key)
{
return redisDatabase.KeyDelete(key);
}
/// <summary>
/// Return the number of active keys stored inside the redis databaase.
/// </summary>
public int GetKeysNumber()
{
var retVal = 0;
retVal = currentServer.Keys().Count();
return retVal;
}
/// <summary>
/// Return an enumerable of all the Keys.
/// </summary>
public IEnumerable<string> GetKeys()
{
return currentServer.Keys().Select(k => k.ToString());
}
#endregion
}
}
When I publish a message, it is not catched by the subscriber.
Can you help me?
Try to check you configuration.
Your code is working for me with the "localhost" as the connection string i.e
redisClient = ConnectionMultiplexer.Connect("localhost");
This is the minimal version of your code which is working for me in a console app.
using StackExchange.Redis;
using System;
using System.Linq;
using System.Net;
namespace RedisHashSets
{
class Program
{
static void Main(string[] args)
{
var redisClient = RedisProvider.Instance;
for (int i = 0; i < 10; i++)
{
//Thread.Sleep(100);
redisClient.redisPubSub.Publish("InvalidateBalances", Guid.NewGuid().ToString());
}
Console.ReadLine();
}
}
public class RedisProvider
{
#region Private Fields
private static readonly Lazy<RedisProvider> lazyObj = new Lazy<RedisProvider>(() => new RedisProvider());
private ConnectionMultiplexer redisClient;
private IDatabase redisDatabase;
private IServer currentServer;
private EndPoint[] endPoints;
public static RedisProvider Instance => lazyObj.Value;
public ISubscriber redisPubSub;
public bool IsRedisEnableByConfig { get; set; }
private RedisProvider()
{
if (!lazyObj.IsValueCreated)
{
IsRedisEnableByConfig = true;
redisClient = ConnectionMultiplexer.Connect("localhost");
endPoints = redisClient.GetEndPoints();
currentServer = redisClient.GetServer(endPoints.First());
ManageSubscriber();
}
}
private void ManageSubscriber()
{
redisPubSub = redisClient.GetSubscriber();
redisPubSub.Subscribe(new RedisChannel("InvalidateBalances", RedisChannel.PatternMode.Pattern), (channel, message) => MessageAction(message));
}
private void MessageAction(RedisValue message)
{
Console.WriteLine("msg arrived: " + message);
}
#endregion
}
}
Also please keep your code examples minimal complete and verifiable.

Any way to avoid Object/casts in the child classes here?

Following the book "Artificial Intelligence - A Modern Approach" I am trying to make search algorithm implementations (DFS, A*, etc.) that can work with different problems (path from A to B, sliding-block puzzle, etc.) instead of just the one specific problem.
public abstract class Problem
{
protected Problem(object initialState)
{
InitialState = initialState;
}
public object InitialState { get; }
/// <summary>
/// Checks if the state is the goal state
/// </summary>
public abstract bool IsGoalState(object state);
/// <summary>
/// Returns the actions available from the state
/// </summary>
public abstract ISet<Action> Actions(object state);
/// <summary>
/// Returns the state that results after performing the action on the state
/// </summary>
public abstract object ResultState(object state, Action action);
/// <summary>
/// Returns the cost of action to reach from state to reachedState
/// </summary>
/// <param name="state">The state from which the action will be performed</param>
/// <param name="action">One of the actions available in the state</param>
/// <param name="reachedState">The state that results after performing the action</param>
public virtual int StepCost(object state, Action action, object reachedState)
{
return 1;
}
}
_
public class Node
{
public Node(object state)
{
State = state;
PathCost = 0;
}
/// <summary>
/// </summary>
/// <param name="action">The action that was applied to the parent to generate the node</param>
/// <param name="stepCost">The cost from the parent node to this node</param>
public Node(object state, Node parent, Action action, int stepCost)
: this(state)
{
Parent = parent;
Action = action;
if (Parent != null)
PathCost = Parent.PathCost + stepCost;
}
public object State { get; }
public Node Parent { get; }
/// <summary>
/// The action that was applied to the parent to generate the node
/// </summary>
public Action Action { get; }
/// <summary>
/// The cost of the path from the initial statee to the node
/// </summary>
public int PathCost { get; }
public bool IsRootNode => Parent == null;
public IEnumerable<Node> PathFromRoot()
{
var path = new Stack<Node>();
var node = this;
while (!node.IsRootNode)
{
path.Push(node);
node = node.Parent;
}
path.Push(node); // root
return path;
}
}
_
public abstract class Action
{
/// <summary>
/// true if it is a "No Operation" action
/// </summary>
public virtual bool IsNoOp()
{
return false;
}
public string Name => GetType().Name;
public override string ToString()
{
return Name;
}
}
public class NoOp : Action
{
public override bool IsNoOp()
{
return true;
}
}
public abstract class EightPuzzleAction : Action
{
}
/// <summary>
/// Move the blank tile to the left
/// </summary>
public class MoveLeft : EightPuzzleAction
{
}
// the same MoveRight, MoveTop, MoveBottom
In order to do that I can implement methods from this base Problem class and pass that concrete instance to a search algorithm (that accepts Problem).
class EightPuzzleProblem : Problem
{
private readonly int[,] _goalState =
{
{0, 1, 2},
{3, 4, 5},
{6, 7, 8},
};
public EightPuzzleProblem(int[,] initialState) : base(initialState)
{ }
public override bool IsGoalState(object state)
{
var puzzleState = (int[,]) state;
......... <check if puzzleState is the same as _goalState>
}
............
}
class DepthFirstSearch
{
public IEnumerable<Action> Search(Problem p)
{
return DfsRecursive(p, new Node(p.InitialState));
}
public IEnumerable<Action> DfsRecursive(Problem p, Node node)
{
if (p.IsGoalState(node.State))
return node.PathFromRoot();
.......<simple DFS implementation>
}
}
// usage example
var dfs = new DepthFirstSearch();
var result = dfs.Search(new EightPuzzleProblem(initialState));
if (!result.Any)
// fail
if (result.Count == 1 && result[0].IsNoOp)
// already at goal
foreach (var act in result)
{
if (act is MoveLeft) .....
}
But I see one inconvenience with my class design: in the concrete class implementation code I need to have a lot of casts from object. Is there any way to avoid that?
If I make the Problem class generic and inherit ConcreteProblem from Problem<Something> then I will not be able to use it via Problem...
Why don't you inject your GoalState too and make GameState also pluggable using the interface like below. In this way you can implement any operation you want to do on two States in the concrete class and Problem class just needs to call those functions. Also you can have different types of states too.
public abstract class Problem<T> where T:IGameState
{
protected Problem(T initialState)
{
InitialState = initialState;
}
public T InitialState { get; set; }
/// <summary>
/// Checks if the state is the goal state
/// </summary>
public abstract bool IsGoalState(T state);
}
public class EightPuzzleProblem<T> : Problem<T> where T : IGameState
{
private readonly T _goalState;
public EightPuzzleProblem(T initialState, T goalState)
: base(initialState)
{
_goalState = goalState;
}
public override bool IsGoalState(T state)
{
return _goalState.IsEqual(state);
}
}
public interface IGameState
{
bool IsEqual(IGameState state);
}
public class EightPuzzleGameState : IGameState
{
private readonly int[,] _goalState =
{
{0, 1, 2},
{3, 4, 5},
{6, 7, 8},
};
public bool IsEqual(IGameState state)
{
//Compare state with _goalState
}
}
You could actually make Problem generic with the generic param set to the actual state.
public abstract class Problem<T> where T : IState{
public abstract bool IsGoalState(T state);
}
Within your derived classes you can now simply derive from Problem passing the actual type of IState as generic parameter:
class MyDerived : Problem<MyState> {
public override bool IsGoalState(MyState state) { ... }
}

Comparing a List<T> with another List<t>

I have been reading on how to compare a list with one annother. I have tried to implement the IEquatable interface. Here is what i have done so far:
/// <summary>
/// A object holder that contains a service and its current failcount
/// </summary>
public class ServiceHolder : IEquatable<ServiceHolder>
{
/// <summary>
/// Constructor
/// </summary>
/// <param name="service"></param>
public ServiceHolder(Service service)
{
Service = service;
CurrentFailCount = 0;
}
public Service Service { get; set; }
public UInt16 CurrentFailCount { get; set; }
/// <summary>
/// Public equal method
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
ServiceHolder tmp = obj as ServiceHolder;
if (tmp == null)
{
return false;
}
else
{
return Equals(tmp);
}
}
/// <summary>
/// Checks the internal components compared to one annother
/// </summary>
/// <param name="serviceHolder"></param>
/// <returns>tru eif they are the same else false</returns>
public bool Equals(ServiceHolder serviceHolder)
{
if (serviceHolder == null)
{
return false;
}
if (this.Service.Id == serviceHolder.Service.Id)
{
if (this.Service.IpAddress == serviceHolder.Service.IpAddress)
{
if (this.Service.Port == serviceHolder.Service.Port)
{
if (this.Service.PollInterval == serviceHolder.Service.PollInterval)
{
if (this.Service.ServiceType == serviceHolder.Service.ServiceType)
{
if (this.Service.Location == serviceHolder.Service.Location)
{
if (this.Service.Name == this.Service.Name)
{
return true;
}
}
}
}
}
}
}
return false;
}
}
and this is where I use it:
private void CheckIfServicesHaveChangedEvent()
{
IList<ServiceHolder> tmp;
using (var db = new EFServiceRepository())
{
tmp = GetServiceHolders(db.GetAll());
}
if (tmp.Equals(Services))
{
StateChanged = true;
}
else
{
StateChanged = false;
}
}
Now when I debug and I put a break point in the equals function it never gets hit.
This leads me to think I have implemented it incorrectly or Im not calling it correctly?
If you want to compare the contents of two lists then the best method is SequenceEqual.
if (tmp.SequenceEquals(Services))
This will compare the contents of both lists using equality semantics on the values in the list. In this case the element type is ServiceHolder and as you've already defined equality semantics for this type it should work just fine
EDIT
OP commented that order of the collections shouldn't matter. For that scenario you can do the following
if (!tmp.Except(Services).Any())
You can compare lists without the order most easily with linq.
List<ServiceHolder> result = tmp.Except(Services).ToList();

Categories