I'm creating a program which will execute a command after user input.
Some commands I want to implement are: creating, reading a file, getting current working directory etc.
I created a dictionary which will store user input and corresponding command:
public static Dictionary<string, Action<string[]>> Commands { get; set; } = new Dictionary<string, Action<string[]>>()
{
{"pwd", PrintWorkingDirectory },
{"create", CreateFile },
{"print", ReadFile },
};
Unfortunately I have issues with triggering the method:
public void Run()
{
Console.WriteLine("Welcome, type in command.");
string input = null;
do
{
Console.Write("> ");
input = Console.ReadLine();
Execute(input);
} while (input != "exit");
}
public int Execute(string input)
{
if(Commands.Keys.Contains(input))
{
var action = Commands.Values.FirstOrDefault(); //doesn't work, gives '{command} not found'
}
Console.WriteLine($"{input} not found");
return 1;
}
Also I noticed that this solution would not work with method which is not void, but returns something, as for example CreateFile.
public static string CreateFile(string path)
{
Console.WriteLine("Create a file");
string userInput = Console.ReadLine();
try
{
string[] file = userInput.Split(new char[] { ' ' }).Skip(1).ToArray();
string newPath = Path.GetFullPath(Path.Combine(file));
using (FileStream stream = new FileStream(newPath, FileMode.Create, FileAccess.ReadWrite))
{
stream.Close();
}
using (StreamWriter sw = new StreamWriter(newPath))
{
Console.WriteLine("Please type the content.Press Enter to save.");
sw.WriteLine(Console.ReadLine());
sw.Close();
Console.WriteLine("File {0} has been created", newPath);
}
}
catch (Exception)
{
throw;
}
return path;
}
public static void ReadFile(string[] args)
{
Console.WriteLine("Reading file");
string userInput = Console.ReadLine();
string[] file = userInput.Split(new char[] { ' ' }).Skip(1).ToArray();
string newPath = Path.GetFullPath(Path.Combine(file));
string[] lines = File.ReadAllLines(newPath);
foreach (string line in lines)
Console.WriteLine(line);
}
public static void PrintWorkingDirectory(string[] args)
{
var currentDirectory = Directory.GetCurrentDirectory();
Console.WriteLine(currentDirectory);
}
Could somebody advise me how to deal with these issues?
Is it that this dictionary I created does not make much sense at all?
First problem: You're always fetching the first element of the dictionary and are not using the index operator to retrieve the correct value. Therefore change:
if(Commands.Keys.Contains(input))
{
var action = Commands.Values.FirstOrDefault(); //doesn't work, gives '{command} not found'
}
to:
public int Execute(string input)
{
if (Commands.Keys.Contains(input))
{
var action = Commands[input]; //doesn't work, gives '{command} not found'
action?.Invoke(new string[] { });
}
else
{
Console.WriteLine($"{input} not found");
}
return 1;
}
Regarding to your second question about dictionary usage. I think it is ok to use a dictionary to map different commands based on a given key. The alternative would be switch or if constructs, which can be prevented in Object Oriented Programming.
Regarding to your question about string CreateFile(string path). Since C# is strongly typed language your dictionary can only contain objects of type Action<string[]>, so you can't use methods with another signature than that. One solution is to add another dictionary in the form of Dictionary<string,Func<string[], string>. As a result you'll get more and more dictionaries depending on your method signatures. From here on you should think to build to encapsulate your commands in an e.g. CommandInterpreter class, that could offer an API like that:
void Request(string cmdName, string[] cmdParameters);
string GetLastResult();
int GetLastCode();
Update:
Below code shows a possible object oriented solution (I've left out interfaces to make the code more compact):
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp1
{
public class Command<T>
{
public string Name { get; }
public T TheCommand { get; }
public Command(string name, T theCommand)
{
Name = name;
TheCommand = theCommand;
}
}
public interface ICommandResult
{
void Ok(Action<ICommandResult> yes, Action<ICommandResult> no);
int Code { get; }
string Description { get; }
}
public abstract class CommandResult : ICommandResult
{
public int Code { get; }
public string Description { get; }
protected CommandResult(int code, string description)
{
Code = code;
Description = description;
}
public abstract void Ok(Action<ICommandResult> yes, Action<ICommandResult> no);
}
public class NullCommandResult : CommandResult
{
public NullCommandResult() : base(-1, "null")
{
}
public override void Ok(Action<ICommandResult> yes, Action<ICommandResult> no) => no?.Invoke(this);
}
public class SuccessCommandResult : CommandResult
{
public SuccessCommandResult(string description) : base(0, description)
{
}
public override void Ok(Action<ICommandResult> yes, Action<ICommandResult> no) => yes?.Invoke(this);
}
public class CommandInterpreter
{
private Dictionary<string, Func<IEnumerable<string>, ICommandResult>> Commands = new Dictionary<string, Func<IEnumerable<string>, ICommandResult>>();
public void RegisterCommand(Command<Func<IEnumerable<string>, ICommandResult>> cmd)
=> Commands.Add(cmd.Name, cmd.TheCommand);
public ICommandResult RunCommand(string name, IEnumerable<string> parameters)
=> Commands.Where(kvp => kvp.Key.Equals(name))
.Select(kvp => kvp.Value)
.DefaultIfEmpty(strArr => new NullCommandResult())
.Single()
.Invoke(parameters);
}
class Program
{
private CommandInterpreter _cmdInterpreter;
private Program()
{
_cmdInterpreter = new CommandInterpreter();
_cmdInterpreter.RegisterCommand(new Command<Func<IEnumerable<string>, ICommandResult>>("pwd", PrintWorkingDirectory));
_cmdInterpreter.RegisterCommand(new Command<Func<IEnumerable<string>, ICommandResult>>("create", CreateFile));
_cmdInterpreter.RegisterCommand(new Command<Func<IEnumerable<string>, ICommandResult>>("print", ReadFile));
}
private static CommandResult ReadFile(IEnumerable<string> arg) => new SuccessCommandResult("File read");
private static CommandResult CreateFile(IEnumerable<string> arg) => new SuccessCommandResult("File xyz created");
private static CommandResult PrintWorkingDirectory(IEnumerable<string> arg) => new SuccessCommandResult("Printed something");
static void Main() => new Program().Run();
private void Run()
{
Console.WriteLine("Welcome, type in command.");
string input;
do
{
Console.Write("> ");
input = Console.ReadLine();
var cmdResult = _cmdInterpreter.RunCommand(input, Enumerable.Empty<string>());
cmdResult.Ok(
r => Console.WriteLine($"Success: {cmdResult.Code}, {cmdResult.Description}"),
r => Console.WriteLine($"FAILED: {cmdResult.Code}, {cmdResult.Description}"));
} while (input != "exit");
}
}
}
Output:
Welcome, type in command.
> pwd
Success: 0, Printed something
> create
Success: 0, File xyz created
> abc
FAILED: -1, null
>
You can just copy the code and play around with it.
How do I separate my code into their own classes and still have it function the same? This is currently what my code looks like.
using System;
using System.Collections.Generic;
using System.Xml;
using XCENT.JobServer.JobPlugIn;
using System.IO;
using HPD.API.Utility.DataAccess;
namespace DataPurge
{
public class Purge : IJob, IJobControl {
public IJobControl JobControl { get { return ( this ); } }
public int MaxInstanceCount { get { return 1; } }
public string Name { get { return "DataPurge"; } }
public Purge() { }
public void Run( string XmlFragment ) {
XmlNode xmlNode = null;
try
{
xmlNode = Common.ConstructXmlNodeFromString(XmlFragment, "Params");
var list = DataList();
foreach (var item in list)
{
var factory = new PurgerFactory(item);
IPurger purge = factory.Purger;
purge.Purge();
purge = null;
factory = null;
}
}
catch (Exception ex)
{
throw;
}
}
public interface IPurger
{
void Purge();
}
public enum PurgeType
{
File,
Database,
}
public class FilePurger : IPurger
{
private Parameters parameter;
public FilePurger(Parameters parameter)
{
this.parameter = parameter;
}
public void Purge()
{
var files = new DirectoryInfo(parameter.FilePath).GetFiles();
foreach (var file in files)
{
if (DateTime.Now - file.CreationTime > TimeSpan.FromDays(7))
{
File.Delete(file.FullName);
}
}
}
}
public class DbPurger : IPurger
{
private Parameters parameter;
public DbPurger(Parameters parameter)
{
this.parameter = parameter;
}
public void Purge()
{
var access = new SqlDataAccess();
var sqlParams = new Dictionary<string, object>();
sqlParams.Add("#OlderThanDays", parameter.OlderThanDays);
access.ExecuteNonQuery(parameter.CString, parameter.SPName, sqlParams, 30, false);
}
}
private List<Parameters> DataList()
{
var sqlParams = new SqlDataAccess();
var list = sqlParams.GetDataTableAsList<Parameters>("Data Source = MYSERVER; Initial Catalog = MYDATABASE; User ID = UID; Password = PASSWORD;", "purge.spoDataTable", null);
return list;
}
public class PurgerFactory
{
public IPurger Purger { get; set; }
public PurgerFactory(Parameters parameter)
{
PurgeType type = (PurgeType)Enum.Parse(typeof(PurgeType), parameter.PurgeType);
switch (type)
{
case PurgeType.File:
Purger = new FilePurger(parameter);
break;
case PurgeType.Database:
Purger = new DbPurger(parameter);
break;
default:
throw new NotImplementedException();
}
}
}
/// <summary>
/// Used to submit a job via the job monitor
/// </summary>
public XmlNode JobXMLNode => Common.ConstructXmlNodeFromString("" +
"<JobParams>" +
" <Param Name=\"InfrastructureAPI\" DataType=\"String\">" +
" <Description>Infrastructure API URL.</Description>" +
" </Param>" +
" <Param Name=\"EnvironmentName\" DataType=\"String\">" +
" <Description>The current environment.</Description>" +
" </Param>" +
"</JobParams>",
"JobParams");
}
}
Currently all parts of the program are stuffed into this one single class. I want to separate them out into their own separate classes to make the code much cleaner but still have it function the same. I'm still a beginner coder and don't know the first place to start. Any help would be much appreciated!
You should create a file IPurger.cs for the interface IPurger, then a file FilePurger.cs for the class FilePurger, the file DbPurger.cs for the class DbPurger and lastly PurgerFactory.cs for the class PurgerFactory.
That should clean up your code quite well.
If that enum is used from multiple places, you may want to place it in its own class too, perhaps a generic Enums.cs.
Currently i've got this code:
private async Task<bool> IsMentionedInDisposeCallAsync(SyntaxNodeAnalysisContext context, FieldDeclarationSyntax fieldDeclarationSyntax)
{
foreach (var variableDeclaratorSyntax in fieldDeclarationSyntax.Declaration.Variables)
{
var declaredSymbol = context.SemanticModel.GetDeclaredSymbol(variableDeclaratorSyntax);
if (declaredSymbol is IFieldSymbol fieldSymbol)
{
// SymbolFinder.FindReferencesAsync()
var b = fieldSymbol.Locations;
// context.SemanticModel.Compilation.
}
}
return false;
}
And this scenario:
private static readonly string TestSourceImplementsDisposableAndDoesMentionDisposableField = #"
using System;
using System.IO;
namespace ConsoleApplication1
{
public class SampleDisposable : IDisposable
{
public void Dispose()
{
}
}
public class SampleConsumer : IDisposable
{
private SampleDisposable _disposable = new SampleDisposable();
private IDisposable _ms = new MemoryStream();
public void Dispose()
{
_disposable?.Dispose();
_ms?.Dispose();
}
}
}";
Ultimately my desire is to figure out whether a dispose method is accessing a disposable field. Unfortunately i can't seem to find a way to get this working without using SymbolFinder, which requires a solution.
I did something similar with SymbolFinder and it was an easy thing to do - but how do i do it from the functionality available within a diagnostic?
Am i missing something obvious here?
You could simply use the SemanticModel to analyse the type used for the field like this:
private async Task<bool> IsMentionedInDisposeCallAsync(SyntaxNodeAnalysisContext context, FieldDeclarationSyntax fieldDeclarationSyntax)
{
foreach (var variableDeclaratorSyntax in fieldDeclarationSyntax.Declaration.Variables)
{
var declaredSymbol = context.SemanticModel.GetDeclaredSymbol(variableDeclaratorSyntax);
if (declaredSymbol is IFieldSymbol fieldSymbol)
{
var isDisposeable = CheckIsTypeIDisposeable(fieldSymbol.Type as INamedTypeSymbol);
// SymbolFinder.FindReferencesAsync()
var b = fieldSymbol.Locations;
// context.SemanticModel.Compilation.
}
}
return false;
}
private string fullQualifiedAssemblyNameOfIDisposeable = typeof(IDisposable).AssemblyQualifiedName;
private bool CheckIsTypeIDisposeable(INamedTypeSymbol type)
{
// Identify the IDisposable class. You can use any method to do this here
// A type.ToDisplayString() == "System.IDisposable" might do it for you
if(fullQualifiedAssemblyNameOfIDisposeable ==
type.ToDisplayString() + ", " + type.ContainingAssembly.ToDisplayString())
{
return true;
}
if(type.BaseType != null)
{
if (CheckIsTypeIDisposeable(type.BaseType))
{
return true;
}
}
foreach(var #interface in type.AllInterfaces)
{
if (CheckIsTypeIDisposeable(#interface))
{
return true;
}
}
return false;
}
Basically you would search through all interfaces of the class and the base class recursively to find the type corresponding to IDisposeable - which should be somewhere in the hierarchy.
I created a little class in order to create logs.
This class use streamwriter functions in order to do this.
I write a log, and after closed the log, i would like to re-open after, in order to append some datas.
I tried severals tips, and...i always have an exception who tell me " The file is used by another process".
Nevertheless, i use the close(), but, i always had this exception.
Here is my class :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace TEST
{
public class CLog
{
private StreamWriter myWriter;
public string Name
{
set { Name = value; }
get
{
FileInfo myFile = new FileInfo(CompleteFileName);
return myFile.Name;
}
}
public string Directory
{
set { Directory = value; }
get
{
FileInfo myFile = new FileInfo(CompleteFileName);
return myFile.Directory.ToString();
}
}
public string CompleteFileName { set; get; }
public CLog(string _CompleteFileName)
{
CompleteFileName = _CompleteFileName;
}
public bool CreateLog()
{
try
{
myWriter = new StreamWriter(CompleteFileName);
return true;
}
catch (Exception ex )
{
return false;
}
}
public bool AppendTextToFile(string strText)
{
try
{
using (System.IO.StreamWriter sw = System.IO.File.AppendText(CompleteFileName))
{
sw.WriteLine(strText);
}
return true;
}
catch (Exception ex)
{
return false;
}
}
public bool WriteLine(string strLine)
{
try
{
myWriter.WriteLine(strLine);
return true;
}
catch (Exception ex)
{
return false;
}
}
public bool SaveFile()
{
try
{
myWriter.Close();
myWriter.Dispose();
return true;
}
catch (Exception ex)
{
return false;
}
}
}
}
Please look at the AppendTextToFile.
Here is the use :
monLog = new CLog("C:\\TEST.TXT");
if (monLog.CreateLog())
{
monLog.WriteLine("");
monLog.WriteLine("******");
monLog.WriteLine("Some data");
monLog.WriteLine("******");
monLog.SaveFile();
...
....
monLog.AppendTextToFile("** my AppendedData***);
monLog.SaveFile();
}
Anyone know why i have this exception and how solve it ?
Thanks a lot :)
Update: with help from Henk, determined that public Dispose() is being called, which in turn calls private Dispose(true). This is my first implementation of IDisposable interface, so not sure if it is correct. I don't call Dispose explicitly anywhere. It seems that WCF architecture is calling it on exit from each OperationContract member.
Took unmanaged cleanup code out of Dispose for now, and multiple calls are able to access the static data. It seems that Dispose() is called on all locally allocated objects on return from calls, even if there is a reference to object in static storage. Not sure in .net world how to get around this so that IDisposable interface will get callled correctly. I'm guessing that these objects will get garbage collected at some point also.
Here is call stack on return from 1st call when Dispose is being called:
BossISeriesCwbxService.dll!BossISeriesCwbxServices.DataContracts.ISeriesSystem.Dispose(bool
bDisposing = true) Line 119 C#
BossISeriesCwbxService.dll!BossISeriesCwbxServices.DataContracts.ISeriesSystem.Dispose()
Line 107 + 0xd bytes C#
System.ServiceModel.dll!System.ServiceModel.Dispatcher.MessageRpc.DisposeParametersCore()
+ 0x56 bytes System.ServiceModel.dll!System.ServiceModel.Dispatcher.MessageRpc.DisposeParameters()
+ 0xf bytes System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessageCleanup(ref
System.ServiceModel.Dispatcher.MessageRpc
rpc =
{System.ServiceModel.Dispatcher.MessageRpc})
+ 0x135 bytes System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(ref
System.ServiceModel.Dispatcher.MessageRpc
rpc =
{System.ServiceModel.Dispatcher.MessageRpc})
+ 0x1bf bytes System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(ref
System.ServiceModel.Dispatcher.MessageRpc
rpc) + 0x80 bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(ref
System.ServiceModel.Dispatcher.MessageRpc
rpc) + 0x36 bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(ref
System.ServiceModel.Dispatcher.MessageRpc
rpc) + 0x43 bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(ref
System.ServiceModel.Dispatcher.MessageRpc
rpc) + 0xd7 bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.MessageRpc.Process(bool
isOperationContextSet = false) + 0x9b
bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.Dispatch(ref
System.ServiceModel.Dispatcher.MessageRpc
rpc, bool isOperationContextSet) +
0x2d bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(System.ServiceModel.Channels.RequestContext
request =
{System.ServiceModel.Security.SecuritySessionServerSettings.SecuritySessionRequestContext},
bool cleanThread,
System.ServiceModel.OperationContext
currentOperationContext) + 0x20c
bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(System.ServiceModel.Channels.RequestContext
request,
System.ServiceModel.OperationContext
currentOperationContext) + 0xdf bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(System.IAsyncResult
result) + 0x43 bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.OnContinueAsyncReceive(object
state) + 0x45 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.Invoke2()
+ 0x46 bytes System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.OnSecurityContextCallback(object
o) + 0x28 bytes
mscorlib.dll!System.Security.SecurityContext.Run(System.Security.SecurityContext
securityContext,
System.Threading.ContextCallback
callback, object state) + 0x55 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.Invoke()
+ 0x4d bytes
System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.ProcessCallbacks()
+ 0x180 bytes System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.CompletionCallback(object
state) + 0x7a bytes
System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.ScheduledOverlapped.IOCallback(uint
errorCode, uint numBytes,
System.Threading.NativeOverlapped*
nativeOverlapped) + 0xf bytes
SMDiagnostics.dll!System.ServiceModel.Diagnostics.Utility.IOCompletionThunk.UnhandledExceptionFrame(uint
error, uint bytesRead,
System.Threading.NativeOverlapped*
nativeOverlapped) + 0x3d bytes
mscorlib.dll!System.Threading._IOCompletionCallback.PerformIOCompletionCallback(uint
errorCode, uint numBytes,
System.Threading.NativeOverlapped*
pOVERLAP) + 0x54 bytes
I read some posts on caching static data in a WCF service implementation class, and was having a problem with the GC calling dispose on the objects in a static Dictionary. I am referencing some activex objects from IBM iSeries Access, so I implemented IDisposable interface to clean up connection to iSeries. My problem is the GC is Disposing of objects in the Static members of the Service class. Not sure all the code was required, but here it is anyway. The problem is that on return from each OperationContract method, the GC is calling Dispose on the ISeriesSystem or Queue object that was added to the associated Dictionary, but the ISeriesSystem Dictionary is static, so I thought that it held a reference to the object, so GC wouldn't be done until it is removed from Dictionary.
Service Interface:
[ServiceContract(Namespace="BossISeriesCwbxServices")]
public interface IDataQueueService
{
[OperationContract]
ISeriesSystem SystemInitialize(string sISeriesName);
[OperationContract(Name="FinalizeSystemByName")]
void SystemFinalize(string sISeriesName);
[OperationContract]
void SystemFinalize(ISeriesSystem oISeriesSystem);
[OperationContract]
Queue QueueInitialize(string sQueueName, string sLibrary, string sISeriesName);
[OperationContract(Name="FinalizeQueueByName")]
void QueueFinalize(string sQueueName, string sLibrary, string sISeriesName);
[OperationContract]
void QueueFinalize(Queue oDataQueue);
[OperationContract (Name="QueueWriteByName")]
void QueueWrite(string sQueueName, string sLibrary, string sISeriesName, string sMessage);
[OperationContract]
void QueueWrite(Queue oDataQueue, string sMessage);
[OperationContract (Name="QueueReadByName")]
string QueueRead(string sQueueName, string sLibrary, string sISeriesName);
[OperationContract]
string QueueRead(Queue oDataQueue);
}
Service Implementation:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single)]
public class DataQueueService : IDataQueueService
{
private static Dictionary<string, ISeriesSystem> mdictISeriesSystems = new Dictionary<string, ISeriesSystem>();
public static IDictionary<string, ISeriesSystem> ISeriesDict
{
get { return mdictISeriesSystems; }
}
public ISeriesSystem SystemInitialize(string sISeriesName)
{
ISeriesSystem oISeriesSystem = AddSystem(sISeriesName);
return oISeriesSystem;
}
public void SystemFinalize(string sISeriesName)
{
}
public void SystemFinalize(ISeriesSystem oISeriesSystem)
{
SystemFinalize(oISeriesSystem.Name);
}
public Queue QueueInitialize(string sQueueName, string sLibrary, string sISeriesName)
{
ISeriesSystem oISeriesSystem = null;
Queue oDataQueue = null;
try
{
oISeriesSystem = AddSystem(sISeriesName);
oDataQueue = oISeriesSystem.AddQueue(sQueueName, sLibrary);
}
catch (Exception ex)
{
// ToDo: Log ex to WCF service log and remove from Console.
Console.WriteLine(ex.ToString());
oDataQueue = null;
}
return oDataQueue;
}
public Queue QueueInitialize(string sQueueName, string sLibrary, ISeriesSystem oISeriesSystem)
{
return QueueInitialize(sQueueName, sLibrary, oISeriesSystem.Name);
}
public void QueueFinalize(string sQueueName, string sLibrary, string sISeriesName)
{
string sISeriesKey = sISeriesName.Trim();
string sDataQueueKey = sLibrary.Trim() + sQueueName.Trim();
ISeriesSystem oISeriesSystem = null;
Queue oDataQueue = null;
if (DataQueueService.ISeriesDict.TryGetValue(sISeriesKey, out oISeriesSystem))
{
if (oISeriesSystem.DataQueueDict.TryGetValue(sDataQueueKey, out oDataQueue))
{
oDataQueue.Dispose();
oDataQueue = null;
oISeriesSystem.DataQueueDict.Remove(sDataQueueKey);
}
if (oISeriesSystem.DataQueueDict.Count == 0)
{
oISeriesSystem.Dispose();
oISeriesSystem = null;
}
}
}
public void QueueFinalize(Queue oDataQueue)
{
QueueFinalize(oDataQueue.Name, oDataQueue.Library, oDataQueue.ISeriesName);
}
public void QueueWrite(string sQueueName, string sLibrary, string sISeriesName, string sMessage)
{
string sISeriesKey = sISeriesName.Trim();
string sDataQueueKey = sLibrary.Trim() + sQueueName.Trim();
ISeriesSystem oISeriesSystem = null;
Queue oDataQueue = null;
if (DataQueueService.ISeriesDict.TryGetValue(sISeriesKey, out oISeriesSystem))
{
if (oISeriesSystem.DataQueueDict.TryGetValue(sDataQueueKey, out oDataQueue))
{
oDataQueue.Write(sMessage);
}
}
}
public void QueueWrite(Queue oDataQueue, string sMessage)
{
QueueWrite(oDataQueue.Name, oDataQueue.Library, oDataQueue.ISeriesName, sMessage);
}
public string QueueRead(string sQueueName, string sLibrary, string sISeriesName)
{
string sISeriesKey = sISeriesName.Trim();
string sDataQueueKey = sLibrary.Trim() + sQueueName.Trim();
ISeriesSystem oISeriesSystem = null;
Queue oDataQueue = null;
if (DataQueueService.ISeriesDict.TryGetValue(sISeriesKey, out oISeriesSystem))
{
if (oISeriesSystem.DataQueueDict.TryGetValue(sDataQueueKey, out oDataQueue))
{
return oDataQueue.Read();
}
}
return "";
}
public string QueueRead(Queue oDataQueue)
{
return QueueRead(oDataQueue.Name, oDataQueue.Library, oDataQueue.ISeriesName);
}
ISeriesSystem AddSystem(string sISeriesName)
{
ISeriesSystem oISeriesSystem = null;
string sISeriesKey = sISeriesName.Trim();
if (!DataQueueService.ISeriesDict.TryGetValue(sISeriesKey, out oISeriesSystem))
{
oISeriesSystem = new ISeriesSystem(sISeriesName);
DataQueueService.ISeriesDict[sISeriesKey] = oISeriesSystem;
}
return oISeriesSystem;
}
ISeriesSystem DataContract:
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.Runtime.Serialization;
using cwbx;
namespace BossISeriesCwbxServices.DataContracts
{
public class ISeriesSystem : IDisposable
{
private string msName;
[DataMember]
public string Name
{
get { return msName; }
set { msName = value; }
}
private Dictionary<string, Queue> mdictDataQueues = new Dictionary<string, Queue>();
public IDictionary<string, Queue> DataQueueDict
{
get { return mdictDataQueues; }
}
private cwbx.AS400System mcwbxISeriesSystem = new AS400System();
private cwbx.AS400System CwbxISeriesSystem
{
get { return mcwbxISeriesSystem; }
set { mcwbxISeriesSystem = value; }
}
private bool bDisposed = false;
public ISeriesSystem()
{
}
public ISeriesSystem(string sISeriesName)
{
try
{
// Set DataContract properties.
this.Name = sISeriesName;
// Connect to iSeries, Logon and connect to iSeries services that may be used.
this.CwbxISeriesSystem.Define(sISeriesName);
this.CwbxISeriesSystem.UserID = "APP1DAK";
this.CwbxISeriesSystem.Password = "DONNA99";
this.CwbxISeriesSystem.Signon();
this.CwbxISeriesSystem.Connect(cwbcoServiceEnum.cwbcoServiceDataQueues);
this.CwbxISeriesSystem.Connect(cwbcoServiceEnum.cwbcoServiceSecurity);
this.CwbxISeriesSystem.Connect(cwbcoServiceEnum.cwbcoServiceRemoteCmd);
}
catch (Exception ex)
{
// ToDo: Log ex to WCF service log and remove from Console.
Console.WriteLine(ex.ToString());
foreach (cwbx.Error cwbxError in this.CwbxISeriesSystem.Errors)
{
Console.WriteLine(cwbxError.Text);
Console.WriteLine(cwbxError.ToString());
}
}
}
~ISeriesSystem()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool bDisposing)
{
// Only Dispose of the object 1 time.
if (!this.bDisposed)
{
// If disposing equals true, Dispose() was called by GC, so dispose all managed resources.
if (bDisposing)
{
// Dispose managed resources, calling object Dispose method for objects
// that implement IDisposable interface.
}
try
{
if (this.CwbxISeriesSystem.IsConnected(cwbcoServiceEnum.cwbcoServiceAny) == 1)
{
this.CwbxISeriesSystem.Disconnect(cwbcoServiceEnum.cwbcoServiceAll);
}
}
catch (Exception ex)
{
// ToDo: Log ex to WCF service log and remove from Console.
Console.WriteLine(ex.ToString());
foreach (cwbx.Error cwbxError in this.CwbxISeriesSystem.Errors)
{
Console.WriteLine(cwbxError.Text);
Console.WriteLine(cwbxError.ToString());
}
}
// Mark disposing as being done.
bDisposed = true;
}
}
public Queue AddQueue(string sQueueName, string sLibrary)
{
Queue oDataQueue = null;
string sDataQueueKey = sLibrary.Trim() + sQueueName.Trim();
if (!this.DataQueueDict.TryGetValue(sDataQueueKey, out oDataQueue))
{
oDataQueue = new Queue(sQueueName, sLibrary, this.CwbxISeriesSystem);
this.DataQueueDict[sDataQueueKey] = oDataQueue;
}
return oDataQueue;
}
}
}
Queue DataContract:
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.Runtime.Serialization;
using cwbx;
namespace BossISeriesCwbxServices.DataContracts
{
[DataContract]
public class Queue : IDisposable
{
private string msName;
[DataMember]
public string Name
{
get { return msName; }
set { msName = value; }
}
private string msLibrary;
[DataMember]
public string Library
{
get { return msLibrary; }
set { msLibrary = value; }
}
private string msISeriesName;
[DataMember]
public string ISeriesName
{
get { return msISeriesName; }
set { msISeriesName = value; }
}
private short miWaitTime = 10;
[DataMember]
public short WaitTime
{
get { return miWaitTime; }
set { miWaitTime = value; }
}
private short miNumberOfAttempts = 1;
[DataMember]
public short NumberOfAttempts
{
get { return miNumberOfAttempts; }
set { miNumberOfAttempts = value; }
}
private short miMaxQueueIndex = 1;
public short MaxQueueIndex
{
get { return miMaxQueueIndex; }
set { miMaxQueueIndex = value; }
}
private short miCurrentQueueIndex = 1;
public short CurrentQueueIndex
{
get { return miCurrentQueueIndex; }
set { miCurrentQueueIndex = value; }
}
private cwbx.DataQueue mcwbxDataQueue = new cwbx.DataQueue();
private cwbx.DataQueue CwbxDataQueue
{
get { return mcwbxDataQueue; }
set { mcwbxDataQueue = value; }
}
private bool bDisposed = false;
public Queue()
{
}
public Queue(string sQueueName, string sLibrary, cwbx.AS400System cwbxISeriesSystem)
{
this.Name = sQueueName;
this.Library = sLibrary;
this.ISeriesName = cwbxISeriesSystem.SystemName;
this.CwbxDataQueue.QueueName = sQueueName;
this.CwbxDataQueue.LibraryName = sLibrary;
this.CwbxDataQueue.system = cwbxISeriesSystem;
}
~Queue()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool bDisposing)
{
// Only Dispose of the object 1 time.
if (!this.bDisposed)
{
// If disposing equals true, Dispose() was called by GC, so dispose all managed resources.
if (bDisposing)
{
// Dispose managed resources, calling object Dispose method for objects
// that implement IDisposable interface.
}
// Call the appropriate methods to clean up unmanaged resources here.
try
{
this.CwbxDataQueue = null;
}
catch (Exception ex)
{
// ToDo: Log ex to WCF service log and remove from Console.
Console.WriteLine(ex.ToString());
foreach (cwbx.Error cwbxError in this.CwbxDataQueue.Errors)
{
Console.WriteLine(cwbxError.Text);
Console.WriteLine(cwbxError.ToString());
}
}
// Mark disposing as being done.
bDisposed = true;
}
}
public void Write(string sMessage)
{
try
{
cwbx.StringConverter cwbxStringConverter = new cwbx.StringConverter();
Object oBytes = cwbxStringConverter.ToBytes(sMessage);
this.CwbxDataQueue.Write(oBytes, false);
}
catch (Exception ex)
{
// ToDo: Log ex to WCF service log and remove from Console.
Console.WriteLine(ex.ToString());
foreach (cwbx.Error cwbxError in this.CwbxDataQueue.Errors)
{
Console.WriteLine(cwbxError.Text);
Console.WriteLine(cwbxError.ToString());
}
}
}
public string Read()
{
try
{
Object oObject = null;
return (new cwbx.StringConverter()).FromBytes(this.CwbxDataQueue.Read(this.WaitTime * this.NumberOfAttempts, out oObject));
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
foreach (cwbx.Error cwbxError in this.CwbxDataQueue.Errors)
{
Console.WriteLine(cwbxError.Text);
Console.WriteLine(cwbxError.ToString());
}
return "";
}
}
}
}
Client Code:
ISeriesSystem oISeriesSystem = null;
Queue oDataQueue = null;
oISeriesSystem = DQService.SystemInitialize("A2029D2.AS400.US.UPS.COM");
oDataQueue = DQService.QueueInitialize("SMTLST020", "IB5EXE", oISeriesSystem.Name);
oISeriesSystem.DataQueueDict.Add(oDataQueue.Library + oDataQueue.Name, oDataQueue);
ISeriesSystemDict.Add(oISeriesSystem.Name, oISeriesSystem);
DQService.QueueWrite(oDataQueue, "Testing cwbx.DataQueue WCF service");
string sMessage = DQService.QueueRead(oDataQueue);
Exe Hosted Service:
Uri baseAddress = new Uri("http://localhost:8080/BossISeriesCwbxServices");
//Instantiate new ServiceHost
moServiceHost = new ServiceHost(typeof(BossISeriesCwbxServices.DataQueueService), baseAddress);
// Add Endpoint
moServiceHost.AddServiceEndpoint(typeof(BossISeriesCwbxServices.IDataQueueService), new WSHttpBinding(), "IDataQueueService");
// Enable metadata exchange.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
moServiceHost.Description.Behaviors.Add(smb);
//Open moServiceHost
moServiceHost.Open();
Console.WriteLine("The IDataQueueService is ready.");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.WriteLine();
Does it happen regularly or every now and then? Does it happen under development or only (after some time) on the production server?
When you host this under IIS, the server might decide to 'recycle' your app. Basic advice: don't use static in server apps. It's not reliable and not scalable.
Edit
OK, I've read a little more (but not all).
The problem is that on return from
each OperationContract method, the GC
is calling Dispose on the
ISeriesSystem or Queue object
You should verify that in great detail. Is it really the GC that calls your Finalizer(s) (aka destructors)? You should use logging or debugging to verify that the overload Dispose(false) is being called. If Dispose(true) is called (and I see a lot of code involved in doing that) you should stacktrace to the actual cause.
Can you put the following code :
[ServiceContract(SessionMode = SessionMode.Required)]
on your service contract just to make sure the transport session is being created? If it isn't this should throw an error.
WSHttpBinding does not create a transport session without security and reliable session.
EDIT :
Other than that, since you are creating a Single Instance service, why would you need static members? There is only going to be one instance alive, so, any instance members of that instance will live with it and will be kind of static. Have you thought about that or there is a specific reason for using static members?
I know it's a bit late but have you tried adding the [OperationBehavior(AutoDisposeParameters = false)] attribute?
Link to MSDN Forum