IIS Remote Exception - c#

I have an IIS Application which references a plugin manager which reads all available plugins from a folder, when it is hosted after about 5-10 minutes I start getting the following Exception
[RemotingException: Object '/1608465e_9d80_4b40_be20_4c96904643e0/wizi+0g5od5gwmunm_indiws_253.rem' has been disconnected or does not exist at the server.]
System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg) +14416170
System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type) +388
PluginManager.Core.Interfaces.IPluggin.get_Id() +0
PluginManager.PluginDetails.get_Id() +27
I did some research and came accross ILease and ISponsor but I have no Idea how to implement it or how it works. My current code as it is at this moment, I just removed parts the body of the methods for clarity
[Edit : added the method bodies]
public class AssemblyReflectionProxy : MarshalByRefObject
{
private string _assemblyPath;
public AssemblyReflectionProxy()
{
Id = "";
}
public void LoadAssembly(String assemblyPath)
{
try
{
_assemblyPath = assemblyPath;
Assembly.ReflectionOnlyLoadFrom(assemblyPath);
}
catch (FileNotFoundException)
{
}
}
public TResult Reflect<TResult>(Func<Assembly, TResult> func)
{
var directory = new FileInfo(_assemblyPath).Directory;
ResolveEventHandler resolveEventHandler = (s, e) => OnReflectionOnlyResolve(e, directory);
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += resolveEventHandler;
var assembly = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies().FirstOrDefault(a => String.Compare(a.Location, _assemblyPath, StringComparison.Ordinal) == 0);
var result = func(assembly);
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= resolveEventHandler;
return result;
}
public T GetEntryType<T>()
{
var directory = new FileInfo(_assemblyPath).Directory;
ResolveEventHandler resolveEventHandler = (s, e) => OnReflectionOnlyResolve(e, directory);
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += resolveEventHandler;
var assembly = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies().FirstOrDefault(a => string.Compare(a.Location, _assemblyPath, StringComparison.Ordinal) == 0);
if (assembly != null)
{
var result = assembly.GetTypes();
var type = result.FirstOrDefault(x => x.GetInterface(typeof(T).Name) != null);
if (type != null)
{
var remoteObject = AppDomain.CurrentDomain.CreateInstanceFrom(type.Assembly.Location, type.FullName);
var obj = remoteObject.Unwrap();
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= resolveEventHandler;
return (T)obj;
}
}
return default(T);
}
private Assembly OnReflectionOnlyResolve(ResolveEventArgs args, DirectoryInfo directory)
{
var loadedAssembly = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies().FirstOrDefault(asm => string.Equals(asm.FullName, args.Name, StringComparison.OrdinalIgnoreCase));
if (loadedAssembly != null)
{
return loadedAssembly;
}
var assemblyName = new AssemblyName(args.Name);
var dependentAssemblyFilename = Path.Combine(directory.FullName, assemblyName.Name + ".dll");
if (File.Exists(dependentAssemblyFilename))
{
return Assembly.ReflectionOnlyLoadFrom(dependentAssemblyFilename);
}
return Assembly.ReflectionOnlyLoad(args.Name);
}
private string Id { get; set; }
internal string GetId()
{
if (String.IsNullOrEmpty(Id))
{
var fileBytes = File.ReadAllBytes(_assemblyPath);
var hash = Convert.ToBase64String(fileBytes).GetHashCode();
var bytes = BitConverter.GetBytes(hash);
StringBuilder sb = new StringBuilder();
foreach (byte b in bytes)
sb.Append(b.ToString("X2"));
Id = sb.ToString();
}
return Id;
}
}
public sealed class AssemblyManager : MarshalByRefObject, IDisposable
{
private readonly Dictionary<string, AppDomain> _assemblyDomains = new Dictionary<string, AppDomain>();
readonly Dictionary<string, AssemblyReflectionProxy> _proxies = new Dictionary<string, AssemblyReflectionProxy>();
public AssemblyManager()
{
}
public string LoadAssembly(string assemblyPath)
{
var fileInfo = new FileInfo(assemblyPath);
var name = fileInfo.Name.Replace(".dll", "");
if (fileInfo.Exists)
{
if (!_assemblyDomains.ContainsKey(name))
{
var appDomain = CreateChildDomain(AppDomain.CurrentDomain, fileInfo.Name);
_assemblyDomains[name] = appDomain;
try
{
Type proxyType = typeof(AssemblyReflectionProxy);
{
var proxy = (AssemblyReflectionProxy)appDomain.CreateInstanceFrom(proxyType.Assembly.Location, proxyType.FullName).Unwrap();
proxy.LoadAssembly(assemblyPath);
_proxies[name] = proxy;
return name;
}
}
catch
{ }
}
else
{
return name;
}
}
return "";
}
public void Unload()
{
}
private AppDomain CreateChildDomain(AppDomain parentDomain, string domainName)
{
var evidence = new Evidence(parentDomain.Evidence);
var setup = parentDomain.SetupInformation;
return AppDomain.CreateDomain(domainName, evidence, setup);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~AssemblyManager()
{
Dispose(false);
}
public IPluggin GetEntryPluggin(string name)
{
IPluggin plugin = default(IPluggin);
if (_proxies.ContainsKey(name))
{
plugin = _proxies[name].GetEntryType<IPluggin>();
}
return plugin;
}
private void Dispose(bool disposing)
{
if (disposing)
{
foreach (var appDomain in _assemblyDomains.Values)
AppDomain.Unload(appDomain);
_assemblyDomains.Clear();
}
}
internal string GetEntryPlugginID(string name)
{
string Id = "";
if (_proxies.ContainsKey(name))
{
Id = _proxies[name].GetId();
}
return Id;
}
}
My Interface is
public interface IPluggin
{
string Name { get; }
string Version { get; }
string Id { get; }
void Initialize();
string[] GetElements();
void SaveSettings(string settings);
void SetBasePath(string path);
}

.NET remoting is kind of deprecated, but I don't see why it would be a problem for communicating between application domains, so...
When you create a new instance of a remoting object, what you actually do is request the remote side to create one for you, while you only maintain a proxy. The remote side associates each of such objects with a lease - something that describes how the lifetime of the object is handled. This means that even if the client side still has strong references to the remote object, the remote object can be garbage collected when its lease expires.
The easiest way to check if this happened is using the RemotingServices.GetLifetimeService method. Just use it on the proxy object, and you will get the information you need - for example, CurrentState will tell you if the remote is still alive. If it is, you can also extend the lease using Renew.
So, the usual way to handle the lifetime of the remote object would be - check if it is still alive; if it is, extend the lease and do whatever you want. Make sure you check CurrentLeaseTime too - it's a good idea to maintain it some reasonable value, rather than always Renewing a fixed amount of time.

Related

Long lived AssemblyLoadContext

So this is what I'm trying to do. I would like to have a log lived AssemblyLoadContext instance in order to quickly execute loaded assemblies.
When the assemblies updated event comes I would like to unload the context (I'm using .net core 3) and create a new context with versions of assemblies. When I try to do that however I get an error when unloading for the second time
so I'm guessing I'm doing something (very) wrong. I'm guessing the context doesn't get actually unloaded?
If the loaded assembly depends on some external dll I also get an error that that dll cannot be loaded, even though it is in the same folder. How can I prevent that?
Any advice would be very appreciated. This is my code
class Program
{
private static AssembliesContext assembliesContext;
const int iterations = 10;
static void Main(string[] args)
{
for (int i = 0; i < iterations; i++)
{
assembliesContext = new AssembliesContext();
RunViaReflection();
var wr = new WeakReference(assembliesContext);
assembliesContext.Unload();
assembliesContext = null;
int retries = 0;
while (wr.IsAlive && retries < 5)
{
GC.Collect();
GC.WaitForPendingFinalizers();
retries++;
}
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
static void RunViaReflection()
{
try
{
string dllPath = Path.Combine(Directory.GetCurrentDirectory(), "..", "..", "..", "..", "Execution.Sample", "bin", "Debug", "netcoreapp2.2", "win10-x64", "publish", "Execution.Sample.dll");
using (var fs = new FileStream(dllPath, FileMode.Open, FileAccess.Read))
{
var assembly = assembliesContext.LoadFromStream(fs);
var executableType = assembly.GetTypes()
.Where(x => typeof(IExecutableCode).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract)
.FirstOrDefault();
IExecutableCode instance = (IExecutableCode)Activator.CreateInstance(executableType);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
public class AssembliesContext : AssemblyLoadContext
{
public AssembliesContext(): base(true)
{
}
protected override Assembly Load(AssemblyName assemblyName)
{
return null;
}
}
Edit
I got rid of the error by casting the invoke result to Task (since it's an async method):
((Task)method.Invoke(instance, new object[] { })).ConfigureAwait(false).GetAwaiter().GetResult();
I also loaded the depending assemblies like this:
foreach (var ass in assembly.GetReferencedAssemblies())
{
string path = Path.Combine(basePath, $"{ass.Name}.dll");
using (var dependantAssemblyStream = new FileStream(path, FileMode.Open, FileAccess.Read))
{
assembliesContext.LoadFromStream(dependantAssemblyStream);
}
}
I am still wondering how to manage a long lived AssemblyLoadContext since if I try to unload it as a class member it wil not unload, even if I set it to null and instantiate a new one.
Is a different approach warranted?
Edit #2
Right now I have a ConcurrentDictionary of WeakReference items which seems so work fairly ok, but every WeakReference in it loses it's target when the GC runs. Logical, but I still don't know how to make everything work.
If I do something like this
namespace AssemblyLoadTest
{
class Program
{
static void Main(string[] args)
{
AssemblyExecutor executor = new AssemblyExecutor();
for (int i = 0; i < 1; i++)
{
var assemblyPath = Path.Combine(Directory.GetCurrentDirectory(), "..", "..", "..", "..", "Assembly", "Sinapro.IoT.Testing.DotNetRuleTestImplementation.dll");
executor.Execute(assemblyPath);
Console.WriteLine($"Iteration {i} completed");
if (i > 0 && i % 10 == 0)
{
executor.Clear();
}
}
executor.Clear();
while (true)
{
Thread.Sleep(100);
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
}
public class AssemblyExecutor
{
private readonly ConcurrentDictionary<string, CollectibleAssemblyLoadContext> references = new ConcurrentDictionary<string, CollectibleAssemblyLoadContext>();
[MethodImpl(MethodImplOptions.NoInlining)]
public void Clear()
{
var keys = references.Keys.ToList();
foreach (var k in keys)
{
references.TryRemove(k, out CollectibleAssemblyLoadContext ctx);
ctx.Unload();
}
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("GC called");
}
public void Execute(string assemblyPath)
{
var wr = references.AddOrUpdate(
assemblyPath,
(k) =>
{
return CreateReference(assemblyPath);
},
(k, existingReference) =>
{
if (existingReference == null || !existingReference.Assemblies.Any())
{
return CreateReference(assemblyPath);
}
return existingReference;
}
);
ExecuteAssembly(wr);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void ExecuteAssembly(CollectibleAssemblyLoadContext context)
{
//CollectibleAssemblyLoadContext context = wr.Target as CollectibleAssemblyLoadContext;
if (context == null)
return;
var assembly = context.Assemblies.FirstOrDefault(a => a.ExportedTypes.Any(t => t.Name == "TestEntry"));
var types = assembly.GetTypes().ToList();
var type = assembly.GetType("Sinapro.IoT.Testing.DotNetRuleTestImplementation.TestEntry");
var greetMethod = type.GetMethod("Execute");
var instance = Activator.CreateInstance(type);
var result = greetMethod.Invoke(instance, new object[] { null, null });
}
[MethodImpl(MethodImplOptions.NoInlining)]
private CollectibleAssemblyLoadContext CreateReference(string assemblyPath)
{
Console.WriteLine($"Creating new context");
var context = new CollectibleAssemblyLoadContext(assemblyPath);
using (var fs = new FileStream(assemblyPath, FileMode.Open, FileAccess.Read))
{
var assembly = context.LoadFromStream(fs);
}
return context;
}
}
public class CollectibleAssemblyLoadContext : AssemblyLoadContext
{
private AssemblyDependencyResolver _resolver;
public CollectibleAssemblyLoadContext(string path)
: base(isCollectible: true)
{
_resolver = new AssemblyDependencyResolver(path);
}
protected override Assembly Load(AssemblyName assemblyName)
{
var deps = AssemblyLoadContext.Default;
var res = deps.Assemblies.Where(d => d.FullName.Contains(assemblyName.Name)).ToList();
if (res.Any())
{
return Assembly.Load(new AssemblyName(res.First().FullName));
}
string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
if (assemblyPath == null)
return null;
return LoadFromAssemblyPath(assemblyPath);
}
}
}
everything runs fine, but the memory consumption keeps increasing. I find it curious though, since there shouldn't be any reference to the AssemblyLoadContext anymore so the GC should've cleared it?

Using SharpSvn to retrieve log revisions got Disk issue

I'm workin on a wcf service to get some info of svn log.
My service method:
public List<SvnLogInfo> ViewLog(Executable ejecutable) {
Configuration config = m_context.Configuration.SingleOrDefault();
if (config != null) {
SvnClient svnClient = new SvnClient();
SvnRevisionRange svnRevisionRange = new SvnRevisionRange(ejecutable.SvnRevisionFrom, ejecutable.SvnRevisionTo);
SvnLogArgs args = new SvnLogArgs(svnRevisionRange);
Collection<SvnLogEventArgs> logCollection;
svnClient.GetLog(config.RepositoryPath, args, out logCollection);
List<SvnLogInfo> logInfo = new List<SvnLogInfo>();
foreach (SvnLogEventArgs log in logCollection) {
logInfo.Add((SvnLogInfo)log);
}
return logInfo;
}
return null;
}
[Serializable]
public class SvnLogInfo {
public SvnLogInfo() {
}
private string m_message;
public string Mensaje {
get { return m_message; }
set { m_message = value; }
}
private string m_author;
public string Autor {
get { return m_author; }
set { m_author = value; }
}
public static explicit operator SvnLogInfo(SvnLogEventArgs e) {
SvnLogInfo info = new SvnLogInfo();
info.Mensaje = e.LogMessage;
info.Autor = e.Author;
return info;
}
}
It works, but when excetuing this line:
svnClient.GetLog(config.RepositoryPath, args, out logCollection);
Throws me this error message:
There is no disk in drive G.
As I mention, I'm using SharpSvn library. Is there a way to solve this issues?. By the way, the variable config.RepositoryPath has this value "C:\Users\carlos.vega.CONTAPERU\Desktop\Solucion ContaNet v3 Espero Funcione"

Dynamically loading assemblies and instantiating types using reflection

I have a requirement to dynamically load and cast an instance of a class to it's various base types using requirement. Now on reading and trying my hand on some examples, I find that either I probably don't understand all that I need to when it comes to working with classes at runtime.
I have a situation where Abstract class B implements interface A. Now Class B is a base class for class C. When I dynamically load, at runtime, the assembly that contains all 3 types, I expect that I should be able to, using Load From context, load the assembly, create an instance of class C, and cast it to type of interface A. But that does not seem to be happening at all and I would appreciate an explanation as to why. Thanks in Advance.
http://msdn.microsoft.com/en-us/library/2xkww633.aspx
http://msdn.microsoft.com/en-us/library/1009fa28.aspx
public interface ICaseOutputGenerator
{
String SampleProperty { get; set; }
void Process();
}
public abstract class CaseOutputGeneratorBase : ICaseOutputGenerator
{
public String SecondSampleProperty { get; set; }
public virtual void SecondProcessMethod()
{
}
public abstract void ThirdSampleProcessMethod();
public string SampleProperty
{
get;
set;
}
public void Process()
{
Console.WriteLine("Process in CaseOutputGeneratorBase Called");
}
}
public class TestCaseOutputGenerator : CaseOutputGeneratorBase
{
public override void ThirdSampleProcessMethod()
{
throw new NotImplementedException();
}
}
//////////////////////////////////////////////////////////////////////////////
public class TestSandBoxManager
{
public TestSandBoxManager()
{
}
public String ProcessAssemblyByFullDisplayName(String assemblyFullDisplayName)
{
String temp = String.Empty;
var casecust = GetAssemblyByFullDisplayName(assemblyFullDisplayName);
if (casecust != null)
temp = ("Cast Passed");
else
temp = ("Cast Failed");
return temp;
}
public String ProcessFile(String assemblyName, String className)
{
String temp = String.Empty;
var casecust = GetCaseOutputGeneratorObject(assemblyName, className);
if (casecust != null)
temp=("Cast Passed");
else
temp=("Cast Failed");
return temp;
}
private static object GetAssemblyByFullDisplayName(string fullName)
{
try
{
Type caseOutputGen = null;
String fullQualifiedName = String.Empty;
var localAssembly = Assembly.LoadFrom(fullName);
foreach (var testType in localAssembly.GetTypes())
{
if ( testType.FullName != fullName)
continue;
fullQualifiedName = testType.FullName;
break;
}
if (fullQualifiedName == null)
return null;
var obj = Activator.CreateInstance(Type.GetType(fullQualifiedName));
return obj;
}
catch (Exception ex)
{
throw ex;
}
}
public String ProcessFile2(String assemblyName, String className)
{
String temp = String.Empty;
var casecust = GetCaseOutputGeneratorObjectLoadFrom(assemblyName, className);
if (casecust != null)
temp = ("Cast Passed");
else
temp = ("Cast Failed");
return temp;
}
public static ICaseOutputGenerator GetCaseOutputGeneratorObject(string assemblyName, string className)
{
ICaseOutputGenerator caseOutputGen = null;
var obj = GetObject(assemblyName, className);
if (obj != null)
caseOutputGen = (ICaseOutputGenerator)obj; // FAILS HERE
return caseOutputGen;
}
public static ICaseOutputGenerator GetCaseOutputGeneratorObjectLoadFrom(string assemblyName, string className)
{
ICaseOutputGenerator caseOutputGen = null;
try
{
var obj = GetObject2(assemblyName, className);
if (obj != null)
caseOutputGen = (ICaseOutputGenerator)obj; // FAILS HERE
}
catch (Exception ex)
{
throw ex;
}
return caseOutputGen;
}
private static object GetObject2(string fullName, string className)
{
try
{
Type caseOutputGen = null;
String fullQualifiedName = String.Empty;
var localAssembly = Assembly.LoadFrom(fullName);
foreach (var testType in localAssembly.GetTypes())
{
if (!testType.FullName.EndsWith(className, StringComparison.InvariantCultureIgnoreCase))
continue;
caseOutputGen = testType;
fullQualifiedName = testType.FullName;
break;
}
if (caseOutputGen == null)
return null;
var obj = Activator.CreateInstanceFrom(fullName, fullQualifiedName);
return obj.Unwrap();
}
catch (Exception ex)
{
throw ex;
}
}
private static object GetObject(string fullName, string className)
{
try
{
Type caseOutputGen = null;
var localAssembly = Assembly.LoadFrom(fullName);
foreach (var testType in localAssembly.GetTypes())
{
if (!testType.FullName.EndsWith(className, StringComparison.InvariantCultureIgnoreCase)) continue;
caseOutputGen = testType;
break;
}
if (caseOutputGen == null) return null;
var obj = Activator.CreateInstance(caseOutputGen);
return obj;
}
catch (FileNotFoundException ex)
{
throw new Exception("Failed to load assembly: " + Environment.NewLine + fullName, ex);
}
catch (Exception ex)
{
throw new Exception("Failed to load assembly: " + Environment.NewLine + fullName, ex);
}
}
}
//////////////////////////////////////////////////////////////////////////////
public Boolean testReflection2()
{
try
{
//create an instance of the testsandboxmanager
TestSandBoxManager t = new TestSandBoxManager();
String ret = t.ProcessFile2(#"...\Documents\visual studio 2012\Projects\TestSandBox\TestSandBox\bin\Debug\TestSandBox.dll", "TestCaseOutputGenerator");
Console.WriteLine(ret);
Console.ReadLine();
return true;
}
catch (Exception)
{
return false;
}
}
Most likely you have 2 ICaseOutputGenerator - one in each assembly. You can't cast object/interface to similarly named interface in another assembly even if code is identical. You can check the fact that create object implements ICaseOutputGenerator from its own assembly by looking at created object in the debugger.
If it is the case you need to figure out where you want to put ICaseOutputGenerator interface so it is coming from the same assembly for both "custom loaded assembly" and you main application. Often shared interfaces are implemented in separate assembly and linked to all "plug-in" assemblies as well as application assembly.
I think that Alexei Levenkov is spot on. You load your TestSandBox.dll twice. Once as a reference to your project and second time via Assembly.LoadFrom. As per documentation that you yourself linking this can result in unexpected behaviour. Here is a quote for you reference:
If an assembly is loaded with LoadFrom, and the probing path includes
an assembly with the same identity but a different location, an
InvalidCastException, MissingMethodException, or other unexpected
behavior can occur.
This is exactly what's happening in your case. If you change the path you are loading your assembly from to point to the same folder as your main executable, it will work fine.

Getting a System.Error when attempting to Sync using batching in Sync Framework

I am writing an n-tiered application that uses the Sync Framework v2.1 in combination with a WCF web service. My code is based on this example: Database-Sync
Syncing seems to work without a problem. But, when I add the batching functionality I get an error that is proving difficult to debug.
In the client portion of the solution (a WPF app), I have a virtual class that inherits from KnowledgeSyncProvider. It also implements the IDisposable interface. I use this class to call the WCF service I wrote and use the sync functionality there. When the sync code runs (orchestrator.Synchronize() is called), everything seems to work correctly and the EndSession function override that I have written runs without error. But, after execution leaves that function, a System.Error occurs from within Microsoft.Synchronization.CoreInterop.ISyncSession.Start (as far as I can tell).
The functions I have written to provide the IDisposable functionality are never called so, something is happening after EndSession, but before my application's KnowledgeSyncProvider virtual class can be disposed.
Here is the error information:
_message: "System error."
Stack Trace: at Microsoft.Synchronization.CoreInterop.ISyncSession.Start(CONFLICT_RESOLUTION_POLICY resolutionPolicy, _SYNC_SESSION_STATISTICS& pSyncSessionStatistics)
at Microsoft.Synchronization.KnowledgeSyncOrchestrator.DoOneWaySyncHelper(SyncIdFormatGroup sourceIdFormats, SyncIdFormatGroup destinationIdFormats, KnowledgeSyncProviderConfiguration destinationConfiguration, SyncCallbacks DestinationCallbacks, ISyncProvider sourceProxy, ISyncProvider destinationProxy, ChangeDataAdapter callbackChangeDataAdapter, SyncDataConverter conflictDataConverter, Int32& changesApplied, Int32& changesFailed)
at Microsoft.Synchronization.KnowledgeSyncOrchestrator.DoOneWayKnowledgeSync(SyncDataConverter sourceConverter, SyncDataConverter destinationConverter, SyncProvider sourceProvider, SyncProvider destinationProvider, Int32& changesApplied, Int32& changesFailed)
at Microsoft.Synchronization.KnowledgeSyncOrchestrator.Synchronize()
at Microsoft.Synchronization.SyncOrchestrator.Synchronize()
at MyApplication.Library.SynchronizationHelper.SynchronizeProviders() in C:\My Documents\Visual Studio 2010\Projects\MyApplication\MyApplication\Library\SynchronizationHelper.cs:line 43
at MyApplication.ViewModels.MainViewModel.SynchronizeDatabases() in C:\My Documents\Visual Studio 2010\Projects\MyApplication\MyApplication\ViewModels\MainViewModel.cs:line 46
I have enabled trace info but, that doesn't seem to provide any helpful information in this case.
Can anyone make a suggestion as to how I might be able to figure out what is causing this error?
Thanks in advance for any help you can provide.
Here is the code that handles syncing:
public void SynchronizeProviders()
{
CeDatabase localDb = new CeDatabase();
localDb.Location = Directory.GetCurrentDirectory() + "\Data\SYNCTESTING5.sdf";
RelationalSyncProvider localProvider = ConfigureCeSyncProvider(localDb.Connection);
MyAppKnowledgeSyncProvider srcProvider = new MyAppKnowledgeSyncProvider();
localProvider.MemoryDataCacheSize = 5000;
localProvider.BatchingDirectory = "c:\batchingdir";
srcProvider.BatchingDirectory = "c:\batchingdir";
SyncOrchestrator orchestrator = new SyncOrchestrator();
orchestrator.LocalProvider = localProvider;
orchestrator.RemoteProvider = srcProvider;
orchestrator.Direction = SyncDirectionOrder.UploadAndDownload;
CheckIfProviderNeedsSchema(localProvider as SqlCeSyncProvider, srcProvider);
SyncOperationStatistics stats = orchestrator.Synchronize();
}
And here is the KnowledgeSyncProviderClass that is used to create srcProvider:
class MyAppKnowledgeSyncProvider : KnowledgeSyncProvider, IDisposable
{
protected MyAppSqlSync.IMyAppSqlSync proxy;
protected SyncIdFormatGroup idFormatGroup;
protected DirectoryInfo localBatchingDirectory;
protected bool disposed = false;
private string batchingDirectory = Environment.ExpandEnvironmentVariables("%TEMP%");
public string BatchingDirectory
{
get { return batchingDirectory; }
set
{
if (string.IsNullOrEmpty(value))
{
throw new ArgumentException("value cannot be null or empty");
}
try
{
Uri uri = new Uri(value);
if (!uri.IsFile || uri.IsUnc)
{
throw new ArgumentException("value must be a local directory");
}
batchingDirectory = value;
}
catch (Exception e)
{
throw new ArgumentException("Invalid batching directory.", e);
}
}
}
public MyAppKnowledgeSyncProvider()
{
this.proxy = new MyAppSqlSync.MyAppSqlSyncClient();
this.proxy.Initialize();
}
public override void BeginSession(SyncProviderPosition position, SyncSessionContext syncSessionContext)
{
this.proxy.BeginSession(position);
}
public DbSyncScopeDescription GetScopeDescription()
{
return this.proxy.GetScopeDescription();
}
public override void EndSession(SyncSessionContext syncSessionContext)
{
this.proxy.EndSession();
if (this.localBatchingDirectory != null)
{
this.localBatchingDirectory.Refresh();
if (this.localBatchingDirectory.Exists)
{
this.localBatchingDirectory.Delete(true);
}
}
}
public override ChangeBatch GetChangeBatch(uint batchSize, SyncKnowledge destinationKnowledge, out object changeDataRetriever)
{
MyAppSqlSync.GetChangesParameters changesWrapper = proxy.GetChanges(batchSize, destinationKnowledge);
changeDataRetriever = changesWrapper.DataRetriever;
DbSyncContext context = changeDataRetriever as DbSyncContext;
if (context != null && context.IsDataBatched)
{
if (this.localBatchingDirectory == null)
{
string remotePeerId = context.MadeWithKnowledge.ReplicaId.ToString();
string sessionDir = Path.Combine(this.batchingDirectory, "MyAppSync_" + remotePeerId);
this.localBatchingDirectory = new DirectoryInfo(sessionDir);
if (!this.localBatchingDirectory.Exists)
{
this.localBatchingDirectory.Create();
}
}
string localFileName = Path.Combine(this.localBatchingDirectory.FullName, context.BatchFileName);
FileInfo localFileInfo = new FileInfo(localFileName);
if (!localFileInfo.Exists)
{
byte[] remoteFileContents = this.proxy.DownloadBatchFile(context.BatchFileName);
using (FileStream localFileStream = new FileStream(localFileName, FileMode.Create, FileAccess.Write))
{
localFileStream.Write(remoteFileContents, 0, remoteFileContents.Length);
}
}
context.BatchFileName = localFileName;
}
return changesWrapper.ChangeBatch;
}
public override FullEnumerationChangeBatch GetFullEnumerationChangeBatch(uint batchSize, SyncId lowerEnumerationBound, SyncKnowledge knowledgeForDataRetrieval, out object changeDataRetriever)
{
throw new NotImplementedException();
}
public override void GetSyncBatchParameters(out uint batchSize, out SyncKnowledge knowledge)
{
MyAppSqlSync.SyncBatchParameters wrapper = proxy.GetKnowledge();
batchSize = wrapper.BatchSize;
knowledge = wrapper.DestinationKnowledge;
}
public override SyncIdFormatGroup IdFormats
{
get
{
if (idFormatGroup == null)
{
idFormatGroup = new SyncIdFormatGroup();
idFormatGroup.ChangeUnitIdFormat.IsVariableLength = false;
idFormatGroup.ChangeUnitIdFormat.Length = 1;
idFormatGroup.ReplicaIdFormat.IsVariableLength = false;
idFormatGroup.ReplicaIdFormat.Length = 16;
idFormatGroup.ItemIdFormat.IsVariableLength = true;
idFormatGroup.ItemIdFormat.Length = 10 * 1024;
}
return idFormatGroup;
}
}
public override void ProcessChangeBatch(ConflictResolutionPolicy resolutionPolicy, ChangeBatch sourceChanges, object changeDataRetriever, SyncCallbacks syncCallbacks, SyncSessionStatistics sessionStatistics)
{
DbSyncContext context = changeDataRetriever as DbSyncContext;
if (context != null && context.IsDataBatched)
{
string fileName = new FileInfo(context.BatchFileName).Name;
string peerId = context.MadeWithKnowledge.ReplicaId.ToString();
if (!this.proxy.HasUploadedBatchFile(fileName, peerId))
{
FileStream stream = new FileStream(context.BatchFileName, FileMode.Open, FileAccess.Read);
byte[] contents = new byte[stream.Length];
using (stream)
{
stream.Read(contents, 0, contents.Length);
}
this.proxy.UploadBatchFile(fileName, contents, peerId);
}
context.BatchFileName = fileName;
}
SyncSessionStatistics stats = this.proxy.ApplyChanges(resolutionPolicy, sourceChanges, changeDataRetriever);
sessionStatistics.ChangesApplied += stats.ChangesApplied;
sessionStatistics.ChangesFailed += stats.ChangesFailed;
}
public override void ProcessFullEnumerationChangeBatch(ConflictResolutionPolicy resolutionPolicy, FullEnumerationChangeBatch sourceChanges, object changeDataRetriever, SyncCallbacks syncCallbacks, SyncSessionStatistics sessionStatistics)
{
throw new NotImplementedException();
}
~MyAppKnowledgeSyncProvider()
{
Dispose(false);
}
#region IDisposable Members
public virtual void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
if (this.proxy != null)
{
CloseProxy();
}
}
disposed = true;
}
}
public virtual void CloseProxy()
{
if (this.proxy != null)
{
this.proxy.Cleanup();
ICommunicationObject channel = proxy as ICommunicationObject;
if (channel != null)
{
try
{
channel.Close();
}
catch (TimeoutException)
{
channel.Abort();
}
catch (CommunicationException)
{
channel.Abort();
}
}
this.proxy = null;
}
}
#endregion
}
Bizarrely, it has just started working.
I got a notification within Visual Studio 2010 that I should "update components" or "install components". It installed some web components.
Now, my code runs without error.
While I am very glad to be past this error, I am getting past it in a very unsatisfying way.
Well. I'll take what I can get.

Having problem with WCF Service with static data

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

Categories

Resources