Thursday, December 14, 2017

Merging a common project with build variations in .NET MVC

Leave a Comment

I have a .net mvc site that should be published to a lot of different customers, and thus vary slightly depending on the target.

Is there any way to set up the core project structure, e.g. (simplified):

  • views
  • models
  • controllers
  • assets
  • bin

and make a merge at build time with whatever variations the current target might have. For example:

core project:

  • views
    • view1.cshtml
    • view2.cshtml

(removed rest of the folders for brevity)

customer 1 target:

  • views
    • view2.cshtml
    • view3.cshtml

desired merge result:

  • views
    • view1.cshtml (from core project)
    • view2.cshtml (from customer 1 target)
    • view3.cshtml (from customer 1 target)

The same rule should apply to controllers, binaries etc.

6 Answers

Answers 1

I would either use multiple projects, nuget, or use source control. I will talk about these ideas below, but in no particular order. By the end I may include a preference towards one or the other.

First idea I'll talk about is to use multiple projects. Create your base project, let's call it WebBase. Create your core website that you have talked about. Next create Customer1 website, if you create Customer1 as an empty website you will need to recreate the folder structures in WebBase or you cane create it the same as you did with WebBase and remove all the files(do this from within Visual Studio); either way you end up with the folder structure with no files. I would recommend keeping web.config,packages.config, Properties folder with AssemblyInfo.cs. Then you will add the files from WebBase, but don't just add them normally. For illustration's purpose let's do the Home Index View: Expand the Views Folder in Customer1, Right click on home, choose add, choose Exising Item, browse out of Customer1 and then in to WebBase/Views/Home and single click index.cshtml, now notice the drop down on the button? click that and choose "Add as Link". Now do this for all the files in WebBase! It will seem cumbersome to choose "add as link" every time, but if you just click add, it will copy the file and updates won't propagate from WebBase to Customer1! Also, you will see things like remove from project vs delete (with prompt about permanent deletion).

Second thought would be to create a nuget package for WebBase and you can than install it as a package, the benefit of this approach would be versioning and it would not require you to update every project with each little change. It would keep each project completely isolated. Down side is you would need to update each project individually to propagate changes globally. You would need to learn about nuget's nuspec file and how to include files and such. It isn't that bad, it is just XML. But you can indicate which files to include when the package is installed/updated.

Third would be source control, I know with git you can use submodule to include a seperate project (even from external repository). Which might be an option, or you could just create a WebBase branch, setup WebBase and then branch it off into each Customer's website. So create a branch called Customer1, start adding the custom customer things. Switch back to WebBase, create a new branch called Customer2... you are off to the races. Switch back to WebBase, make a global change and then merge these changes into Customer1 and Customer2 branches.

Okay, I will admit it, I would probably go with the third option. It gives you lots of benefit with little downside. It even get gives you history! If you aren't currently using source control... you should! You can install git on your machine and have the ability to check code in locally and you don't have to worry about an external repository (although I would recommend you have one as it gives you DR).

Either way, there are options available. But nothing like a single project with configurable file includes.

Good luck!

Answers 2

First thing that comes in mind (and probably easiest because it does not require any additional tooling) is to create core project with core functionality, views and controllers. And for each customer create separate project with custom views and controllers. Then for customer-specific project simply link all required files from core project. It can be a little tedious to link the files depending on the number, but seems doable.

Another approach could be to use tools like CAKE or FAKE, with the help of which you can script the entire build process the way you want, but I never tried doing such custom scripting myself.

Third option that I can work as well is to conditionally include files based on defined constant, but that will require editing *.csproj files. The code can be something like:

<Content Include="Views\View1.cshtml" /> <Content Include="Views\View2.cshtml" Condition="$(DefineConstants.Contains('CORE'))" /> <Content Include="Views\View2.cshtml" Condition="$(DefineConstants.Contains('CUSTOMER1'))" /> <Content Include="Views\View3.cshtml" Condition="$(DefineConstants.Contains('CORE'))" /> <Content Include="Views\View3.cshtml" Condition="$(DefineConstants.Contains('CUSTOMER1'))" /> 

Not sure how easy it will be to maintain it though.

I would probably consider to split the application into independent components/projects that will contain all functionality related to the component. The during the build compose components with FAKE based on what components are needed for particular client.

Answers 3

Your requirement is a great candidate for a custom Visual Studio Project Template.

I am thinking of preparing one big project with all the features you're deploying to whatever customer. This project also could be the trunk that you might update when a new feature or a fix is needed. Then, export the trunk-solution into a template. Then continue with VSIX project template and incorporate a wizard into it, to collect user input on a project creation. Based on the input take the appropriate action and add/remove the necessary files or enable/disable features as needed.

Or you might just keep the source files on the file system and organize them into the template on the fly - i.e., as a result of the user input during the wizard. At the end of the wizard, the template is deployed and ... voila.

Answers 4

What you need is super-admin section, where you could [de]activate different portions of the site, depending on customer.

Answers 5

There are some really good code answers here but if you're wanting an Automated Build system for every client and you have loads they could be a pain too setup. When you could set up a script for Powershell that can do this

pseudo code

For each client site     download base code to code/     download this client's changes to code/ overwriting files     msbuild ....     copy client/ bin to build/client/     Delete code/ End For each 

Answers 6

The answer to this question requires some innovation. So look at my solution please:

Set up the core project structure for the classes that cover Model and Controller files and then use them with technique named Add Existing Item As a Link which can share the followings:

  • App logic common to both apps, but not portable
  • User controls with no platform dependencies

Unfortunately that is not supported for razor views. So the easiest thing to copy shared views is to have multiple layout files like famous _Layout.cshtml file, one in each web app. You can also Compile your asp.net mvc Razor views into a separate dll to use them like a Reference (as shared views).

For the Assets, You can host all your style sheets (and some javascript if appropriate) from your main web application and access it from each web app.

The bin folder has core MVC dll files plus those that you add for using in your project and a projectName.dll file which will be created after building. You can simply Add/remove them by right clicking on References using Add Reference tool.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment