Stono
Stono

Reputation: 2337

.NET STA Com Object from MTA apartment

I'm working on a site (MVC) at the moment, which uses ajax quite heavily to request data from slow responding asynchronous resources.

I have a problem though, every request has to first be authorised, and part of that authorisation is to get the current RSA username, by doing Server.CreateObject("Rsacookieapi.RSACookie").GetUserName(). The problem is that this particular com object doesn't run on MTApartments, only STAparatments (they don't offer any alternative .net component, so we must use this one), therefore CreateObject fails.

I have got round this through much researching by creating a custom route handler that executes the controller context in an STA (equivalent to do ASPCOMPAT=TRUE on webforms), but this brings in a further problem, I can no longer create Asynchronous controller methods when the controller context is being executed from within an STA.

So I started thinking, if I could create the COM object in an STA, and somehow create a delegate/marshall to it, so it could be accessed from MTA, then all would be good.

As a proof of concept, I had a global variable myComObject, which I populated by doing the CreateObject from within an STA request (/StaController/Index).

I then tried to access myComObject from an MTA request (/MtaController/Index).

The great news is, this works. It appears as if the clr is handling all the marshalling for me.

So I began to write some code, that spawns a new thread in an STA, but the problem is i'm stil required to pass the HttpContext.Current.Server(HttpServerUtilityBase) to the thread, which when I call .CreateObject on just executes it on the MTA of the .Server object anyway.

I hope this makes sense? To clarify:

  1. I don't want any of my controllers or actions to be executed on an STA's
  2. I want to create the object on an STA's and then give access to it from all other MTA's

I sincerely hope someone has a suggestion!

Regards

Karl

Upvotes: 1

Views: 1180

Answers (1)

Ben
Ben

Reputation: 35643

Option 1: Use Tlbimp to create an assembly.

Have you tried using Tlbimp.exe to create an assembly for the component, and use that? The documentation says this results in "poor performance". But this is due to the required marshalling, which is just the same as what you are trying to do here.

http://msdn.microsoft.com/en-us/library/zwk9h2kb.aspx

The AspCompat attribute forces the page to execute in STA mode. The runtime throws an exception if the compatibility tag is omitted and an STA component is referenced on the page. If you convert the STA component to an assembly using Tlbimp.exe, the runtime does not detect that the component uses the STA model and does not throw an exception, but your application can suffer from poor performance.

Option 2: Lie.

It may work fine in your scenario if you just change the registration to "both". That depends on the object and how you use it.

Provided that you use a fresh object each time, and dispose of the object immediately using Marshall.ReleaseComObject so you aren't tempted to re-use it from another thread, then there will be no issues.

(STA means that the object can only be called from one thread. COM will guarantee that for you, when sharing objects, by marshalling between apartments with different threading models, so you can assume that all objects in your apartment are compatible with your threading model. However you can also provide that guarantee yourself by just not sharing the object and not calling the object from any other threads).

To do this just go into the registry under HKEY_CLASSES_ROOT\CLSID\{guid} and change the ThreadingModel key to "Both".

Then stress-test it.

Upvotes: 3

Related Questions