Convert string to IMemoryOwner / Copy string into a rented buffer - c#

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));
}
}

Related

Cosmos DB - CreateCollectionIfNotExistsAsync Generic method

I am using cosmos DB and I have created below generic repository for CRUD operations.
public class CosmosDBRepository : ICosmosDBRepository
{
private readonly DocumentClient _client;
private readonly string DatabaseId = "FleetHub";
public CosmosDBRepository()
{
var endpoint = CloudConfigurationManager.GetSetting("CosmoDbEndpoint");
var authkey = CloudConfigurationManager.GetSetting("CosmoDbAuthKey");
try
{
if (endpoint == null || authkey == null)
{
throw new ArgumentNullException("CosmoDbEndpoint or CosmoDbAuthKey could not be found in the config file, check your settings.");
}
if (_client == null)
{
_client = new DocumentClient(new Uri(endpoint), authkey, connectionPolicy: new ConnectionPolicy { EnableEndpointDiscovery = false });
}
CreateDatabaseIfNotExistsAsync().Wait();
CreateCollectionIfNotExistsAsync().Wait();
}
catch (Exception e)
{
throw new Exception($"Initialise failed CosmoDbEndpoint {endpoint} or CosmoDbAuthKey {authkey} could not be found in the config file, check your settings. {e.Message}");
}
}
/// <summary>
///
/// </summary>
/// <param name="id"></param>
/// <param name="partitionkey"></param>
/// <returns></returns>
public async Task<T> GetItemAsync<T>(string id, string partitionkey) where T : class
{
try
{
string CollectionId = GetAttributeCosmoDbCollection<T>(typeof(T));
Document document = await _client.ReadDocumentAsync(UriFactory.CreateDocumentUri(DatabaseId, CollectionId, id), new RequestOptions { PartitionKey = new PartitionKey(partitionkey) });
return (T)(dynamic)document;
}
catch (DocumentClientException e)
{
if (e.StatusCode == System.Net.HttpStatusCode.NotFound)
{
return null;
}
else
{
throw;
}
}
}
/// <summary>
///
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public async Task<Document> CreateItemAsync<T>(T item) where T : class
{
string CollectionId = GetAttributeCosmoDbCollection<T>(typeof(T));
return await _client.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId), item);
}
public async Task<IEnumerable<T>> GetItemsAsync<T>(Expression<Func<T, bool>> predicate, Expression<Func<T, object>> orderByDesc, int takeCount = -1)
where T : class
{
string CollectionId = GetAttributeCosmoDbCollection<T>(typeof(T));
var criteria = _client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId), new FeedOptions { EnableCrossPartitionQuery = true })
.Where(predicate)
.OrderByDescending(orderByDesc)
.AsDocumentQuery();
IDocumentQuery<T> query = criteria;
List<T> results = new List<T>();
while (query.HasMoreResults)
{
if (takeCount > -1 && results.Count >= takeCount)
{
break;
}
results.AddRange(await query.ExecuteNextAsync<T>());
}
return results;
}
/// <summary>
///
/// </summary>
/// <param name="id"></param>
/// <param name="item"></param>
/// <returns></returns>
public async Task<Document> UpdateItemAsync<T>(string id, T item) where T : class
{
string CollectionId = GetAttributeCosmoDbCollection<T>(typeof(T));
return await _client.ReplaceDocumentAsync(UriFactory.CreateDocumentUri(DatabaseId, CollectionId, id), item);
}
#region private methods
private async Task CreateDatabaseIfNotExistsAsync()
{
try
{
await _client.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(DatabaseId));
}
catch (DocumentClientException e)
{
if (e.StatusCode == System.Net.HttpStatusCode.NotFound)
{
await _client.CreateDatabaseAsync(new Database { Id = DatabaseId });
}
else
{
throw;
}
}
}
/// <summary>
///
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
private string GetPartitionKeyAttributeCosmoDbCollection(Type t)
{
// Get instance of the attribute.
CosmoDBCollection attribute =
(CosmoDBCollection)Attribute.GetCustomAttribute(t, typeof(CosmoDBCollection));
if (attribute == null)
{
throw new Exception("The attribute CosmoDbCollection was not found.");
}
return attribute.PartitionKey;
}
private string GetAttributeCosmoDbCollection<T>(Type t) where T : class
{
// Get instance of the attribute.
CosmoDBCollection attribute =
(CosmoDBCollection)Attribute.GetCustomAttribute(t, typeof(CosmoDBCollection));
if (attribute == null)
{
throw new Exception("The attribute CosmoDbCollection was not found.");
}
return attribute.Name;
}
#endregion
}
Here is CosmoDBCollection class:
public class CosmoDBCollection : Attribute
{
public string Name { get; set; }
public string PartitionKey { get; set; }
}
I am calling CreateCollectionIfNotExistsAsync().Wait(); in the constructor and this method required collectionId. How Can I pass CollectionId to this method? As this is the generic repository.
Do I need to create a Generic CreateCollectionIfNotExistsAsync() method?
If your collection name is based off <T>, why not simply have CosmosDBRepository<T> as the actual class. That way you can get the value also on the constructor.
Ideally it would also be a readonly private property that you only calculate once (on the constructor) and reuse on all operations to avoid paying the cost to construct it later on (since it doesn't change).

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.

C# Polymorphism with Dictionaries not working as intended

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);
}
}

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();

