MongoDb Insert speed very slow, single threaded inserts and multiple - c#

I have written some test for repository I have wrote.
public interface IEntity<T> {
T Id { get; set; }
}
public abstract class Entity<TEntity> : IEntity<TEntity> {
[BsonId]
public TEntity Id { get; set; }
public override string ToString() {
return JsonConvert.SerializeObject(this, Formatting.Indented, new JsonSerializerSettings {
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
});
}
}
public interface IMongoRepository<TEntity, TE> : IQueryable<TEntity> where TEntity : IEntity<TE> {
string ConnectionString { get; set; }
bool Remove<T>(TEntity entity);
bool Save<T>(TEntity entity);
bool Update<T>(TEntity entity);
}
public sealed class MongoRepository<TEntity, TU> : IMongoRepository<TEntity, TU>
where TEntity : Entity<TU> {
public MongoCollection<TEntity> Collection {
get {
var client = new MongoClient(ConnectionString);
var server = client.GetServer();
var db = server.GetDatabase("mykantina");
return db.GetCollection<TEntity>(typeof (TEntity).Name.Pluralize());
}
}
private IQueryable<TEntity> Queryable {
get { return Collection.FindAll().AsQueryable(); }
}
#region IRepository<T,U> Implementation
public string ConnectionString { get; set; }
public bool Remove<T>(TEntity entity) {
try {
var e = entity as Entity<TU>;
if (e == null) return false;
WriteConcernResult resultConcern = Collection.Remove(Query.EQ("_id", e.Id.ToBson()));
return true;
}
catch (Exception e) {
return false;
}
}
public bool Save<T>(TEntity entity) {
try {
WriteConcernResult resultConcern = Collection.Save(entity);
return true;
}
catch (Exception e) {
return false;
}
}
public bool Update<T>(TEntity entity) {
throw new NotImplementedException();
}
#endregion
#region IQueryable<T> Implementation
public IEnumerator<TEntity> GetEnumerator() {
return Queryable.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return Queryable.GetEnumerator();
}
public Type ElementType {
get { return Queryable.ElementType; }
}
public Expression Expression {
get { return Queryable.Expression; }
}
public IQueryProvider Provider {
get { return Queryable.Provider; }
}
#endregion
}
Here are the tests:
[Test]
public void LinearInsertsToMongoDb() {
Stopwatch total = Stopwatch.StartNew();
for (int i = 0; i <= 100000; i++) {
Stopwatch sw = Stopwatch.StartNew();
var isOk = _repository.Save<Widget>(new Widget {
Name = "Calendar" + i
});
sw.Stop();
Trace.WriteLine(string.Format("Took to insert {0}ms - item {1}", sw.ElapsedMilliseconds, i));
}
Trace.WriteLine(string.Format("================================================================"));
total.Stop();
Trace.WriteLine(string.Format("Total = {0}ms", total.ElapsedMilliseconds));
}
Time result: Total = 212398ms
private static object _sync = new object();
[Test]
public void ConcurrentInsertsToMongoDb() {
Stopwatch total = Stopwatch.StartNew();
var waitHandlers = new List<WaitHandle>();
for (int i = 0; i < 50; i++) {
var threadFinish = new EventWaitHandle(false, EventResetMode.ManualReset);
waitHandlers.Add(threadFinish);
var thread = new Thread(() => {
RunMongoThread();
threadFinish.Set();
});
thread.Start();
}
WaitHandle.WaitAll(waitHandles: waitHandlers.ToArray());
Trace.WriteLine(string.Format("================================================================"));
total.Stop();
Trace.WriteLine(string.Format("Total = {0}ms", total.ElapsedMilliseconds));
}
private static void RunMongoThread() {
for (var i = 0; i < 300; i++) {
bool isLocked = false;
try {
Monitor.Enter(_sync, ref isLocked);
var widget = new Widget {
Name = "Calendar" + i
};
var isOk = _repository.Save<Widget>(widget);
Trace.WriteLine(string.Format("Object: {0} - Thread {1}", widget, Thread.CurrentThread.ManagedThreadId));
}
finally {
if(isLocked)
Monitor.Exit(_sync);
}
}
}
Time result: Total 31303ms
The question what I'm doing wrong and why it's so slow?

Related

Trying to build a simple Pipe and Filter

