Refactoring multiple instances of ecommerce software into single code base - asp-classic

Disclaimer, I am technical support and sysadmin for my company, not a developer. I'm not after the specifics, simply an idea if what I'd like to acheive is possible or not.
We host hundreds of instances of our in-house classic ASP legacy ecommerce software application and due to countless customisations by clients and ourselves, version management is nightmarish, custom code can't be managed and we've given up releasing new features and mass deploying bug fixes due to the inability to track who needs what patches where.
Parellel to this question I am making management scripts to better automate this though.
What however I'd really like to do is using the miniumum possible effort, port the application code (not the database) to a single code base. Questions I have:
Can ASP relatively effeciently handle connecting to different databases depending on the host header being called? I plan some basic extension to the routine, get hostheader
lookup up db credentials in metadb, set application connection string accordingly logic.
The application writes a few files to the webserver from the database for caching purposes, I'd like to handle this by emulating this behaviour by writing it to something like /masterapp/customer1/specificfile.htm then changing the references to /specificfile.htm in the code to more like /masterapp/shop name/specificfile.htm. Obviously the routines that write specificfile.htm would write to the new location accordingly. Does this seem reasonable?
Other webserver-bound store specific contents like images and csv files I need to keep working without URLs changing ideally, can ASP employ logic to redirect get requests for /images/example.jpg to either /masterapp/shop name/images/example.jpg or /shopname/images/example.jpg depending again on host header? Or could that be done via isapirewrite? (which we already use)
I think these are the biggest challenges. I don't need a complete project plan of how to implement each of these things, I just want to know if it's possible. If the answer is 'yes' I should be able to sell my bosses on the development due to saving support time and our in-house developers could hopefully manage this.

This should be possible and I have achieved similar outcomes with code developed that way from the start. As you are retrofitting this in it's going to be a lot harder, but that's separate to your actual question.
To answer your actual points:
Presumably your DB connection string is already in a application variable or settings file? If so, you just need some logic in your global.asa Session_OnStart that reads the host header and selects the appropriate DB string. This could be hard coded or you could have a "control" DB that stores sites, their DB strings file paths etc and pulls the details into the session object.
This is related to the above, pull your cache storage locations from the DB, or have a "directory name friendly" base name for each site, so you can have "/masterapp/" & Session("strSiteBaseName") & "/cache/somefile.htm"
If you're on IIS7 then you can use the URL rewrite module to handle this, if you're on IIS6 there are 3rd party tools you can get to do URL rewriting for you. Again I have done this so can vouch for it working. If you want to get really clever, you can have your master app create the rewrite files for you and "touch" web.config to get them loaded into IIS.
One "gotcha" you'll have with host headers is remember to handle www and no-www records!
You mentioned custom code as well for each site, I haven't done this in production but have tested outside an app and you can rewrite functions after they've already been declared. You can't have includes with variable names, but you can load in a text file and execute it, so there is a way to have custom functions, or changed core functions specific to an individual instance of your over-arching app.

Related

Is it possible to use Kentico's staging API to pull serialized object information from a target server?

We have a large, complex Kentico build which uses Kentico's Continuous Integration locally, and Kentico's Staging module to push Kentico object changes through various environments.
We have a large internal dev team and have found that occasionally (probably due to Git merging issues) certain staging tasks aren't logged. When dealing with large deployments this is often not obvious until something breaks on the target server.
What I'd like is to write a custom module which can pull certain data from a target server (e.g. a collection of serialized web parts). I can then use this to compare with the source server to identify where objects are not correctly synchronized. I'd hoped this might be possible using the web services already exposed by Kentico which handle the staging sync tasks.
I've been hunting through a few namespaces in the Kentico API (CMS.Synchronization, CMS.Synchronization.WSE3 etc.) but it's not clear if what I'm trying to do is even possible. Has anyone tried anything similar. If so, could you point me in the right direction?
Instead of writing your own code/tool for this I'd suggest taking advantage of what someone else has already done. This is like Red Gate's SQL Compare for Kentico BUT on steroids. It compares, database data, schema AND file system changes on staging and target servers.
Compare for Kentico

How to separate configurations in ASP.NET?