Entity Framework - System.StackOverflowException

I am new to entity framework and have hit an issue trying to insert new items into a lookup table.
The error is -
"An unhandled exception of type System.StackOverflowException
occurred in mscorlib.dll"
It is thrown in the final code block below where public DIEMEntities() is called.
It occurs whenever I add a new item, I can update items ok.
Any help would be appreciated.
The code is -
protected void OnSave(object sender, EventArgs e)
{
ArrayList validationErrors = new ArrayList();
ContactTypeEO contactType = new ContactTypeEO();
if (uxID.Value.Length > 0)
{
//Updating
contactType.Id = int.Parse(uxID.Value);
}
contactType.Name = uxName.Text;
contactType.ExpressionValidator = uxExpression.Text;
contactType.Save(ref validationErrors);
if (validationErrors.Count > 0)
{
ShowValidationMessages(validationErrors);
}
else
{
this.RefreshUI();
}
}
public bool Save(ref ArrayList validationErrors)
{
ValidateSave(ref validationErrors);
if (validationErrors.Count == 0)
{
if (Id == 0)
{
ContactTypeData.Insert(Name, ExpressionValidator);
}
else
{
ContactTypeData.Update(Id, Name, ExpressionValidator);
}
return true;
}
else
{
return false;
}
}
/// <summary>
/// Inserts the new Contact Type.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="validator">The validator.</param>
public static void Insert(string name, string validator)
{
using (DIEMEntities diemEntities = new DIEMEntities())
{
Insert(name, validator);
}
}
/// <summary>
/// Inserts the new Contact Type.
/// </summary>
/// <param name="diemEntities">The DIEM Entities.</param>
/// <param name="name">The name.</param>
/// <param name="validator">The validator.</param>
public static void Insert(DIEMEntities diemEntities, string name, string validator)
{
diemEntities.usp_ContactTypes_Insert(name, validator);
}
public partial class DIEMEntities : DbContext
{
public DIEMEntities()
: base("name=DIEMEntities")
{
}
... OTHER CODE}
You have a recursive loop here:
public static void Insert(string name, string validator)
{
using (DIEMEntities diemEntities = new DIEMEntities())
{
Insert(name, validator);
}
}
This will execute continuously until you run out of memory.
I believe you're intention is to do this:
public static void Insert(string name, string validator)
{
using (DIEMEntities diemEntities = new DIEMEntities())
{
Insert(diemEntities, name, validator);
}
}

Categories