views:

351

answers:

4

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.

+3  A: 

The first thing I would try is going to the project properties and changing "Generate serialization assembly: "Auto" to "On" and be sure to deploy those serialization assemblies with your project.

If you do not, the first attempt to serialize something will cause it to try to generate that assembly on the fly, probably on the server's temporary location. If the permission is not allowed to do that, you'll get all kinds of exceptions. By pre-baking the serialization assembly you can avoid that step for both performance and compatibility. Beyond that, I'm not sure.

slf
Thanks, I'll give that a try after lunch.
NickLarsen
This is not quire the answer because of the restrictions on the Generate Serialization Assembly and what triggers it to make the serialization assembly. Check this post (http://stackoverflow.com/questions/134224/generating-an-xml-serialization-assembly-as-part-of-my-build), second answer for more information.
NickLarsen
+3  A: 

Try running the sgen.exe command against your assembly to pre-generate the serialization assemblies. sgen.exe is part of the Visual Studio SDK and should already be on your machine if you're using VS.

Haacked
This was the solution, but I ran into more than just a few problems along the way to getting it to work. I'll be editing my question with a follow up soon to go over the process, and some errors I am now running into, though...my problem is solved as far as getting my GoDaddy hosted site working. Thanks a lot for your help.
NickLarsen
A: 

Assuming you set up medium trust properly, it's still possible to fail, if you serialize anything related that itself requires full trust. So, it's likely you are indeed serializing something that requires full trust, or you configured your assemblies wrong.

Haacked and slf are right for your scenario. Since you can't change the GoDaddy environment, the simplest thing for you to is to follow their advice.

Richard Hein
Give a reason for the vote down.
Richard Hein
There is another failure, but I am unsure why, check my follow up edits for more questions.
NickLarsen
+2  A: 

Yes, your assemblies must be signed for AllowPartiallyTrustedCallers to take effect:

AllowPartiallyTrustedCallersAttribute is only effective when applied by a strong-named assembly at the assembly level.

MSDN

Rex M
"Somewhere it was mentioned that you need to sign your assemblies, but I noticed no difference at all when I did."
Richard Hein
It turns out you do need to have the assembly which you are creating the XmlSerializer assembly for signed. Check my edits up top to see the my full wrap up.
NickLarsen