I am having trouble using XmlSerializer with medium trust in my ASP.NET MVC 2 application. The stack trace is:
[SecurityException: That assembly does not allow partially trusted callers.]
Bactum.PracticeTesting.MultipleChoiceQuestion.set_Prompt(String value) +0
Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderQuestionPool.Read3_MultipleChoiceQuestion(Boolean isNullable, Boolean checkType) +555
Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderQuestionPool.Read4_QuestionPoolSection(Boolean isNullable, Boolean checkType) +728
Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderQuestionPool.Read5_QuestionPool(Boolean isNullable, Boolean checkType) +726
Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderQuestionPool.Read6_QuestionPool() +79
The application is an asp.net mvc 2 presentation layer which calls my class library that serializes XML question pools to generate randomized practice tests. I took great care in designing the application to use the XmlSerializer in the System.Xml.Serialization namespace so I wouldn't have to write my own serializer and maintain it. In development on my local machine, this all worked fine, and when I published it to my GoDaddy host (windows economy), it generated the error above.
What have I tried? First I set up my local application to run in medium trust mode. Then I started googling and found that step 1 is to add
[assembly: AllowPartiallyTrustedCallers]
to each of my assemblies, but the problem stuck around. Somewhere it was mentioned that you need to sign your assemblies, but I noticed no difference at all when I did. Do you actually need to sign your assemblies for this?
Does anyone have some insight as to why this problem only occurs under medium trust, or how to resolve my issue with XmlSerializer under medium trust? I do know that I could just write my own serializer, but since I took so much care to use the one included with .NET, I would like to avoid that solution if possible.
@Haacked, @slf, Thank you very much for your help. The root of the problem is just as @slf mentioned: "the first attempt to serialize something will cause it to try to generate that assembly on the fly", and it turns out that assembly is not particularly all that friendly with medium trust. I was unable to figure out if your reason (probably on the server's temporary location) was correct or not, but the only other thing I could come up with is that the assembly created on the fly is not signed (though it does have the AllowPartiallyTrustedCallers attribute applied to it). I do not know if that is correct or not, but either way, prebaking the classes fixes it because it takes care of both problems at once.
sgen.exe: no thanks. This is more of a list of things to know when trying to use it than anything else, and solutions to all the small problems I ran into along the way. In order to use sgen, your assembly has to be signed, and the serialized assembly must have the same strong name key as the assembly with the types that are to be serialized. In order to specify this in command line, you use something like this:
C:\my\current\directory>sgen MyAssembly.dll /c:"\"/keyfile:C:\path\to\same\key\as\MyAssembly.snk"\"
The backslash quotes are escape chars which are needed to make the call work, they are not just me messing up the SO code. There are a number of other arguments you can specify and you can see how to use them all here, but this was all that was necessary to get me up and running. After this creates MyAssembly.XmlSerializers.dll, I had to reference the dll in my project. Without referencing it presentation layer, where the XmlSerializer is needed to instance my question pool, the XmlSerializer still wanted to create a new assembly. It would be nice if you only had to reference MyAssembly; if anyone knows how to make that possible I would love to know.
At this point I was up and running, but had some questions and so I started playing with things. I found that after creating the serialization assembly I could recompile MyAssembly without a strong name and it worked just fine; the only thing that had to be strongly named was MyAssembly.XmlSerializers.dll. That aside, the next problem that happened was when I tried to publish to my local machine IIS7, where I now received a new error:
[SecurityException: Request for the permission of type 'System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.]
System.Security.CodeAccessSecurityEngine.Check(Object demand, StackCrawlMark& stackMark, Boolean isPermSet) +0
System.Security.CodeAccessPermission.Demand() +54
System.Web.HttpRequest.MapPath(VirtualPath virtualPath, VirtualPath baseVirtualDir, Boolean allowCrossAppMapping) +419
System.Web.HttpServerUtility.MapPath(String path) +117
System.Web.HttpServerUtilityWrapper.MapPath(String path) +16
RocketClubs.Controllers.TestingController.NAR() in TestingController.cs:29
lambda_method(ExecutionScope , ControllerBase , Object[] ) +30
System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) +236
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +31
System.Web.Mvc.<>c__DisplayClassa.<InvokeActionMethodWithFilters>b__7() +85
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation) +309171
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +288
System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +309238
System.Web.Mvc.Controller.ExecuteCore() +174
System.Web.Mvc.MvcHandler.ProcessRequest(HttpContextBase httpContext) +209
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +599
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +171
Once again I am baffled, because my application works fine on the VS development server as well as my hosted website. Admittedly I don't know much about setting up IIS however, so its probably something to do with my setup.
The last thing about sgen is that it seems like a pain to run every time I make a change to my types, so I wanted to know how to go about setting it up to happen automatically, and possibly a way for MyAssembly to reference this new assembly each time. A quick browse on the topic landed me on this SO question, but I haven't had time to try it out yet.
Again, thanks for the help.