I have two different asp.net web applications both referencing the same dll e.g. SharedLibrary.dll.
I want to know if there is a way of adding some web.config setting to one of the application's config files to avoid the need to have two copies of the dll lying around.
My [simplified] directory structure is as follows:
\root
\Admin
\web.config
\Addins
\AdminWebAppPage.aspx
\bin
\AdminWebApp.dll
\SharedLibrary.dll <- this is the duplicated dll (I'd like to remove it from here ideally)
\Websites
\MyWebsite
\webroot
\web.config
\MainWebPage.aspx
\bin
\MainWebsite.dll
\SharedLibrary.dll
If you register the assembly in the Global Assembly Cache (GAC), all your apps can access it without having a copy around. However, from a versioning and deployment perspective, I'd say keeping a per-site copy is preferable.
Clarification: keepin a per-site copy is preferable when the sites are not related. Obviously if a shared library changes, you'd want both the main web site and the admin site to get the updated copy. :-)
I have done this in the past where virtual ROOTS under a website shared a DLL. That is, I had a website with some DLL's and an administrative virtual root underneath it. Since it is a child of the main website it inherits the website's DLL's. But I'm not sure about seperate websites...
If you're on a Vista machine you could look at using a NTFS symbolic link
If you're pre-Vista but on Win2k or later a NTFS Junction Point might help
You can put that DLL in Directory where all web-apps you want this DLL to be shared among have the read rights.
In the Application_OnStart event you can dynamically load the assembly using this code..
Assembly SampleAssembly;
SampleAssembly = Assembly.LoadFrom("c:\\Sample.Assembly.dll");
And you can also try resolving the reference by using Assembly_Resolve event.
Related
I'm not sure if I'm asking the right question exactly, but if you know the answer then you'll probably understand what I'm asking.
I updated a stored proc and tested in dev w/ no problem. So then I updated the proc in production and it caused errors. Took me a bit to figure out what was going on because all I changed was a field in the select list from i.Price to Price = coalesce(r.Price, i.Price). Eventually I figured the only difference could be that r.Price is a decimal value whereas i.Price is a money value so the return type of the field had changed.
Normally that shouldn't matter for c# code. the DataReader value gets cast to decimal either way just fine. But all I had to do to fix the error was reset the app pool and then it worked. I believe I could have also updated my query to cast to a money type and that probably would have worked as well.
I know I've run into this kind of issue a few times before, enough to know there's some kind of caching going on under the hood in asp.net and/or the sql assemblies.
My question is, how can I get this cache to refresh without recycling the app pool. Or is that the only way?
Here what exactly happens when you deploy :
ASP.NET has a feature called shadow copying that enables assemblies that are used in an application domain to be updated without unloading the AppDomain. Normally, this is required because the Common Language Runtime (CLR) will lock the assemblies so you can’t just copy a new assembly over it. Shadow copying means that the original assembly is copied to a temporary location. The copied assembly is locked, and the original assembly can be updated with a new version.
What is assembly interning?
From :
From MSDN:
The ASP.NET shadow copy feature enables assemblies that are used in an application domain to be updated without unloading that AppDomain (necessary because the CLR locks assemblies that are being used). This is done by copying application assemblies to a separate location (either a default CLR-determined location or a user-specified one) and loading the assemblies from that location. This allows the original assembly to be updated while the shadow copy is locked. ASP.NET turns on this feature by default for Bin folder assemblies so that DLLs can continue to be updated while a site is up and running.
https://msdn.microsoft.com/en-us/magazine/hh882452.aspx
App Pool will be recycled automatically when below happens:
Any modifications in the Application’s BIN Directory
Making changes in any Configuration File/s, like Web.config or others ( if you have any specific config file in your application say in a directory called App_Config).
Making modifications in the Source code files in APP_CODE Directory. This maybe change in any Source code files, or adding or deleting files from this directory.
Making changes in the Global.asax file
Making Changes in the Machine.config file.
Making any modifications in the Web Application’s Root Directory. This means creating files/subdirectories on the fly can lead to application pool recycling.
Modifications for references of Web Services of App_WebReferences directory.
Modifying the Security Settings of any directory in the Root directory. (Like specifying read security rights for everyone or any other specific user or user group.)
For more details :
Does any change in any file inside bin folder cause application recycle in ASP.NET web application?
We have a solution with multiple web projects, and there are some pages that should be present in several of them. So we'd need some sort of a shared project which contains aspx files, and which can be referenced by other web projects.
Now there are a few implementations out there:
One implementation described by ScottGu which involves building the shared project, and than copying the output aspx into the host project, and referencing the dll of the aspx. This method has the disadvantage that if the apsx gets modified it must been recopied.
Another option, based on David Ebbo's post would be to convert the aspx into ascx-es which can be referenced as custom controls, and than include those custom-control-aspx-es into the host project inside of some placeholder pages. But my concerns are: can all apsx pages transformed into an ascx? I mean there's no Page.LoadComplete event in user controls for example.
And yet another option is to use virtual directories that map into the shared webproject, as described in a Microsoft KB article. The problem again with this method is that the shared aspx-es must be in predefined directories(that is the virtual directory).
If the name of virtual directory overlaps a physical directory, the virtual overrides it and no pages from the latter can be used.
Is it perhaps possible to merge these two together?
Any thoughts? Thanks in advance
P.S. How about debugging the shared pages?
I solved this problem by making shared Class Library that contains .ascx controls. Have not found real difficulties when converting from .aspx pages to .ascx controls. Found this sample by ScottGu really simple and good place to start. http://webproject.scottgu.com/CSharp/usercontrols/usercontrols.aspx.
The solution which we are using is ScottGu's method, which involves copying the aspx files from the shared project to the host projects.
A post build event in the shared project copies the files to their place, like this
xcopy "$(ProjectDir)Forms\Techs\AddEditTech.aspx" "$(SolutionDir)..\TTAdmin\Forms\Companies" /i /d /y
if errorlevel 1 goto BuildEventFailed
xcopy ....
goto BuildEventOK
:BuildEventFailed
echo POSTBUILDSTEP for $(ProjectName) FAILED
exit 1
:BuildEventOK
echo POSTBUILDSTEP for $(ProjectName) COMPLETED OK
Have you thought about baseclassing the functionality into classes that inherit from Page and place those classes in the shared DLL. All implementing applications could then implement that page by inheriting from it and still be able to change functionality assuming the base classes provide overridable methods. I've had pretty good success with this when I had a lot of pages that were used in many applications and all had the same code. Just a possibility.
Have you considered service oriented development? Build the functionality into services you can share. Build your functionality like widgets. This way you only have one codebase but you can use it in multiple sites.
Think outside the (.Net box)
I have a web project called "TestResourceApp" with Labels.resx in App_GlobalResources folder. I want to add another language by creating a satellite assembly.
Here are the steps I took to create the satellite assembly. The default text always get displayed. What did I do wrong ?
1) Create Labels.fr.resx in a different folder.
2) Generate resource file:
Resgen Labels.fr.resx TestResourceApp.App_GlobalResources.Labels.fr.resources
3) Generate satellite assembly:
AL /t:lib /embed:TestResourceApp.App_GlobalResources.Labels.fr.resources /out:french.dll /c:fr
4) Copy french.dll to TestResourceApp/bin/fr
I have uiculture set to auto in web.config and I have change the language on the browser.
I was able to use this page to solve some satellite assembly issues I was having. I'll throw in a few more things to check.
It's helpful to decompile the "neutral" assembly and see how it's put together. A tool like ILDASM.exe is helpful for this purpose. Once you get it decompiled, look through the text output for ".mresource", and you should see one with your naming. For example, if you add a resource to a Visual Studio project, they're named MyAssemblyName + ".Properties.Resources" + a language (if any) + ".resources" Examples:
MyAssembly.Properties.Resources.resources (neutral language)
MyAssembly.Properties.Resources.en-US.resources (English (US))
In my case, I had the file named properly, and in the appropriate folder (such as Bin\en-US). I was able to verify that much by using ProcMon.exe (by the SysInternals guys) and could see the worker process finding and reading in my DLL file (instead of just saying "PATH NOT FOUND"). However, it was not finding the resource by the name that it expected it to. That's when some disassembly helped to get to the bottom of the naming problem.
So, use ProcMon.exe to narrow down the kind of problem you might have. Hopefully that's helpful to someone.
It's complicated but here are a few tips for those who run into this problem:
Try to include the resx in the web project and let VS do the job for you.
Reflector is your friend. Compare satellite assemblies you created and those created by VS.
If you web app is targetting ASP.NET 2.0, you should use Resgex and AL that come with .net 2.0. Open the assemblies in Reflector and check the "references". It should reference mscorlib version 2.0.
If you deploy your web app using web deployment project, make sure the namespace for the resources in your satellite assemblies is correct. Again, compare with what VS creates. In my case, I used the wrong tool to generate the designer.cs file because I wanted them to be accessible from a different assembly. Make sure you are using GlobalResourceProxyGenerator. Otherwise, the namespaces won't match and the deployment code will not be able to find your resource. The namespace in the designer.cs should simply be "Resources", not "XXXX.App_GlobalResources"
Did you have set enableClientBasedCulture to true in globalization ?
I've a working XML Web service written in ASP.Net. Now I need to reference certain assemblies lying in a specific folder e.g. c:\NotMyCode
I do not want to copy/duplicate zillions of dlls in that folder into my bin folder.
I tried to keep the CopyLocal=false for the assemblies referred in the Web Service. That ended up in a FileNotFound exception for the assembly.
When I switch to CopyLocal=true, the referenced DLLs are copied over to the bin folder.. and it works.
So my question here is:
How do I reference assemblies that do not lie in my bin folder or a subfolder beneath it ? Do I need to change some security policy files somewhere? I think I'm not the first person to ever want to do something like this.. so I'm assuming someone has already solved this problem.
(I've tried impersonating an admin user in the IIS ASP.net configuration panel, but that didnt work either.)
Update: Can't install it in the GAC either. To give an analogy, this web service is giving a simplified view to a third party app e.g. Starteam. I can't (shouldn't have to.. don't want to..) copy all the binaries in that folder to the bin folder or install it into the GAC
According to the MSDN documentation,
Referenced assemblies outside the application's root directory must have strong names and must either be installed in the global assembly cache or specified using the <codeBase> element.
So, it might appear that you're out of luck. Here's what I'd try:
Create an NTFS junction point under your application base directory which points to the directory containing your shared code. This is the key step. For example, in your application base directory, run linkd SharedCode c:\NotMyCode. This makes <yourappbase>\SharedCode effectively be an alias for c:\NotMyCode.
Tell ASP.NET to probe for assemblies in this path, using a <probing> element, referencing the junction point SharedCode. Since this is under your application base, it should work. Alternatively, use AppDomainSetup.PrivateBinPath to set the path probed for assemblies.
I'm quite curious to see if this works :-)
You could always use the:
AppDomain.CurrentDomain.AssemblyResolve Event
and call
Assemly.Load(Path.Combine(#"c:\NotMyStuff",args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll"))
See link for more info.
You could always put your referenced assemblies in the GAC, then the location would not matter. You can install the component by dragging it into the GAC (C:\windows\assembly) or running GACUtil.exe.
This article may help. It describes the "Best Practices for Assembly Loading."
At first I thought you could used the <probing privatePath="bin\debug"/> element in the runtime/assemblyBinding in the web.config, but the probing will only allow you to specify subdirectories under the root location.
I think you can reference specific assembly paths in code as well.
AppDomain.CurrentDomain.AppendPrivatePath("C:\\NotMyCode");
Doing that in your Global.asax Application_Start should do the trick.
You can put in GAC and access it from there.
How can one specify the connection string in a config file of a class library and later modify this when used in a ASP.NET Web Application?
The Class library is a data access layer that has a Dataset connecting to a database based on a connection string specified in a config file (Settings.settings/app.config).
This class library is used in a web application where user inputs data and is written to the database using the DAL classes & methods exposed in the class library.
Now, I want to migrate this application from development environment to testing environment and later to production. The problem I'm facing is that after migrating to testing, the app in testing still connects to development database. I've changed the connection string mentioned in <class library>.dll.config file but this seems to have no impact.
Can someone explain the right way to achieve this? Thanks in advance for any help. Cheers.
With the .config files the name has to match the main executing assembly. For example I had a situation like yours, I needed a class library to have its settings in a .dll.config file. While it was able to reference it the actual application would not be able to read the config file because it was expecting .exe.config. Renaming the .dll.config to .exe.config fixed the problem.
In your case migrating your connection strings from .dll.config to web.config should fix your problem!
Good luck!
Joshua is partly right ... For posterity I would like to add a bit more to this answer as I have delt with the same problems on several occasions. First, one must consider their architecture. There are several issues you can run into with .config files in ASP.NET based on deployments.
Considering the architectural ramifications:
Single tier (one server):
A simple web application may be able to leverage a reference to the sites Web.config file and resolve your issues. This would be a fine solution for a single tier application. In the case of a windows application leveraged as a .exe file, the App.config will work too.
Multi-tier (more than one server):
Here is where things became a bit hairy for me the first time I was working with .config files across boundries. Remember the hierarchy of the config structure and keep this in mind (MSDN Article on .Config structure) - there is a machine.config at the root in the appropriate ASP.NET folder. These reside at each physical server. These are overridden by the site Web.config (or App.config) which are in turn overridden by subfolder .config files. If you have more than one .config file you may want to use one of the methods to pass the file path for the specific .config you want to use. More importantly, these files each may have connection information. ASP.NET's machine.config holds some for the framework ... so you should at least be senstive to the fact this is an "inheritance" chain. Second, any changes to the Web.config file once deployed will tell the application to restart. This will result in loss of state (bad if you have active users on the site). The way around this is to keep a separate .config file (e.g. connections.config) and put a reference to that file in the Web.config. This will allow you to change the connection information (e.g. password) without having to restart the application. Here is a link to more info: MSDN: Working with Configuration Files. This article lays out all the details you need to be aware of in a normal server / IIS deployed application. Keep in mind that the .config files are mainly intended for applications, not libraries. If you have several tiers, chances are you are using some communicaiton / messaging layer (e.g. WCF). This will have / allow its own Web.config. You can keep connection strings there (and encrypt them if needed), but better yet, put them in a second file referenced by the Web.config for manageability. One final point, if you are ever going to consider the cloud, .config files are wrapped for application deployments which in effect removes all of the benefits they offer in terms of "not having restart or redeploy". Azure deployments will want to consider this article to save themselves from nightmares of maintenance: Bill Lodin blog - Configuration files in Azul / Cloud. One other point on this article – great example on how to programmatically select configuration depending on deployment! Be sure to check that out if you want to add flexibility to deploy in or out of the cloud .
I hope these points saves all of you time and headaches. I know I lost a couple days of programming time dealing with these issues ... and it was hard to find all the reasons in one place why may app was not "implementing" its connection object. Hopefully this will save you all from the same fate I had.