I am trying to deserialize an xml string in c#3.5, the code below does work in c# 4.0.
When I try to run in the code in c#3.5 I get an Object reference not set to an instance of an object exception when the code tries in initialize the XmlSerializer.
Any help would be appreciated.
string xml = "<boolean xmlns=\"http://schemas.microsoft.com/2003/10/serialization/\">false</boolean>";
var xSerializer = new XmlSerializer(typeof(bool), null, null,
new XmlRootAttribute("boolean"),
"http://schemas.microsoft.com/2003/10/serialization/");
using (var sr = new StringReader(xml))
using (var xr = XmlReader.Create(sr))
{
var y = xSerializer.Deserialize(xr);
}
System.NullReferenceException was unhandled
Message="Object reference not set to an instance of an object."
Source="System.Xml"
StackTrace:
at System.Xml.Serialization.XmlSerializer..ctor(Type type, XmlAttributeOverrides overrides, Type[] extraTypes, XmlRootAttribute root, String defaultNamespace, String location, Evidence evidence)
at System.Xml.Serialization.XmlSerializer..ctor(Type type, XmlAttributeOverrides overrides, Type[] extraTypes, XmlRootAttribute root, String defaultNamespace)
....
at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException:
It looks like in .NET 3.5 it doesn't like the Type[] extraTypes to be null. Just pass an empty Type[] instead, for example new Type[0], or just simply:
var xSerializer = new XmlSerializer(typeof(bool), null, Type.EmptyTypes,
new XmlRootAttribute("boolean"),
"http://schemas.microsoft.com/2003/10/serialization/");
As a side note: when creating XmlSerializer instances using non-trivial constructors (like this one), it is very important to cache and re-use the serializer - otherwise it'll generate an in-memory assembly per serializer, which is a: bad for performance, but b: causes a severe memory leak (assemblies cannot be unloaded).
Related
I am using extentent reports to generate report but the test excution fails on extent.flush() method. I am using
extentreports 3.1.3
Specflow.xunit 2.2.1
This code use to work previously but not working anymore. Error returned :
System.IO.FileNotFoundException
HResult=0x80070002
Message=Could not load file or assembly 'System.Web.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. The system cannot find the file specified.
Source=RazorEngine
StackTrace:
at RazorEngine.Compilation.CSharp.CSharpDirectCompilerService..ctor(Boolean strictMode, Func1 markupParserFactory)
at RazorEngine.Compilation.DefaultCompilerServiceFactory.CreateCompilerService(Language language)
at RazorEngine.Templating.RazorEngineCore.CreateTemplateType(ITemplateSource razorTemplate, Type modelType)
at RazorEngine.Templating.RazorEngineCore.Compile(ITemplateKey key, Type modelType)
at RazorEngine.Templating.RazorEngineService.CompileAndCacheInternal(ITemplateKey key, Type modelType)
at RazorEngine.Templating.RazorEngineService.GetCompiledTemplate(ITemplateKey key, Type modelType, Boolean compileOnCacheMiss)
at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action1 withWriter)
at AventStack.ExtentReports.Reporter.ExtentHtmlReporter.Flush()
at AventStack.ExtentReports.Model.Report.<>c.b__29_1(IExtentReporter x)
at System.Collections.Generic.List1.ForEach(Action1 action)
at AventStack.ExtentReports.Model.Report.NotifyReporters()
at AventStack.ExtentReports.Model.Report.Flush()
at AventStack.ExtentReports.ExtentReports.Flush()
at XUnitTestProject5.UnitTest1.Test1() in C:\Users\kotar\source\repos\XUnitTestProject5\XUnitTestProject5\UnitTest1.cs:line 25
var HTMLReporter = new ExtentHtmlReporter(#"C:\test\TestReport.html");
HTMLReporter.Configuration().Theme = AventStack.ExtentReports.Reporter.Configuration.Theme.Dark;
var extent = new ExtentReports();
extent.AttachReporter(HTMLReporter);
var featurename = extent.CreateTest<Feature>("login feature");
var scenario= featurename.CreateNode<Scenario>("Login as a user ");
scenario.CreateNode<Given>("user has access to the methos ");
extent.Flush();
If you are using .net core, you have to use "ExtentReports.Core" NuGet package instead of "ExtentReports" and initialize your extent object like this :
public AventStack.ExtentReports.ExtentReports _extent = new
AventStack.ExtentReports.ExtentReports();
I am creating a .Net app in Visual Studio 2012 that queries an address table in my SQL dB and uses the Census Geocoding API to return the specific MSA for each address. I have existing code for the dB query, but I am having trouble with converting the Json output of the Census API to an Xml dataset. I am using Json.net to serialize the json output and then deserialize to .net in order to load into an XmlDocument. Unfortunately, I keep getting an XmlException error:
Data at the root level is invalid. Line 1, position 1
Details:
System.Xml.XmlException was unhandled HResult=-2146232000
Message=Data at the root level is invalid. Line 1, position 1.
Source=System.Xml LineNumber=1 LinePosition=1 SourceUri=""
StackTrace:
at System.Xml.XmlTextReaderImpl.Throw(Exception e)
at System.Xml.XmlTextReaderImpl.Throw(String res, String arg)
at System.Xml.XmlTextReaderImpl.ParseRootLevelWhitespace()
at System.Xml.XmlTextReaderImpl.ParseDocumentContent()
at System.Xml.XmlTextReaderImpl.Read()
at System.Xml.XmlLoader.Load(XmlDocument doc, XmlReader reader, Boolean preserveWhitespace)
at System.Xml.XmlDocument.Load(XmlReader reader)
at System.Xml.XmlDocument.LoadXml(String xml)
at ConsoleApplication1.Program.Main(String[] args) in c:\Users\jdsmith\Documents\Visual Studio
2012\Projects\C#\MSA_Application_v2\MSA_Application_v2\Model\Program.cs:line
54
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext
executionContext, ContextCallback callback, Object state, Boolean
preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean
preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart() InnerException:
I believe either the Json or the Xml need to be formatted further, but I don't know how. Also, I'm sure I am making this too difficult on myself...if there is a better way, I am all ears.
Here is the sample geolookup I am using to test:
http://geocoding.geo.census.gov/geocoder/geographies/address?street=4600+Silver+Hill+Rd&city=Suitland&state=MD&benchmark=Public_AR_Census2010&vintage=Census2010_Census2010&layers=14&format=json
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Data;
using System.Net;
using System.IO;
using System.Xml;
using System.Runtime.Serialization.Json;
using System.Xml.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace ConsoleApplication1
{
class Program
{
private static string geoRT = "geographies";
private static string geoST = "address";
private static string geoStreet = "4600+Silver+Hill+Rd";
private static string geoCity = "Suitland";
private static string geoState = "MD";
private static string geoZip = "20746";
private static string geoBM = "Public_AR_Census2010";
private static string geoVin = "Census2010_Census2010";
private static string geoLayer = "all";
private static string geoFormat = "json";
static void Main(string[] args)
{
StringBuilder geoRelURI = new StringBuilder();
geoRelURI.AppendFormat(#"{0}/{1}?street={2}&city={3}&state={4}&zip={5}&benchmark={6}&vintage={7}&layers={8}&format={9}"
, geoRT, geoST, geoStreet, geoCity, geoState, geoZip, geoBM, geoVin, geoLayer, geoFormat);
Uri geoBaseURI = new Uri("http://geocoding.geo.census.gov/geocoder/");
Uri geoURI = new Uri(geoBaseURI, geoRelURI.ToString());
//Console.WriteLine(geoURI);
//Console.ReadLine();
WebRequest geoRequest = WebRequest.Create(geoURI);
WebResponse geoResponse = geoRequest.GetResponse();
Stream geoDataStream = geoResponse.GetResponseStream();
StreamReader geoReader = new StreamReader(geoDataStream);
string geoString = geoReader.ReadToEnd();
var jsonConvert = JsonConvert.SerializeObject(geoString);
string jsonString = jsonConvert.ToString();
var xmlConvert = JsonConvert.DeserializeObject(jsonString);
string xmlString = xmlConvert.ToString();
XmlDocument geoXMLDoc = new XmlDocument();
geoXMLDoc.LoadXml(xmlString);
XmlWriterSettings xmlSettings = new XmlWriterSettings();
xmlSettings.Indent = true;
XmlWriter geoXMLWriter = XmlWriter.Create("geoXML.xml", xmlSettings);
geoXMLDoc.Save(geoXMLWriter);
Console.Write("<BR>" + geoXMLDoc.OuterXml);
//Console.WriteLine(xmlString);
//Console.ReadLine();
geoDataStream.Close();
geoResponse.Close();
}
}
}
First of all, you are passing a JSON string to geoXMLDoc.LoadXml(). That's not going to work. What you want to do is to convert the JSON to an XmlDocument via JsonConvert.DeserializeXmlNode.
However, some of your JSON properties contain characters that are invalid for use in XML names, in specific whitespace:
{"Census Blocks":[{"BLKGRP":"1",
It seems that this causes DeserializeXmlNode to throw an exception. Thus you'll need to rename the names:
var obj = JObject.Parse(geoString);
foreach (var fix in (from property in obj.Descendants().OfType<JProperty>()
let newName = XmlConvert.EncodeLocalName(property.Name.Replace(" ", ""))
where newName != property.Name
select new { Old = property, New = new JProperty(newName, property.Value) })
.ToList())
{
fix.Old.Replace(fix.New);
}
var xmldoc = JsonConvert.DeserializeXmlNode(obj.ToString());
Need you to post what you are attempting to load into the XmlDocument. That is where you are hitting your problem. If you are trying to load the JSON you get from the web call it won't work, if you are (as I suspect) using JSON.Net to convert the JSON to Xml, the Xml is missing something that the XmlDocument wants. Could be the Xml declaration line, or your xml fragment may not include a root node. Without seeing the xml we have no way to tell specifically what is missing or malformed.
I am getting exception on trying to consume a service (3rd party), below is the stack trace for the exception.
StackTrace " at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id)\r\n at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle)\r\n at System.ServiceModel.Dispatcher.XmlSerializerOperationFormatter.SerializeBody(XmlDictionaryWriter writer, MessageVersion version, XmlSerializer serializer, MessagePartDescription returnPart, MessagePartDescriptionCollection bodyParts, Object returnValue, Object[] parameters)\r\n at System.ServiceModel.Dispatcher.XmlSerializerOperationFormatter.SerializeBody(XmlDictionaryWriter writer, MessageVersion version, String action, MessageDescription messageDescription, Object returnValue, Object[] parameters, Boolean isRequest)" string
Earlier it was working fine, but after updating the service reference it started throwing the above exception.
Also tried the solution described in the below link, but was of no help.
There was an error in serializing body of message
Appreciate your kind help in resolving the same.
Below is the code that i am using
//reportContent is having xml data as string.
XmlDocument xmlReportDocument = new XmlDocument();
xmlReportDocument.LoadXml(reportContent);
RequestHandlerProcessSoapClient ws = new RequestHandlerProcessSoapClient();
ws.ClientCredentials.UserName.UserName = _reportFormatData.User;
ws.ClientCredentials.UserName.Password = _reportFormatData.Password;
///Create the request
myRequest request = new myRequest();
request.Requestor = new Requestor();
request.Requestor.ApplicationID = _reportFormatData.ApplicationId;
request.Requestor.Stylesheet = template;
request.Requestor.Environment = _reportFormatData.Environment;
request.Payload = xmlReportDocument;
///Setup the attachements
myAttachmentRequest attachmentrequest = new myAttachmentRequest();
attachmentrequest.setRequest = request;
myResponse serResp = ws.renderDocument(attachmentrequest)//Error shows up in this line.
Finally found the solution to this question. After adding a service reference, by default payload property in Reference.cs was returning a value of type 'Object', and this i changed it to return System.Xml and it clicked.
END GOAL: Efficiently (in one pass) read all CellRecords on a huge (30,000+ row), protected Worksheet.
Problem:
Using the HSSF.EventUserModel, how can I read all Records (including CellRecords) for an XLS file with both Workbook and Worksheet protection?
Create Input Spreadsheet (in Excel 2010):
Create new Blank workbook.
Set value of A1 to number: 50
Set value of A2 to string: fifty
Set value of A3 to formula: =25*2
Review (ribbon) -> Protect Sheet -> Password: pass1
Review (ribbon) -> Protect Workbook -> Password: pass1
File (ribbon) ->Save As... -> Save as type: Excel 97-2003 Workbook
Progress thus far:
The XLS file opens without a password in Excel. Therefore, you shouldn't need the password to open it in POI.
The XLS file opens successfully with new HSSFWorkbook(Stream fs). However, I need the efficiency of EventUserModel for my actual spreadsheet.
Setting NPOI.HSSF.Record.Crypto.Biff8EncryptionKey.CurrentUserPassword = "pass1"; did not work.
The ProcessRecord( ) function catches a PasswordRecord, but I can't find any documentation on how to properly handle it.
Perhaps, the EncryptionInfo or Decryptor classes may be of some use.
Note:
I'm using NPOI. However, I can translate any java examples to C#.
Code:
I use the following code to capture Record events. My Book1-unprotected.xls (without protection) shows all Record events (including cell values). My Book1-protected.xls displays some records and throws an exception.
I just view processedEvents in the debugger.
using System;
using System.Collections.Generic;
using System.IO;
using NPOI.HSSF.Record;
using NPOI.HSSF.Model;
using NPOI.HSSF.UserModel;
using NPOI.HSSF.EventUserModel;
using NPOI.POIFS;
using NPOI.POIFS.FileSystem;
namespace NPOI_small {
class myListener : IHSSFListener {
List<Record> processedRecords;
private Stream fs;
public myListener(Stream fs) {
processedRecords = new List<Record>();
this.fs = fs;
HSSFEventFactory factory = new HSSFEventFactory();
HSSFRequest request = new HSSFRequest();
MissingRecordAwareHSSFListener mraListener;
FormatTrackingHSSFListener fmtListener;
EventWorkbookBuilder.SheetRecordCollectingListener recListener;
mraListener = new MissingRecordAwareHSSFListener(this);
fmtListener = new FormatTrackingHSSFListener(mraListener);
recListener = new EventWorkbookBuilder.SheetRecordCollectingListener(fmtListener);
request.AddListenerForAllRecords(recListener);
POIFSFileSystem poifs = new POIFSFileSystem(this.fs);
factory.ProcessWorkbookEvents(request, poifs);
}
public void ProcessRecord(Record record) {
processedRecords.Add(record);
}
}
class Program {
static void Main(string[] args) {
Stream fs = File.OpenRead(#"c:\users\me\desktop\xx\Book1-protected.xls");
myListener testListener = new myListener(fs); // Use EventModel
//HSSFWorkbook book = new HSSFWorkbook(fs); // Use UserModel
Console.Read();
}
}
}
UPDATE (for Juan Mellado):
Below is the exception. My best guess right now (in the answer by Victor Petrykin) is that the HSSFEventFactory uses RecordInputStream which cannot natively decrypt protected records. Upon receiving the exception, processedRecords contains 22 records including the following potentially significant ones:
processedRecords[5] is a WriteAccessRecord with a garbled (probably encrypted) value for .name
processedRecords[22] is a RefreshAllRecord and is the last Record in the list
Exception:
NPOI.Util.RecordFormatException was unhandled
HResult=-2146233088
Message=Unable to construct record instance
Source=NPOI
StackTrace:
at NPOI.HSSF.Record.RecordFactory.ReflectionConstructorRecordCreator.Create(RecordInputStream in1)
at NPOI.HSSF.Record.RecordFactory.CreateSingleRecord(RecordInputStream in1)
at NPOI.HSSF.Record.RecordFactory.CreateRecord(RecordInputStream in1)
at NPOI.HSSF.EventUserModel.HSSFRecordStream.GetNextRecord()
at NPOI.HSSF.EventUserModel.HSSFRecordStream.NextRecord()
at NPOI.HSSF.EventUserModel.HSSFEventFactory.GenericProcessEvents(HSSFRequest req, RecordInputStream in1)
at NPOI.HSSF.EventUserModel.HSSFEventFactory.ProcessEvents(HSSFRequest req, Stream in1)
at NPOI.HSSF.EventUserModel.HSSFEventFactory.ProcessWorkbookEvents(HSSFRequest req, POIFSFileSystem fs)
at NPOI_small.myListener..ctor(Stream fs) in c:\Users\me\Documents\Visual Studio 2012\Projects\myTest\NPOI_small\Program.cs:line 35
at NPOI_small.Program.Main(String[] args) in c:\Users\me\Documents\Visual Studio 2012\Projects\myTest\NPOI_small\Program.cs:line 80
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException: NPOI.Util.RecordFormatException
HResult=-2146233088
Message=Expected to find a ContinueRecord in order to read remaining 137 of 144 chars
Source=NPOI
StackTrace:
at NPOI.HSSF.Record.RecordInputStream.ReadStringCommon(Int32 requestedLength, Boolean pIsCompressedEncoding)
at NPOI.HSSF.Record.RecordInputStream.ReadUnicodeLEString(Int32 requestedLength)
at NPOI.HSSF.Record.FontRecord..ctor(RecordInputStream in1)
I think it's the bug in the NPOI library code. As far as I understood they use incorrect stream type for HSSFEventFactory: it uses RecordInputStream instead of RecordFactoryInputStream with decryption function like in the original POI library or in the UserModel (that's why HSSFWorkbook is working)
This code is working too but it's not a event logic:
POIFSFileSystem poifs = new POIFSFileSystem(fs);
Entry document = poifs.Root.GetEntry("Workbook");
DocumentInputStream docStream = new DocumentInputStream((DocumentEntry)document);
//RecordFactory factory = new RecordFactory();
//List<Record> records = RecordFactory.CreateRecords(docStream);
RecordFactoryInputStream recFacStream = new RecordFactoryInputStream(docStream, true);
Record currRecord;
while ((currRecord = recFacStream.NextRecord()) != null)
ProcessRecord(currRecord);
I have a program that takes objects stored as XML in a database (basicly a message queue) and de-serializes them. Intermittently, I will get one of the following errors:
System.Runtime.InteropServices.ExternalException: Cannot execute a program. The command being executed was "C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\csc.exe" /noconfig /fullpaths #"C:\Documents and Settings\useraccount\Local Settings\Temp\lh21vp3m.cmdline".
at System.CodeDom.Compiler.Executor.ExecWaitWithCaptureUnimpersonated(SafeUserTokenHandle userToken, String cmd, String currentDir, TempFileCollection tempFiles, String& outputName, String& errorName, String trueCmdLine)
at System.CodeDom.Compiler.Executor.ExecWaitWithCapture(SafeUserTokenHandle userToken, String cmd, String currentDir, TempFileCollection tempFiles, String& outputName, String& errorName, String trueCmdLine)
at Microsoft.CSharp.CSharpCodeGenerator.Compile(CompilerParameters options, String compilerDirectory, String compilerExe, String arguments, String& outputFile, Int32& nativeReturnValue, String trueArgs)
at Microsoft.CSharp.CSharpCodeGenerator.FromFileBatch(CompilerParameters options, String[] fileNames)
at Microsoft.CSharp.CSharpCodeGenerator.FromSourceBatch(CompilerParameters options, String[] sources)
at Microsoft.CSharp.CSharpCodeGenerator.System.CodeDom.Compiler.ICodeCompiler.CompileAssemblyFromSourceBatch(CompilerParameters options, String[] sources)
at System.CodeDom.Compiler.CodeDomProvider.CompileAssemblyFromSource(CompilerParameters options, String[] sources)
at System.Xml.Serialization.Compiler.Compile(Assembly parent, String ns, XmlSerializerCompilerParameters xmlParameters, Evidence evidence)
at System.Xml.Serialization.TempAssembly.GenerateAssembly(XmlMapping[] xmlMappings, Type[] types, String defaultNamespace, Evidence evidence, XmlSerializerCompilerParameters parameters, Assembly assembly, Hashtable assemblies)
at System.Xml.Serialization.TempAssembly..ctor(XmlMapping[] xmlMappings, Type[] types, String defaultNamespace, String location, Evidence evidence)
at System.Xml.Serialization.XmlSerializer.GenerateTempAssembly(XmlMapping xmlMapping, Type type, String defaultNamespace)
at System.Xml.Serialization.XmlSerializer..ctor(Type type, String defaultNamespace)
at System.Xml.Serialization.XmlSerializer..ctor(Type type)
.....
Or I'll get this one:
System.InvalidOperationException: Unable to generate a temporary class (result=1).
error CS0016: Could not write to output file 'c:\Documents and Settings\useraccount\Local Settings\Temp\nciktsd7.dll' -- 'Could not execute CVTRES.EXE.'
at System.Xml.Serialization.Compiler.Compile(Assembly parent, String ns, XmlSerializerCompilerParameters xmlParameters, Evidence evidence)
at System.Xml.Serialization.TempAssembly.GenerateAssembly(XmlMapping[] xmlMappings, Type[] types, String defaultNamespace, Evidence evidence, XmlSerializerCompilerParameters parameters, Assembly assembly, Hashtable assemblies)
at System.Xml.Serialization.TempAssembly..ctor(XmlMapping[] xmlMappings, Type[] types, String defaultNamespace, String location, Evidence evidence)
at System.Xml.Serialization.XmlSerializer.GenerateTempAssembly(XmlMapping xmlMapping, Type type, String defaultNamespace)
at System.Xml.Serialization.XmlSerializer..ctor(Type type, String defaultNamespace)
at System.Xml.Serialization.XmlSerializer..ctor(Type type)
....
The program process thousands of messages a day successfully, but I only get these errors maybe 2 or 3 times a day. They don't appear to be correlated to any specific kind of message, just completely random.
Any idea what causes those errors and how to fix it?
ETA - Here's the code that is causing the errors, in case that helps:
public class MessageContextBuilder<T> where T : MessageContextBase
{
private static IDictionary<string, XmlSerializer> SerializerCache { get; set; }
public ILog Logger { get; set; }
public MessageContextBuilder() {
if (SerializerCache == null) SerializerCache = new Dictionary<string, XmlSerializer>();
Logger = LogContextManager.Context.GetLogger<MessageContextBuilder<T>>();
}
public T BuildContextFromMessage(IEmailQueueMessage msg) {
XmlSerializer serializer = GetSerializer(typeof(T));
XmlReader r = XmlReader.Create(new StringReader(msg.MessageDetails));
if (serializer.CanDeserialize(r)) {
T rval = (T)serializer.Deserialize(r);
rval.EmailAddress = msg.EmailAddress;
rval.LocaleID = msg.LocaleID;
rval.StoreID = msg.StoreID;
rval.MessageID = msg.UniqueKey;
return rval;
} else {
throw new ArgumentException("Cannot deserialize XML in message details for message #" + msg.UniqueKey);
}
}
public XmlSerializer GetSerializer(Type t) {
if (!SerializerCache.ContainsKey(t.FullName)) {
SerializerCache.Add(t.FullName, new XmlSerializer(t)); // Error occurs here, in XmlSerializer constructor, intermittently
}
return SerializerCache[t.FullName];
}
}
You can pre-create serializers: http://msdn.microsoft.com/en-us/library/bk3w6240%28v=VS.100%29.aspx Just give it a try. The next canonical candidate for such problems is your virus scanner. Your tool is writing to disc while creating serializers. I have seen virus scanner producing all kind of strange errors in such situations.
XmlSerializer is supposed to be thread safe.
Even if that's the case, you can notice the behavior you are getting is in both cases failing at: XmlSerializer..ctor(Type type)
Given that, it seriously look like a multi-threading limitation trying to create serializers.
I suggest to take this code you have:
public XmlSerializer GetSerializer(Type t) {
if (!SerializerCache.ContainsKey(t.FullName)) {
SerializerCache.Add(t.FullName, new XmlSerializer(t)); // Error occurs here, intermittently
}
return SerializerCache[t.FullName];
}
And implement a lock on the Add. This way you are only creating 1 serializer at a time. The hit is small if you aren't processing tons of different types.
Note that you need the lock anyway, as the way it is you could get duplicate exceptions when 2 types try to be added at the same time.
static object serializerCacheLock = new object();
public XmlSerializer GetSerializer(Type t) {
if (!SerializerCache.ContainsKey(t.FullName))
lock(serializerCacheLock)
if (!SerializerCache.ContainsKey(t.FullName)) {
SerializerCache.Add(t.FullName, new XmlSerializer(t));
}
return SerializerCache[t.FullName];
}
If the above still isn't enough, I'd try with a read/write lock on serializer constructor vs. serializers usage. Line of thought being that maybe the multi-threading issue is worth than 2 ctors running at the same time.
All above is a Huge guess, but if it were me I'd definitely confirm is not that.
For the first error (cannot execute a program), you might be running into the same XmlSerializer bug that we ran into. It turns out XmlSerlializer throws that exception when Directory.CurrentDirectory is set to a folder that no longer exists.
Our specific situation is different than yours, but I'll give the details in case it helps shed light on what might be happening for you, or it helps anyone else. In our case, a small number of our customers would get that error after launching our WinForms application directly from the installer, i.e. they chose the "run now" option after installing or upgrading. (Unclear why it happened to some but not others). What we suspect is happening is that our installer (InstallAware) occasionally starts our application with the current directory set to a folder that no longer exists, or is about to be deleted. To test this theory, I wrote a test app which simulates launching from the installer:
string dir = #"C:\Users\me\Documents\Temp\WillBeDeleted";
Directory.CreateDirectory(dir);
Directory.SetCurrentDirectory(dir);
Process.Start(#"C:\Program Files (x86)\...\our.exe");
Directory.SetCurrentDirectory(#"C:\"); // otherwise, won't be able to delete
Directory.Delete(dir);
Sure enough, as soon as the launched application created a new instance of XmlSerializer, the exception would be thrown. I put in trace statements to show the result of GetCurrentDirectory(), and indeed it was set to the WillBeDeleted folder. The fix was to SetCurrentDirectory to a valid location during application initialization, before any serialization took place.
This is a sign that you are not caching your serialisers which is not good at all => it leads to memory leak and I suspect you will experience this.
Remember that .NET generates code and compiles them into assemblies every time you create a serialiser.
Always create your serialisers and then cache them.
Here is a sample:
public class SerialiserCache
{
private static readonly SerialiserCache _current = new SerialiserCache();
private Dictionary<Type, XmlSerializer> _cache = new Dictionary<Type, XmlSerializer>();
private SerialiserCache()
{
}
public static SerialiserCache Current
{
get { return _current; }
}
public XmlSerializer this[Type t]
{
get
{
LoadIfNecessary(t);
return _cache[t];
}
}
private void LoadIfNecessary(Type t)
{
// double if to prevent race conditions
if (!_cache.ContainsKey(t))
{
lock (_cache)
{
if (!_cache.ContainsKey(t))
{
_cache[t] = new XmlSerializer(typeof(T));
}
}
}
}
}