I am trying to implement the Pipe and Filter pattern integrating TPL Dataflow within it. I am having issues where not all my results are churned out. For example, i put 99999 items into the pipeline and only 85238 came out.
EmployeeModel.cs
public class EmployeeModel
{
public String FirstName { get; set; }
public String LastName { get; set; }
public String FullName { get; set; }
public override String ToString()
{
return $"FirstName: {FirstName}\nLastName: {LastName}\nFullName: {FullName}\n";
}
}
IFilter.cs
public interface IFilter<T>
{
T Execute(T input);
}
AbstractParallelFilter.cs
public abstract class AbstractParallelFilter<T> : IFilter<T>
{
public AbstractParallelFilter()
{
TransformBlock = new TransformBlock<T, T>(new Func<T, T>(Execute), new ExecutionDataflowBlockOptions()
{
BoundedCapacity = DataflowBlockOptions.Unbounded,
MaxDegreeOfParallelism = Environment.ProcessorCount
});
}
public abstract T Execute(T input);
internal TransformBlock<T, T> TransformBlock { get; private set; }
}
IParallelPipeline.cs
public interface IParallelPipeline<T>
{
IParallelPipeline<T> Register(AbstractParallelFilter<T> filter);
IParallelPipeline<T> CompleteRegisteration();
IParallelPipeline<T> Process(T input);
Task CompleteProcessing();
ConcurrentBag<T> Results { get; set; }
}
AbstractParallelPipeline.cs
public abstract class AbstractParallelPipeline<T>: IParallelPipeline<T>
{
public AbstractParallelPipeline()
{
filters = new List<AbstractParallelFilter<T>>();
Results = new ConcurrentBag<T>();
}
public IParallelPipeline<T> Register(AbstractParallelFilter<T> filter)
{
filters.Add(filter);
return this;
}
public abstract IParallelPipeline<T> Process(T input);
public Task CompleteProcessing()
{
if (filters.Count == 0)
throw new Exception("No filters have been registered");
filters.First().TransformBlock.Complete();
return filters.Last().TransformBlock.Completion;
}
public IParallelPipeline<T> CompleteRegisteration()
{
if (filters.Count < 2)
{
return this;
}
else
{
for (int i = filters.Count - 2; i >= 0; i--)
{
filters[i].TransformBlock.LinkTo(filters[i + 1].TransformBlock, new DataflowLinkOptions() { PropagateCompletion = true });
}
ActionBlock<T> dumpBlock = new ActionBlock<T>(x => Results.Add(x));
filters.Last().TransformBlock.LinkTo(dumpBlock, new DataflowLinkOptions() { PropagateCompletion = true });
}
return this;
}
public IList<AbstractParallelFilter<T>> filters;
public ConcurrentBag<T> Results { get; set; }
}
ParallelPipeline.cs
public class ParallelPipeline<T> : AbstractParallelPipeline<T>
{
public override IParallelPipeline<T> Process(T input)
{
filters.First().TransformBlock.Post(input);
return this;
}
}
Program.cs
class Program
{
static void Main(string[] args)
{
List<EmployeeModel> employeeModels = new List<EmployeeModel>();
int count = 99999;
for (int i = 0; i < count; i++)
{
EmployeeModel employee = new EmployeeModel()
{
FirstName = NameGenerator.GenerateFirstName(Gender.Female),
LastName = NameGenerator.GenerateLastName()
};
employeeModels.Add(employee);
}
IParallelPipeline<EmployeeModel> parallelPipeline = new ParallelPipeline<EmployeeModel>()
.Register(new ParallelFirstNameToUpperFilter())
.Register(new ParallelLastNameToUpperFilter())
.Register(new ParallelFullNameConcatFilter())
.CompleteRegisteration();
for (int i = 0; i < count; i++)
{
parallelPipeline.Process(employeeModels[i]);
}
parallelPipeline
.CompleteProcessing()
.Wait();
Console.WriteLine(parallelPipeline.Results.Count);
Console.Read();
}
}
Ok found the newbie bug, during CompleteProcessing(), I need to return my ActionBlock instead of my TransformBlock as the ActionBlock is the last block.

Timer in SignalR overlaps when another group enters into hub