My team is doing web development (ASP.NET, WCF), and we are at a beginning stage where everyone needs to make DB changes and use own sample data.
We use a dedicated DB server, and we want each developer to develop against separate DB.
What we appear to need is ability to configure connection string on per-developer basis in source controlled way. Obviously, we might have other configuration settings that need custom setting and finally, we'll need to maintain a set of configuration settings that are common to all developers.
Can anyone suggest a best practice here?
PS Similar issue appears when we want to deploy a built application to different environments (test, stage, production) without having to manually tweak configurations (except perhaps configuring the environment name).
You can use config transforms for your deployment to different environments. That's easy enough. Scott Hanselman did a pretty awesome video on it here.
For your individual developer db problem, there isn't any particularly elegant solution I can think of. Letting each developer have a unique configuration isn't really a "best practice" to begin with. Once everyone starts integrating their code, you could have a very ugly situation on your hands if everyone wrote their code against a unique db and configuration set. It almost guarantees that code won't perform the same way for two developers.
Here is what I would recommend, and have done in the past.
Create a basic framework for your database, on one database on your test db server.
Create a Database Project as part of your solution.
Use .Net's built in Schema Compare to write your existing database to the database project.
When someone needs to change the database, first, they should get latest on the Database project, then make their changes, and then repeat step 4 to add their changes to the project.
Using this method, it is also very easy for developers to deploy a local instance of the database that matches the "main" database, make changes, and write those changes back to the project.
OK.
Maybe not so elegant solution, but we've chosen to read connection string from a different place when the project is built using Debug configuration.
We are using registry, and it has to be maintained manually.
It requires some extra coding, but the code to read the registry is only compiled in debug (#if debug), so there is no performance hit in production.
Hope this helps as well.
Cheers
v.

Is it commonplace/appropriate for third party components to make undocumented use of the filesystem?

I have been utilizing two third party components for PDF document generation (in .NET, but i think this is a platform independent topic). I will leave the company's names out of it for now, but I will say, they are not extremely well known vendors.
I have found that both products make undocumented use of the filesystem (i.e. putting temp files on disk). This has created a problem for me in my ASP.NET web application as I now have to identify the file locations and set permissions on them as appropriate. Since my web application is setup for impersonation using Windows authentication, this essentially means I have to assign write permissions to a few file locations on my web server.
Not that big a deal, once I figured out why the components were failing, but...I see this as a maintenance issue. What happens when we upgrade our servers to some OS that changes one of the temporary file locations? What happens if the vendor decides to change the temporary file location? Our application will "break" without changing a line of our code. Related, but if we have to stand this application up in a "fresh" machine (regardless of environment), we have to know about this issue and set permissions appropriately.
Unfortunately, the components do not provide a way to make this temporary file path "configurable", which would certainly at least make it more explicit about what is going on under the covers.
This isn't really a question that I need answered, but more of a kick off for conversation about whether what these component vendors are doing is appropriate, how this should be documented/communicated to users, etc.
Thoughts? Opinions? Comments?
First, I'd ask whether these PDF generation tools are designed to be run within ASP.NET apps. Do they make claims that this is something they support? If so, then they should provide documentation on how they use the file system and what permissions they need.
If not, then you're probably using an inappropriate tool set. I've been here and done that. I worked on a project where a "well known address lookup tool" was used, but the version we used was designed for desktop apps. As such, it wasn't written to cope with 100's of requests - many simultaneous - and it caused all sorts of hard to repro errors.
Commonplace? yes. Appropriate? usually not.
Temp Files are one of the appropriate uses IMHO, as long as they use the proper %TEMP% folder or even better, use the integrated Path.GetTempPath/Path.GetTempFileName Functions.
In an ideal world, each Third Party component comes with a Code Access Security description, listing in detail what is needed (and for what purpose), but CAS is possibly one of the most-ignored features of .net...
Writing temporary files would not be considered outside the normal functioning of any piece of software. Unless it is writing temp files to a really bizarre place, this seems more likely something they never thought to document rather than went out of their way to cause you trouble. I would simply contact the vendor explain what your are doing and ask if they can provide documentation.
Also Martin makes a good point about whether it is a app that should run with Asp.net or a desktop app.

Single ASP.net site with Multiple Instances & web.configs

We have a legacy ASP.net powered site running on a IIS server, the site was developed by a central team and is used by multiple customers. Each customer however has their own copy of the site's aspx files plus a web.config file. This is causing problems as changes made by well meaning support engineers to the copies of the source aspx files are not being folded back into the central source, so our code base is diverging. Our current folder structure looks something like:
OurApp/Source aspx & default web.config
Customer1/Source aspx & web.config
Customer2/Source aspx & web.config
Customer3/Source aspx & web.config
Customer4/Source aspx & web.config
...
This is something I'd like to change to each customer having just a customised web.config file and all the customers sharing a common set of source files. So something like:
OurApp/Source aspx & default web.config
Customer1/web.config
Customer2/web.config
Customer3/web.config
Customer4/web.config
...
So my question is, how do I set this up? I'm new to ASP.net and IIS as I usually use php and apache at home but we use ASP.net and ISS here at work.
Source control is used and I intend to retrain the support engineers but is there any way to avoid having multiple copies of the source aspx files? I hate that sort of duplication!
If you're dead-set on the single app instance, you can accomplish what you're after using a custom ConfigurationSection in your single web.config. For the basics, see:
http://haacked.com/archive/2007/03/12/custom-configuration-sections-in-3-easy-steps.aspx
http://msdn.microsoft.com/en-us/library/2tw134k3.aspx
Example XML might be:
<YourCustomConfigSection>
<Customers>
<Customer Name="Customer1" SomeSetting="A" Another="1" />
<Customer Name="Customer2" SomeSetting="B" Another="2" />
<Customer Name="Customer3" SomeSetting="C" Another="3" />
</Customers>
</YourCustomConfigSection>
Now in your ConfigSection Properties, expose Name, SomeSetting, and Another. When the Property is accessed or set, use a condition (request domain or something else that uniquely identifies the Customer) to decide which to use.
With the proper implementation, the app developers don't need to be aware of what's going on behind the scenes. They just use CustomSettings.Settings.SomeSetting and don't worry about which Customer is accessing the app.
I know it might seem annoying, but the duplication is actually a good thing. The problem here is with your process, not with the way the systems are setup.
Keeping the sites separate is actually a good thing. Whilst it looks like "duplication" it's actually not. It's separation. Making changes in the production code by your support engineers should be actively discouraged.
You should be looking at changing your process to change once deploy everywhere. This will make everything a lot easier for you in the long run.
To actually answer your question, the answer is no, you can't do it. The reason is that web.config isn't designed to store user level settings, it's designed to store per application instance settings. In your case, you need an application instance per user which means separate config files.
For your system to work, you need to be able to preemptively tell the application which config file to use, which isn't possible without some sort of input from the user.
Use an external source control application and keep rolling out updates as required.
It isn't really a good idea to let your live site be updated by support engineers in real time anyway.
Depending on what is actually in the web config, and what settings differ between customers, you could opt to use a single web config, and store other customer specific configuration options in a database or some other custom xml/text file. As long as the specific customer settings in the web.config don't have to do anything with how IIS operates, and you are just using it to store values, then this solution might work out well for you.
Thank you all again for your answers. After reading through them and having a think what I think I will do is leave the multiple instances alone for now and I will try to improve our update process first. then I will develop a new version of the application that has the user configuration information in the database layer and then pick the user based on the request domain or URL as someone suggested. That way I can have a single application instance supporting multiple different client configurations cleanly.
As most of the client configuration data is really presentation or data source related, nothing complicated. I think we ended up with multiple application instances mostly because the original programmer hadn't been expecting multiple customers and didn't design for that so when someone came along later and added a second customer they just duplicated the application which is wasteful as each instance is about 99.99% identical to the original.
I am implementing this as we speak.
In the main web.config, I have 1 item per installation. It points me toward the custom config file I built for each client (and toward the custom masterpage, css, images, etc).
Using WebConfigurationManager.OpenWebConfiguration, I open the new webconfigs in their subdirectories. I determine which one to use by using System.Web.HttpContext.Current.Request.Url.OriginalString, and determining the uRL that called me. Based on that URL, I know which web.config to use.
From that point forward the clients all use the same codebase. They have their own databases too.
The idea of having to update 30-40 installations when we make an update scares the death out of me. We do not want to support 30-40 codebases, so there won't be customization beyond the master page, css, and images.
I wrote a custom class lib that knows how to switch to the proper webconfig, and read the custom section I built with all our settings.
The only issue I have now is the FormsAuthentication Cookie. I need to be able to switch that as well. Unfortunately, the property for the name is read only
If I understand correctly, it sounds like you have multiple deployments (one for each client) where the only difference is the web.config, right?
First off, although I don't know your unique situation, I would generally urge you to stay with separate installs. It usually allows much more flexibility. Off the top of my head: are you ever going to have customizations, or different clients running different versions? Are you sure? The easiest way to stay flexible here is to keep going with separate installs.
In my opinion, it isn't ugly at all if your practices are aligned properly. Based on some things you mentioned, you have trouble in that area - obviously, possible source control buy-in/training issues. But you are aware of that. I would also take a hard look at your deployment procedures and so on. I have a feeling you might have further issues in that area, and I mean absolutely no offense.
That said, let's say you want to move forward with this.
You didn't say whether all the clients share a single common database, but I'm thinking no, since designing that type of system is often not worth the extra complexity (which can be severe in systems of any size) so people often opt to keep them separate.
What that means is that you have store your connection string somewhere. Usually that would be web.config... So that seems to break our plan.
Really, the apparent elegance of this situation is almost always wildly offset by the challenges it introduces. If I thought about it hard enough, I could maybe find a way around this by introducing another database that intelligently manages connection strings or maybe delving into keeping all your login info directly in web.config (which is possible but... not ideal), however my gut says the work will be wasted because some day you will end up going back to how you're doing it now.
Also: changing code directly in production is obviously not the best practice here. But you if you are on a monolithic shared platform with any amount of traffic, that can never ever ever happen. Food for thought.
Let me know if I'm missing something!

Do these values belong in a config file or database?

There are multiple values I have been storing in ASP.NET configSections sections for each "module". I have been wondering if they even belong in these files at all.
The background stands at: These are multiple instances of the web application deployed. All use the same database but have their own settings.
I'm sure that differences between development and production go in the config files. Some of the values I know should include: connection strings, providers to use, setting debug, etc.
I have factored out all the common pieces in to classes with their own rules and methods. The pieces left are miscellaneous settings for each module in each site. Some of the options I'm unsure of include:
For ModuleA, Show/Hide Option
For ModuleB, What is the terminology to be used for this field
For ModuleC, Allow end user to perform X action
Mmm these sounds like things that you might want be able to change at runtime for your application without having to modify the app.config. One rule of thumb I like to follow is that anything in the config should be for the deployment or server configuration. In this case your settings appear to be modifying the application behaviour and so I would probably move them into the DB if it is not alot of effort.
ModuleA and ModuleC sound like they may be user profile information. If they're not dynamic by user, but you could add later functionality, then maybe move them to a DB.
I've written apps where ModuleB would have been put in a DB also. Things like form labels, etc. can easily go in a DB. If, at a later date, someone decides to add or remove colons to all form labels, that's a pretty easy thing to do if all of the text is stored in a DB.
Consider the situation when you need to edit one of the values.
If the value is in web.config, saving the change to that file will cause the app to recycle, inconveniently throwing out current users. Not so much of a problem if your app is on an intranet only used during business hours (though you could get an angry call from the guy who stayed to work late). But potentially a problem on a public website with international users.
If the value is in a database, it will have no impact on the app's processing in that way.
Either way, consider whether the values are cached in the app's RAM (web.config is). Are the database values in an Application variable, or in Cache? If so, you may not know when the change will occur. Unless you want to restart the app.
And, what different access and permissions would the appropriate admins need to make the changes? Someone would have to have access to the web server(s) to change web.config, or to the database (and table) to change that.
A few questions: Why do you use the same DB for multiple instances of the application, and how will that effect maintanence?
In the future will it be an option to split the db in order to improve performance? Does the config model support that change better then the DB based one?
In other words, you will have to consider a lot variables in order to answer your question :-)

Resources