I have created a signalR hub which is solving purpose to show timer at UI of grouped person. If one person starts a play with another one, timer runs very nice.
But the problem is when another group start a new play and previous one is still in progress, I mean Timer is already running, then it overlaps the timer and hold one group. Timer runs once at a time. But I want it to have threading and run for every group.
here is my code:
ConnectionMapping.cs
public class ConnectionMapping<T>
{
private readonly Dictionary<T, HashSet<string>> _connections = new Dictionary<T, HashSet<string>>();
public int Count { get { return _connections.Count; } }
public void Add(T key, string connectionId)
{
lock (_connections)
{
HashSet<string> connections;
if (!_connections.TryGetValue(key, out connections))
{
connections = new HashSet<string>();
_connections.Add(key, connections);
}
lock (connections)
{
connections.Add(connectionId);
}
}
}
public IEnumerable<string> GetConnections(T key)
{
HashSet<string> connections;
if (_connections.TryGetValue(key, out connections))
{
return connections;
}
return Enumerable.Empty<string>();
}
public void Remove(T key, string connectionId)
{
lock (_connections)
{
HashSet<string> connections;
if (!_connections.TryGetValue(key, out connections))
{
return;
}
lock (connections)
{
connections.Remove(connectionId);
if (connections.Count == 0)
{
_connections.Remove(key);
}
}
}
}
}
Below is a Hub class:
public class TimeSyncingHub : Hub
{
private readonly static ConnectionMapping<int> _connections = new ConnectionMapping<int>();
private readonly PlayerPickTicker _playerPickTicker;
private readonly object _PickStateLock = new object();
private IUserBusiness userBusiness;
public TimeSyncingHub() :
this(PlayerPickTicker.Instance)
{
}
public TimeSyncingHub(PlayerPickTicker playerPickTicker)
{
_playerPickTicker = playerPickTicker;
}
public Task LeaveRoom(string roomName)
{
return Groups.Remove(Context.ConnectionId, roomName);
}
public async Task JoinRoom(string roomName)
{
await Groups.Add(Context.ConnectionId, roomName);
Clients.Group(roomName).JoinedRoom("Enter into group - " + roomName);
}
}
Another class where I have written time code is PlayerPickTicker class which is depends in same and implemented in constructor of hub class as shown in class above.
Below is PlayerPickTicker class:
public class PlayerPickTicker : IDisposable
{
private readonly static Lazy<PlayerPickTicker> _instance = new Lazy<PlayerPickTicker>(
() => new PlayerPickTicker(GlobalHost.ConnectionManager.GetHubContext<TimeSyncingHub>()));
private IHubContext _context;
public UserPlayerPickModel UserPlayerPick { get; set; }
public static PlayerPickTicker Instance { get { return _instance.Value; } }
private Timer TickTimer;
private Timer InitialTickTimer;
public int StartSeconds { get; set; }
public int StopSeconds { get; set; }
public bool IsTimerOver { get; set; }
private PlayerPickTicker(IHubContext context)
{
_context = context;
}
public void StartInitialTimer(Model.QueryModel queryModel)
{
try
{
MyDraftBusiness myDraft = new MyDraftBusiness();
myDraft.UdpatePlaysOneMinuteTimerPending(queryModel);
//lock (_InitialTimerStateLock)
//{
//StartSeconds = 60;
//if (InitialTickTimer != null)
//{
// InitialTickTimer.Dispose();
//}
States = new Dictionary<string, bool>() {
{ "IsInitialTimerOver", false},
{ "CallInitialTimerOverMethod", true},
};
PickPlayer = new Dictionary<string, long>() { { "LastPlayerPickId", 0 } };
//lock (States)
//{
StartSeconds = 60;
StopSeconds = 0;
IsInitialTimerOver = false;
CallInitialTimerOverMethod = true;
//}
InitialTickTimer = new Timer();// OnTimerElapsedInitialTimer, queryModel, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
InitialTickTimer.Interval = 1000;
InitialTickTimer.Enabled = true;
InitialTickTimer.Elapsed += (sender, e) => OnTimerElapsedInitialTimer(queryModel, e);
InitialTickTimer.AutoReset = false;
//}
//}
}
catch (Exception ex)
{
Helpers.ExceptionsLog.LogFile(" -( At StartInitialTimer Mehtod )- " + ex.Message);
}
}
private void OnTimerElapsedInitialTimer(object sender, ElapsedEventArgs e)
{
Model.QueryModel queryModel = (Model.QueryModel)sender;
try
{
//lock (_InitialtickStateLock)
//{
var states = (Dictionary<string, bool>)States;
var IsInitialTimerOver = states.FirstOrDefault(x => x.Key == "IsInitialTimerOver").Value;
//Clients.
if (StartSeconds <= StopSeconds && !IsInitialTimerOver)
{
if (InitialTickTimer != null)
{
InitialTickTimer.Dispose();
}
//IsInitialTimerOver = true;
states["IsInitialTimerOver"] = true;
//lock (States)
//{
//************** from client to server
var JsonResult = new JObject();
JsonResult.Add("data", JToken.FromObject(queryModel));
string GroupName = "Group" + queryModel.playId.ToString();
_context.Clients.Group(GroupName).initialTimerCompleted(JsonResult);
//_context.Clients.Group(GroupName, _context.Clients.All).initialTimerCompleted(JsonResult);
//_context.Clients.All.initialTimerCompleted(JsonResult);
MyDraftBusiness myDraft = new MyDraftBusiness();
try
{
myDraft.UdpatePlaysOneMinuteTimerRunning(queryModel);
DraftModel draftModel = myDraft.GetDraftById(queryModel);
if (draftModel != null)
{
var userPlayerPicks = myDraft.GetUserPlayerPickByPlayId(draftModel.PlayId);
if (draftModel.IsDraftFilled)
{
if (draftModel.Play != null && draftModel.Play.IsOneMinuteTimerPending == true)
{
myDraft.UdpatePlaysOneMinuteTimerPending(queryModel);
}
else
{
var activeUserPick = userPlayerPicks.Where(x => !x.Picked).FirstOrDefault();
if (activeUserPick != null)
{
StartTimer(activeUserPick);
}
}
}
}
}
catch (Exception ex)
{
Helpers.ExceptionsLog.LogFile(" -( At OnTimerElapsedInitialTimer Mehtod inner catch )- " + ex.Message);
var userPlayerPicks = myDraft.GetUserPlayerPickByPlayId(queryModel.playId);
var activeUserPick = userPlayerPicks.Where(x => !x.Picked).FirstOrDefault();
if (activeUserPick != null)
{
StartTimer(activeUserPick);
}
}
//}
}
else if (StartSeconds > 0)
{
//IsInitialTimerOver = false;
states["IsInitialTimerOver"] = false;
//lock (States)
//{
StartSeconds -= 1;
var GroupName = "Group" + Convert.ToString(queryModel.playId);
_context.Clients.Group(GroupName).intialTimerElapsed(StartSeconds);
_context.Clients.All.pickRunning(queryModel.playId);
InitialTickTimer.Stop();
InitialTickTimer.Start();
//StartInitialTimer(queryModel);
//}
}
//}
}
catch (Exception ex)
{
Helpers.ExceptionsLog.LogFile(" -( At OnTimerElapsedInitialTimer Mehtod )- " + ex.Message);
}
}
I know it's big code and hard to understand. But I hope for Any Good Developer here, Much appreciation for helper from core of my heart!

c# using other class method

Thanks to NHMountainGoat for an answer!
Implementing Interface looks a good choice so we have only the 'needed' method instanciated.
It looks like this now:
EDIT
class Machine
{
//REM: MachineConnexion is a link to the main server where asking the data
internal linkToPLC LinkToPLC;
public IlinkToPLC ILinkPLC;
public interface IlinkToPLC//Interface to linkPLC
{
Int16 MachineNumIS { get; set; }
}
internal class linkToPLC : IlinkToPLC
{
private Int16 Act_MachineNum;
private List<string> genlnkPLCCanvas;
private List<string> genlnkPLCworkingwith;
static private List<string> ListSymbolNoExist;
private string[] ListToPLClnk = {
"GlobalFolder.PMachine[{0}].",
"GlobalFolder.PMachine[{0}].STATE.",
"GlobalFolder.Machine[{0}].",
"GlobalFolder.Machine[{0}].STATE.",
};
public linkToPLC()//ctor
{
genlnkPLCCanvas = new List<string>(ListToPLClnk);
genlnkPLCworkingwith = new List<string>(ListToPLClnk);
ListSymbolNoExist = new List<string>();
Act_MachineNum = MachineNumIS;
}
public Int16 MachineNumIS { get { return (Int16)ReadWriteMachine("data"); } set { ReadWriteMachine("data", value); } }
public string ValueExist(string ValueToreach, bool WorkingDATA = false)
{
if (!WorkingDATA)
{
for (int inc = 0; inc < genlnkPLCworkingwith.Count; inc++)
{
string StrValueToReach = genlnkPLCworkingwith[inc] + ValueToreach;
if (MachineConnexion.SymbolExists(StrValueToReach))
{
ListSymbolNoExist.Clear();
return StrValueToReach;
}
else ListSymbolNoExist.Add(genlnkPLCworkingwith[inc] + ValueToreach);
}
}
else if (WorkingDATA)
{
string StrValueToReach = genlnkPLCworkingwith[10] + ValueToreach;
if (MachineConnexion.SymbolExists(StrValueToReach))
{
ListSymbolNoExist.Clear();
return StrValueToReach;
}
else ListSymbolNoExist.Add(genlnkPLCworkingwith[10] + ValueToreach);
}
if (ListSymbolNoExist.Count != 0)
{
string ErrorList = "";
for (int inc = 0; inc < ListSymbolNoExist.Count; inc++)
{
ErrorList = string.Concat(ErrorList + "Num: " + inc.ToString() + " " + ListSymbolNoExist[inc].ToString() + "\n");
}
Console.WriteLine("Error" + ErrorList);
}
return null;
}
public object ReadWriteMachine(string VariableName, object DataToWrite = null, bool WorkingDATA = false)
{
string valueToFind = "";
if (ValueExist(VariableName) != "FALSE")
{
if (DataToWrite != null) { MachineConnexion.WriteSymbol(valueToFind, DataToWrite); }
return MachineConnexion.ReadSymbol(valueToFind);
}
return VariableName;
}
}
public Machine() //constructor
{
LinkToPLC = new linkToPLC();
}
}
And It doesn't work telling me that the reference object is not defined to an instance of the object..... in the line : Machine() LinkToPLC = new linkToPLC();//REM I found the bug, it was me ;o)) 24112016
//REM 24112016
What are the main differences between those two concept: static Instance and Interface?
Example:
class Program
{
static void Main(string[] args)
{
ITestInterface InterInstance = new TestInterface();
//test Interface
bool value1 = true;
value1 = InterInstance.invert(value1);
InterInstance.print(value1);
//test Instance static
TestStaticInstance staticInstance = new TestStaticInstance();
staticInstance.Instance.invert(value1);
staticInstance.Instance.print(value1);
Console.ReadKey();
}
}
class TestInterface : ITestInterface
{
public bool invert(bool value)
{
return !value;
}
public void print(bool value)
{
Console.WriteLine(value.ToString()+"\n");
}
private void methodX()
{ }
}
interface ITestInterface
{
bool invert(bool value);
void print(bool value);
}
public class TestStaticInstance
{
public TestStaticInstance Instance;
public TestStaticInstance()
{
Instance = this;
}
internal bool invert(bool value)
{
return !value;
}
internal void print(bool value)
{
Console.WriteLine(value.ToString());
}
}
Thanks
Can you structure your other classes to take an instance of the link class? See:
/// <summary>
/// just a stub to demonstrate the model
/// </summary>
internal class Machine
{
public string ReadData() { return "this is data"; }
public void WriteData(string data) { Console.WriteLine(data); }
}
internal interface IMachineDataAccessor
{
string Read();
void Write(string data);
}
class LinkClass : IMachineDataAccessor
{
protected Machine _machine;
public LinkClass(Machine machine)
{
_machine = machine;
}
public void DoMyWork()
{
// insert work somewhere in here.
string dataFromMachine = Read();
Write("outbound data");
}
public string Read()
{
return _machine.ReadData();
}
public void Write(string data)
{
_machine.WriteData(data);
}
}
class PersistentClass
{
IMachineDataAccessor _machineImpl;
public PersistentClass(IMachineDataAccessor machineAccessImplementation)
{
_machineImpl = machineAccessImplementation;
}
public void DoMyWork()
{
string dataFromMachine = _machineImpl.Read();
// insert work here. Or anywhere, actually..
_machineImpl.Write("outbound data");
}
}
class StateClass
{
IMachineDataAccessor _machineImpl;
public StateClass(IMachineDataAccessor machineAccessImplementation)
{
_machineImpl = machineAccessImplementation;
}
public void DoMyWork()
{
string dataFromMachine = _machineImpl.Read();
// insert work here. Or anywhere, actually..
_machineImpl.Write("outbound data");
}
}
static void Main(string[] args)
{
LinkClass link = new LinkClass(new Machine());
PersistentClass persistent = new PersistentClass(link as IMachineDataAccessor);
StateClass state = new StateClass(link as IMachineDataAccessor);
persistent.DoMyWork();
state.DoMyWork();
link.DoMyWork();
}

Error: cannot convert from 'lab3.MultiSet' to 'string[]'

MultiSet.cs and Form.cs
The best overloaded method match for 'string.Join(string, string[])' has some invalid arguments
Argument '2': cannot convert from 'lab3.MultiSet' to 'string[]'
namespace lab3
{
internal class MultiSet : IEnumerable<int>
{
public MultiSet()
{
}
public MultiSet(IEnumerable<int> elements)
{
if (elements == null)
throw new ArgumentNullException("elements");
foreach (int element in elements)
Add(element);
}
public MultiSet(params int[] elements)
: this((IEnumerable<int>)elements)
{
}
public IEnumerator<int> GetEnumerator()
{
return new Enumerator(this);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public bool Add(int element)
{
if (_cardinal == _elements.Length)
{
int[] newElementsArray = new int[_elements.Length * 2];
_elements.CopyTo(newElementsArray, 0);
_elements = newElementsArray;
}
_elements[_cardinal] = element;
_cardinal++;
_lastModificationTime = DateTime.Now;
return true;
}
public int Count
{
get
{
return _cardinal;
}
}
public override string ToString()
{
return string.Format("{{ {0} }}", string.Join(", ", this));
}
private int _cardinal = 0;
private int[] _elements = new int[8];
private DateTime _lastModificationTime = DateTime.Now;
private sealed class Enumerator: IEnumerator<int>
{
public Enumerator(MultiSet set)
{
if (set == null)
throw new ArgumentNullException("set");
_set = set;
_setLastModificationTime = _set._lastModificationTime;
Reset();
}
public int Current
{
get
{
if (_index < 0 || _set._cardinal <= _index || _setLastModificationTime != _set._lastModificationTime)
throw new InvalidOperationException();
return _set._elements[_index];
}
}
void IDisposable.Dispose()
{
}
object IEnumerator.Current
{
get
{
return Current;
}
}
public bool MoveNext()
{
if (_setLastModificationTime != _set._lastModificationTime)
throw new InvalidOperationException();
_index++;
return (_index < _set.Count);
}
public void Reset()
{
_index = -1;
}
private int _index;
private readonly MultiSet _set;
private readonly DateTime _setLastModificationTime;
}
}
}
namespace lab3
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
_setElementNumericUpDown.Minimum = int.MinValue;
_setElementNumericUpDown.Maximum = int.MaxValue;
_currentSetTextBox.Text = _currentSet.ToString();
}
private void _addSetButton_Click(object sender, EventArgs e)
{
_setsListBox1.Items.Add(_currentSet);
_setsListBox2.Items.Add(_currentSet);
_currentSet = new MultiSet();
_currentSetTextBox.Text = _currentSet.ToString();
}
private void _addElementToSetButton_Click(object sender, EventArgs e)
{
_currentSet.Add((int)_setElementNumericUpDown.Value);
_currentSetTextBox.Text = _currentSet.ToString();
}
private MultiSet _currentSet = new MultiSet();
}
}
Since your class is an IEnumerable< int >, have you tried making a method that returns a string[] of the items in your list?
private string[] ToStringArray()
{
if (this.Count == 0)
return null;
int i = 0;
string[] items = new string[this.Count];
IEnumerator<int> enumerator = this.GetEnumerator();
while(enumerator.MoveNext())
{
items[i] = enumerator.Current.ToString();
i++;
}
// Reset your enumerator index if needed
enumerator.Reset();
return items;
}
Then your ToString() would look like this:
public override string ToString()
{
return string.Format("{{ {0} }}", string.Join(", ", ToStringArray()));
}

Should I unit test by using database simulations

I'm currently in the process of writing various unit tests for an application.
Now, I do have tests to check if the code is working, which it is. But should I also simulate, for example:
Database Unavailable.
Database query returns null.
Database query takes very long time to execute.
I know this is not a coding question, but I would like to have some general thoughts about it.
If it's needed, I thought of the following approach:
SettingsUnavailableMock.Setup(x => x.PageRepository.All()).Throws(new Exception());
SettingsUnavailableMock.Setup(x => x.PageRepository.Get(It.IsAny<int>())).Throws(new Exception());
SettingsUnavailableMock.Setup(x => x.PageRepository.Get(It.IsAny<string>())).Throws(new Exception());
SettingsUnavailableMock.Setup(x => x.PageRepository.Refresh(It.IsAny<Page>())).Throws(new Exception());
SettingsUnavailableMock.Setup(x => x.PageRepository.Save()).Throws(new Exception());
Off course, add for all the repositories.
Then in my test class, I can just choose which Mock I would like to use.
Ideally you should test all of the above; however, it depends on your circumstances. Personally I always test everything that I can feasibly test.
Queries that take a long time are very realistic.
Databases being unavialable is also very realistic.
The Query returning null I'm not so sure about; however, if that is a realistic scenario then by all means stub it and test it.
Update - based on comments I thought this would be a good thing to add
public interface IRepository<T> where T : IRepositoryEntry, new()
{
event EventHandler<RepositoryOperationEventArgs> InsertEvent;
event EventHandler<RepositoryOperationEventArgs> UpdateEvent;
event EventHandler<RepositoryOperationEventArgs> DeleteEvent;
IList<String> PrimaryKeys { get; }
void Insert(T Entry);
void Update(T Entry);
void Delete(Predicate<T> predicate);
bool Exists(Predicate<T> predicate);
T Retrieve(Predicate<T> predicate);
IEnumerable<T> RetrieveAll();
}
public interface IRepositoryEntry
{
IList<String> GetPrimaryKeys();
}
public class OracleRepository
{
const string ConnectionString = "*"
public static IDbConnection GetIDbConnection()
{
IDbConnection connection = new OracleConnection(ConnectionString).OpenConnection();
return connection;
}
public IDbConnection GetConnection()
{
IDbConnection connection = new OracleConnection(ConnectionString).OpenConnection();
return connection;
}
}
public class OracleRepository<T> : OracleRepository, IDisposable, IRepository<T> where T : RepositoryEntryBase, IRepositoryEntry, new()
{
/// <summary>
/// Gets all property names from a type.
/// </summary>
/// <returns>IEnumerable of strings</returns>
static IEnumerable<String> GetEntryPropertyNames(Type type)
{
foreach (var propInfo in type.GetProperties())
yield return propInfo.Name;
}
public event EventHandler<RepositoryOperationEventArgs> InsertEvent;
public event EventHandler<RepositoryOperationEventArgs> UpdateEvent;
public event EventHandler<RepositoryOperationEventArgs> DeleteEvent;
#region Properties
public IList<String> PrimaryKeys
{
get
{
return primaryKeys.AsReadOnly();
}
private set
{
primaryKeys = new List<String>(value);
}
}
public IList<String> Properties { get; private set; }
public String InsertText { get; private set; }
public String UpdateText { get; private set; }
public String DeleteText { get; private set; }
public String SelectText { get; private set; }
#endregion
#region Fields
IDbConnection connection;
IDbTransaction transaction;
List<String> primaryKeys;
#endregion
#region Constructors
public OracleRepository()
{
PrimaryKeys = new List<String>(new T().GetPrimaryKeys());
Properties = new List<String>(GetEntryPropertyNames(typeof(T))).AsReadOnly();
InsertText = GenerateInsertText();
UpdateText = GenerateUpdateText();
SelectText = GenerateSelectText();
DeleteText = GenerateDeleteText();
connection = GetConnection();
}
#endregion
#region Interface Implementations
public void Insert(T Entry)
{
Insert(connection, Entry);
}
public void Update(T Entry)
{
Update(connection, Entry);
}
public void Delete(Predicate<T> predicate)
{
Delete(connection, predicate);
}
public T Retrieve(Predicate<T> predicate)
{
return Retrieve(connection, predicate);
}
public bool Exists( Predicate<T> predicate)
{
return Exists(connection, predicate);
}
public IEnumerable<T> RetrieveAll()
{
return RetrieveAll(connection);
}
public void Dispose()
{
if (transaction != null)
transaction.Dispose();
connection.Dispose();
}
#endregion
#region Public Methods
public void StartTransaction()
{
if (transaction != null)
throw new InvalidOperationException("Transaction is already set. Please Rollback or commit transaction");
transaction = connection.BeginTransaction();
}
public void CommitTransaction()
{
transaction.Commit();
transaction.Dispose();
transaction = null;
}
public void RollbackTransaction()
{
transaction.Rollback();
transaction.Dispose();
transaction = null;
}
public void Insert(IDbConnection connection, T Entry)
{
Type type = typeof(T);
List<Object> args = new List<Object>();
for (int i = 0; i < Properties.Count; i++)
args.Add(type.GetProperty(Properties[i]).GetValue(Entry));
connection.NonQuery(InsertText, args.ToArray());
if (InsertEvent != null)
InsertEvent(this, new OracleRepositoryOperationEventArgs() { Entry = Entry, Transaction = (transaction != null) });
}
public void Update(IDbConnection connection, T Entry)
{
Type type = typeof(T);
List<Object> args = new List<Object>();
foreach (var propertyName in Properties.Where(p => !PrimaryKeys.Any(k => k == p)))
args.Add(type.GetProperty(propertyName).GetValue(Entry));
foreach (var PropertyName in PrimaryKeys)
args.Add(type.GetProperty(PropertyName).GetValue(Entry));
connection.NonQuery(UpdateText, args.ToArray());
if (UpdateEvent != null)
UpdateEvent(this, new OracleRepositoryOperationEventArgs() { Entry = Entry, Transaction = (transaction != null) });
}
public void Delete(IDbConnection connection, Predicate<T> predicate)
{
var entryList = RetrieveAll(connection).Where(new Func<T, bool>(predicate));
Type type = typeof(T);
foreach(var entry in entryList)
{
List<Object> args = new List<Object>();
foreach (var PropertyName in PrimaryKeys)
args.Add(type.GetProperty(PropertyName).GetValue(entry));
connection.NonQuery(DeleteText, args.ToArray());
if (DeleteEvent != null)
DeleteEvent(this, new OracleRepositoryOperationEventArgs() { Entry = null, Transaction = (transaction != null) });
}
}
public T Retrieve(IDbConnection connection, Predicate<T> predicate)
{
return RetrieveAll(connection).FirstOrDefault(new Func<T, bool>(predicate));
}
public bool Exists(IDbConnection connection, Predicate<T> predicate)
{
return RetrieveAll(connection).Any(new Func<T, bool>(predicate));
}
public IEnumerable<T> RetrieveAll(IDbConnection connection)
{
List<T> collection = new List<T>();
var result = connection.Query(SelectText);
foreach (var row in result.Tuples)
collection.Add(RepositoryEntryBase.FromPlexQueryResultTuple(new T(), row) as T);
return collection;
}
#endregion
#region Private Methods
String GenerateInsertText()
{
String statement = "INSERT INTO {0}({1}) VALUES ({2})";
//Do first entry here becasse its unique input.
String columnNames = Properties.First();
String delimiter = ", ";
String bph = ":a";
String placeHolders = bph + 0;
//Start # 1 since first entry is already done
for (int i = 1; i < Properties.Count; i++)
{
columnNames += delimiter + Properties[i];
placeHolders += delimiter + bph + i;
}
statement = String.Format(statement, typeof(T).Name, columnNames, placeHolders);
return statement;
}
String GenerateUpdateText()
{
String bph = ":a";
String cvpTemplate = "{0} = {1}";
String statement = "UPDATE {0} SET {1} WHERE {2}";
//Can only set Cols that are not a primary Keys, Get those Columns
var Settables = Properties.Where(p => !PrimaryKeys.Any(k => k == p)).ToList();
String cvp = String.Format(cvpTemplate, Settables.First() , bph + 0 );
String condition = String.Format(cvpTemplate, PrimaryKeys.First(), bph + Settables.Count);
//These are the values to be set | Start # 1 since first entry is done above.
for (int i = 1; i < Settables.Count; i++)
cvp += ", " + String.Format(cvpTemplate, Settables[i], bph + i);
//This creates the conditions under which the values are set. | Start # 1 since first entry is done above.
for (int i = Settables.Count + 1; i < Properties.Count; i++)
condition += ", " + String.Format(cvpTemplate, PrimaryKeys[i - Settables.Count], bph + i);
statement = String.Format(statement, typeof(T).Name, cvp, condition);
return statement;
}
String GenerateDeleteText()
{
String bph = ":a";
String cvpTemplate = "{0} = {1}";
String statement = "DELETE FROM {0} WHERE {1}";
String condition = String.Format(cvpTemplate, PrimaryKeys.First(), bph + 0);
for (int i =1; i < PrimaryKeys.Count; i++)
condition += ", " + String.Format(cvpTemplate, PrimaryKeys[i], bph + i);
statement = String.Format(statement, typeof(T).Name, condition);
return statement;
}
String GenerateSelectText()
{
String statement = "SELECT * FROM {0}";
statement = String.Format(statement, typeof(T).Name);
return statement;
}
#endregion
}
This is what an element of that implements IReposistoryEntry looks like:
public class APPS : RepositoryEntryBase, IRepositoryEntry
{
public int APP_ID { get; set; }
public string AUTH_KEY { get; set; }
public string TITLE { get; set; }
public string DESCRIPTION { get; set; }
public int IS_CLIENT_CUSTOM_APP { get; set; }
public APPS() : base() {
primaryKeys.Add("APP_ID");
}
public APPS(PlexQueryResultTuple plexTuple) : base(plexTuple) { }
}
public class RepositoryEntryBase
{
public static RepositoryEntryBase FromPlexQueryResultTuple( RepositoryEntryBase reb, PlexQueryResultTuple plexTuple)
{
if (plexTuple.parent == null)
throw new NotSupportedException("This Operation is Not supported by this PlexTuple.");
Type type = reb.GetType();
var pInfo = type.GetProperties();
PlexQueryResult result = plexTuple.parent;
foreach (var p in pInfo)
{
int index = result.Tuples.IndexOf(plexTuple);
if (result[p.Name, index] == null)
continue;
var conversationType = Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType;
object value = Convert.ChangeType(result[p.Name, index], (result[p.Name, index] != null)?conversationType: p.PropertyType);
p.SetValue(reb, value);
}
return reb;
}
protected IList<String> primaryKeys;
public RepositoryEntryBase()
{
primaryKeys = new List<String>();
}
public RepositoryEntryBase(PlexQueryResultTuple plexTuple) : this()
{
FromPlexQueryResultTuple(this, plexTuple);
}
public IList<String> GetPrimaryKeys()
{
return primaryKeys;
}
}
Below I've posted the mock database. What is important to recogonize here is that the tests actually use the interface and I can exchange can interchange the real database with the mock one very easily. I like to reuse this code a fair amount (its actually in a dll for me). So I don't have to recode database code for every project.
public class InMemoryRepository<T> : IRepository<T> where T : IRepositoryEntry, new()
{
//RepositoryEntryBase,
public event EventHandler<RepositoryOperationEventArgs> InsertEvent;
public event EventHandler<RepositoryOperationEventArgs> UpdateEvent;
public event EventHandler<RepositoryOperationEventArgs> DeleteEvent;
public IList<String> PrimaryKeys { get; protected set; }
List<T> data;
public InMemoryRepository() {
PrimaryKeys = new List<String>(new T().GetPrimaryKeys());
data = new List<T>();
}
public void Insert(T Entry){
if(Get(Entry) != null)
throw new Exception("Duplicate Entry - Identical Key already exists");
data.Add(Entry);
if (InsertEvent != null)
InsertEvent(this, new RepositoryOperationEventArgs() { Entry = Entry });
}
public void Update(T Entry){
var obj = Get(Entry);
if (obj == null)
throw new Exception("Object does not exist");
obj = Entry;
if (UpdateEvent != null)
UpdateEvent(this, new RepositoryOperationEventArgs() { Entry = obj });
}
public void Delete(Predicate<T> predicate)
{
data.RemoveAll(predicate);
if (DeleteEvent != null)
DeleteEvent(this, new RepositoryOperationEventArgs() { Entry = null });
}
public bool Exists(Predicate<T> predicate)
{
return data.Exists(predicate);
}
public T Retrieve(Predicate<T> predicate)
{
return data.FirstOrDefault(new Func<T, bool>(predicate));
}
public IEnumerable<T> RetrieveAll()
{
return data.ToArray();
}
T Get(T Entry)
{
//Returns Entry based on Identical PrimaryKeys
Type entryType = typeof(T);
var KeyPropertyInfo = entryType.GetProperties().Where(p => PrimaryKeys.Any(p2 => p2 == p.Name));
foreach (var v in data)
{
//Assume the objects are identical by default to prevent false positives.
Boolean AlreadyExists = true;
foreach (var property in KeyPropertyInfo)
if (!property.GetValue(v).Equals(property.GetValue(Entry)))
AlreadyExists = false;
if (AlreadyExists)
return v;
}
return default(T);
}
}
It really depends on what your code interacting with the database is doing.
The philosophy behind unit test is to test a class by itself. All its external dependencies should be mocked.
However you might also check if your class correctly uses its dependencies. This would be an interaction test
And eventually, if everything sounds good you want to check if the whole system works together. It is an integration test.
Note that there is some librairies allowing you to perform integration tests more easily, such as Specflow
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
public interface IProductRepository
{
List<Product> LoadProducts();
}
public class ProductRepository : IProductRepository
{
public List<Product> LoadProducts()
{
// database code which returns the list of product
return new List<Product>();
}
}
public class StorageStatisticsGenerator
{
private readonly IProductRepository _repository;
public StorageStatisticsGenerator(IProductRepository repository)
{
_repository = repository;
}
public int ComputeNumberOfProducts()
{
var products = _repository.LoadProducts();
return products.Count;
}
}
Given the following class you might want to test different kind of things.
[TestFixture]
public class StorageStatisticsGeneratorTests
{
private Mock<IProductRepository> _productRepository;
private StorageStatisticsGenerator _statisticGenerator;
[SetUp]
public void Setup()
{
_productRepository = new Mock<IProductRepository>();
_statisticGenerator = new StorageStatisticsGenerator(_productRepository.Object);
}
// In this test we test if the statistic generator works correctly
// This is a UNIT TEST
[Test]
public void ComputeNumberOfProducts_Should_Returns_TheCorrectCount()
{
// Arrange
_productRepository.Setup(p => p.LoadProducts()).Returns(new List<Product>
{
new Product(), new Product(), new Product()
});
// Act
int result = _statisticGenerator.ComputeNumberOfProducts();
// Assert
Assert.AreEqual(3, result);
}
// In this test we test if the statistic generator use the repository as expected
// This is an INTERACTION TEST, you could check corner case using "real life data"
[Test]
public void ComputeNumberOfProducts_Should_Use_The_Product_Repository()
{
// Arrange
_productRepository.Setup(p => p.LoadProducts()).Returns(new List<Product>
{
new Product()
});
// Act
_statisticGenerator.ComputeNumberOfProducts();
// Assert
_productRepository.Verify(p => p.LoadProducts());
}
// In this test we use the real repository this is an INTEGRATION TEST
// You can flag this kind of slow test to run only during the night for instabce
[Test, Category("Nightly")]
public void ComputeNumberOfProducts_Should_Correctly_Integrate_With_ProductRepository()
{
// Arrange
_statisticGenerator = new StorageStatisticsGenerator(new ProductRepository());
// Act
_statisticGenerator.ComputeNumberOfProducts();
// Assert
_productRepository.Verify(p => p.LoadProducts());
}
}
If you want to know more, you can read The art of unit testing

Categories

Resources