Using Flutter flavors to separate the DEV and LIVE environment

These are the requirements for our app:

  • Our Flutter app should target iOS and Android.
  • We want a DEV version and a LIVE version of our app, each targeting a different API URL.
  • Developers should never have to manually change any code to switch between the environments.
  • We want to be able to have the DEV app and the LIVE app installed on the same device at the same time.

The best way to solve these requirements in Flutter is to use flavors. There are also some other tutorials for this linked on the official Flutter docs which might be helpful.

The code for this guide is stored on GitHub and changes for each section are separate commits that are linked in the section below.

As we need to change settings in XCode, we need a Mac with Android Studio and XCode for this tutorial.

You also need to have Flutter installed already. Follow the Getting started-docs if you still need to install it.

I’m using Flutter v1.22.0 for this tutorial.

Create the Git repository and clone it locally

Create a new Git repository. Mine is: https://github.com/cwe1ss/flutter-flavors-ci-cd

git clone https://github.com/cwe1ss/flutter-flavors-ci-cd.git
cd flutter-flavors-ci-cd/

Create the Flutter app

Let’s get started by creating the Flutter app, named flutter_flavors via the Flutter CLI directly in the root folder of our repository:

flutter create --project-name flutter_flavors .

Run the app on an Android device/emulator to ensure it works.

See all changes from this step in the Git commit.

Add a Flutter build configuration for each flavor in Android Studio

We want to have two flavors called dev and live.

If you want to launch a flutter app with a flavor, you have to use the --flavor NAME parameter in the Flutter CLI. To automatically start the app with a flavor in Android Studio we need to change the build configurations:

  • Find main.dart in the Android Studio top toolbar and select Edit Configurations.... This opens the “Run/Debug Configurations” window.
  • Change the Name: field to dev
  • For Build flavor: set dev as well.
  • Make sure “Share through VCS” is selected.
  • Copy the dev configuration (It’s an icon in the top left of the window)
  • Change the Name: and Build flavor: values to live
  • Make sure “Share through VCS” is selected as well
  • Close the dialog. Instead of “main.dart”, it will now display “dev” in the top toolbar.

IMPORTANT: Flavor names may not start with “test” as that’s not allowed by Android.

Add the build configurations to Git

When you select “Share through VCS”, Android studio will create files in the .idea/runConfigurations folder, however they’ll be ignored by the existing .gitignore file.

We’ll therefore add these files manually to Git, so that other users in the team can use it as well:

git add .idea/runConfigurations/dev.xml -f
git add .idea/runConfigurations/live.xml -f
git commit -m 'Persist flutter build configurations'

See all changes from this step in the Git commit.

Set up flavors for Android

In order to actually use different flavors, we need to set them up in the lib-folder and in each platform (android, ios).

We’ll start with the android part.

Add the method channel in Android code

When the app starts, Flutter needs a way to ask the native platform which flavor it has been started with. To communicate with native code, Flutter uses method channels.

Go to android/app/src/main/kotlin/com.example.flutter_flavors and replace everything except the first line (the package import) with the following code. This will set up the method channel that returns the BuildConfig.FLAVOR value, which is a built-in value of Android.

import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugins.GeneratedPluginRegistrant

class MainActivity: FlutterActivity() {
    override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        GeneratedPluginRegistrant.registerWith(flutterEngine);

        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "flavor").setMethodCallHandler {
            call, result -> result.success(BuildConfig.FLAVOR)
        }
    }
}

Add the flavor-settings to the Android build config

In Android, the native flavor-specific values are stored in android/app/src/build.gradle via the android.flavorDimensions and android.productFlavors keys.

We’ll use these keys to set up the flavor-specific applicationId and the flavor-specific display name for the app. This is important because we want to be able to have both flavors of the app installed at the same time.

The applicationId is the unique app id for each flavor in the Google Play store. Once deployed to Google Play, this can not be changed anymore!

Therefore, add the following two things within the android { ... } section:

android {
    // ... all existing things like `sourceSets`, ...

    flavorDimensions "app"

    productFlavors {
        dev {
            dimension "app"
            applicationId "at.chwe.flutterflavors.dev"
            resValue "string", "app_name", "DEV Flutter Flavors"
        }
        live {
            dimension "app"
            applicationId "at.chwe.flutterflavors"
            resValue "string", "app_name", "Flutter Flavors"
        }
    }
}

Use the app_name in the AndroidManifest.xml

The applicationId is a well-known key that will already be used when the app is launched with a given flavor.

However, we’ll need to do some more work to get the app_name working: Open android/app/src/main/AndroidManifest.xml and replace the <application android:label="flutter_flavors" /> key with <application android:label="@string/app_name" />.

Run the app again on Android

As we’re now using new applicationIds, make sure the existing “flutter_flavors”-app is uninstalled from your device.

Now, launch the app in Android Studio with the “dev” build configuration.

Close the app in the device and check your application list, the app name will now display “DEV Flutter Flavors”!

Stop the app in Android Studio, change the build configuration to “live” and launch the app again.

You’ll now have both flavors of your app installed on your Android device!

Both flavors are installed on android

Our native Android configuration is now complete.

See all changes from this step in the Git commit.

Get the flavor in our Flutter code

As described in our requirements, we want to target different API endpoints per flavor so we need a way to get the current flavor in our Flutter code.

We’ll first add a class called FlavorSettings in a new file called lib/flavor_settings.dart that will hold all of our flavor-specific settings that we only need in our Flutter code:

/// Contains the hard-coded settings per flavor.
class FlavorSettings {
  final String apiBaseUrl;
  // TODO Add any additional flavor-specific settings here.

  FlavorSettings.dev()
    : apiBaseUrl = 'https://dev.flutter-flavors.chwe.at';

  FlavorSettings.live()
    : apiBaseUrl = 'https://flutter-flavors.chwe.at';
}

Next, we’ll use this in main.dart, where we’ll read the flavor via our method channel from the native platform and we’ll create the corresponding FlavorSettings-object. We’ll also have to make our main-method async for that.

When done, your main.dart should contain the following code before the class MyApp extends StatelessWidget { line:

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import 'flavor_settings.dart';

Future<void> main() async {
  // NOTE: This is required for accessing the method channel before runApp().
  WidgetsFlutterBinding.ensureInitialized();

  final settings = await _getFlavorSettings();
  print('API URL ${settings.apiBaseUrl}');

  runApp(MyApp());
}

Future<FlavorSettings> _getFlavorSettings() async {
  String flavor = await const MethodChannel('flavor')
        .invokeMethod<String>('getFlavor');

  print('STARTED WITH FLAVOR $flavor');

  if (flavor == 'dev') {
    return FlavorSettings.dev();
  } else if (flavor == 'live') {
    return FlavorSettings.live();
  } else {
    throw Exception("Unknown flavor: $flavor");
  }
}

// ... class MyApp extends StatelessWidget {

Run the app with dev build configuration and look at the console output. It will display the following statements:

I/flutter ( 4458): STARTED WITH FLAVOR dev
I/flutter ( 4458): API URL https://dev.flutter-flavors.chwe.at

Switch to the live build configuration and run your app again. This time, the console will display the following statements:

I/flutter ( 4615): STARTED WITH FLAVOR live
I/flutter ( 4615): API URL https://flutter-flavors.chwe.at

That’s it! We can now access the current flavor from within Flutter and we can have flavor-specific settings.

You can pass the FlavorSettings-instance down to your widgets manually, or you can use e.g. the provider-package to access it via dependency injection in your widgets.

See all changes from this step in the Git commit.

Set up flavors for iOS

Unfortunately, setting up flavors in iOS is more complex and we’ll have to use XCode and its UI for most of the steps.

Let’s try building our app with a flavor for iOS now to see, what kind of error we get:

flutter build ios --flavor dev

The Xcode project does not define custom schemes. You cannot use the --flavor option.

This means, that on iOS we have to rely on a feature called “custom schemes” to represent our flutter flavors. Setting them up requires multiple steps.

Create custom build configurations for the flavors

Let’s open our ios-folder in XCode and start by creating our custom build configurations:

  • Make sure the root “Runner” node is selected in XCode
  • In the main window, select the “Runner” node below “PROJECT” (NOT below TARGETS)
  • Select the “Info” tab
  • In the “Configurations” section, do the following:
    • Rename “Debug” to “Debug-dev”
    • Rename “Release” to “Release-dev”
    • Rename “Profile” to “Profile-dev”
    • Duplicate “Debug-dev” and rename it to “Debug-live”
    • Duplicate “Release-dev” and rename it to “Release-live”
    • Duplicate “Profile-dev” and rename it to “Profile-live”

This means, for every flavor, we need a separate “Debug”, “Release” & “Profile” configuration.

Configurations in XCode

Assign build configurations to custom schemes

Now we can set up the actual “custom schemes” by doing the following:

  • Make sure the root “Runner” node is selected in XCode
  • Select “Product -> Scheme -> Manage Schemes…” in the main toolbar.
  • To get the “dev” scheme:
    • Select the “Runner” scheme, click on the settings-icon in the top left and select “Duplicate”
    • Rename the scheme to “dev”
    • Make sure “Shared” is selected
    • Close the dialog
  • To get the “live” scheme:
    • Select the “Runner” scheme again, click on the settings-icon in the top left and select “Duplicate”
    • Rename the scheme to “live”
    • For each of the sections (“Run”, “Test”, “Profile”, “Analyze”, “Archive”) on the left, change the build configuration to the corresponding “-live” version.
    • Make sure “Shared” is selected
    • Close the dialog

live scheme in XCode

Back in the “schemes” list, you can now delete the existing “Runner” scheme. This should result in the list looking like this:

scheme list in XCode

Adding the method channel for iOS

Building the app now shoud succeed, however when you try to run it, it will fail with the following error:

[VERBOSE-2:ui_dart_state.cc(177)] Unhandled Exception: MissingPluginException(No implementation found for method getFlavor on channel flavor)

That’s because we haven’t yet implemented the method channel that Flutter uses to get the current flavor from the native platform.

To add this, we need to add some code to the application()-function of the Runner/AppDelegate.swift-file in XCode. The finished file should look like this:

import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)

    let controller = window.rootViewController as! FlutterViewController

    let flavorChannel = FlutterMethodChannel(
        name: "flavor",
        binaryMessenger: controller.binaryMessenger)

    flavorChannel.setMethodCallHandler({(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
        // Note: this method is invoked on the UI thread
        let flavor = Bundle.main.infoDictionary?["App - Flavor"]
        result(flavor)
    })

    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

This will set up a method channel handler that reads the current flavor from a Bundle.main.infoDictionary with a key called App - Flavor.

Set up the flavor value per scheme

The Bundle.main.infoDictionary from before refers to the Runner/Info.plist file and App - Flavor is a custom key that we have to add there manually next.

So open the Runner/Info.plist file in XCode and and add a new row with the following settings:

  • Key: App - Flavor
  • Type: String
  • Value $(APP_FLAVOR)

We now have the key but we still don’t have the actual flavor-specific values per scheme. To add them, we now have to do the following:

  • Select the root “Runner” node in your XCode project structure
  • Select “Runner” below TARGETS
  • Select the “Build settings” tab
  • Click on the + to add a new User-defined setting
  • Name it APP_FLAVOR
  • Expand the node by clicking on the little arrow on the left of the row and add the actual flavor value to each build configuration:
    • Debug-dev: dev
    • Debug-live: live
    • Profile-dev: dev
    • Profile-live: live
    • Release-dev: dev
    • Release-live: live

When done, it should look like this: flavor setting in XCode

Run the iOS app

We should now be able to select the “dev”-scheme in the top navigation bar of XCode and run the app.

NOTE: If you get weird build errors from XCode, try switching between the dev/live schemes or try restarting XCode or running the iOS app from Android Studio.

You should now see similar console output like for the Android app:

2020-10-03 14:44:05.525493+0200 Runner[26055:336596] flutter: STARTED WITH FLAVOR dev
2020-10-03 14:44:05.526672+0200 Runner[26055:336596] flutter: API URL https://dev.flutter-flavors.chwe.at

See all changes from this step in the Git commit.

Great, we now have set up our flavors for iOS as well!

Set the bundle id and app name per flavor for iOS

You might have noticed that the app name on the iPhone still is “flutter_flavors”. Also, when you run both flavors, you still only have one app on your phone:

wrong app name on iPhone

Remember, that for Android we’ve set those values in the build.gradle file.

To make things flavor-specific in iOS, we need to do something similar like we’ve done for the flavor value itself, where we’ve configured a key in Info.plist and then set different values in the “TARGETS/Runner -> Build Settings” tab.

Set the flavor-specific bundle identifier

The Info.plist file already contains a key named Bundle identifier that already contains a dynamic value $(PRODUCT_BUNDLE_IDENTIFIER), so we don’t have to create another entry in this file.

Instead, we just have to modify this key in the the build settings:

  • In XCode, select the root “Runner” node in the project explorer
  • Select “Runner” below TARGETS
  • Go to the “Build Settings” tab
  • In the “Packaging” section, find the “Product Bundle Identifier” key
  • Expand the key by clicking on the small arrow on the left
  • Set the value per build configuration:
    • Debug-dev: at.chwe.flutterflavors.dev
    • Debug-live: at.chwe.flutterflavors
    • Profile-dev: at.chwe.flutterflavors.dev
    • Profile-live: at.chwe.flutterflavors
    • Release-dev: at.chwe.flutterflavors.dev
    • Release-live: at.chwe.flutterflavors

bundle id per ios config

Set the app name

To have separate display names per flavor, do the following:

  • In XCode, select the root “Runner” node in the project explorer
  • Select “Runner” below TARGETS
  • Select the “Info” tab
  • Change the value of the key Bundle name to $(APP_NAME).
  • Go to the “Build Settings” tab
  • Add a new User-Defined setting
  • Name it APP_NAME
  • Expand the APP_NAME-node by clicking on the small arrow on the left side of the node.
  • Set the value per build configuration:
    • Debug-dev: DEV Flutter Flavors
    • Debug-live: Flutter Flavors
    • Profile-dev: DEV Flutter Flavors
    • Profile-live: Flutter Flavors
    • Release-dev: DEV Flutter Flavors
    • Release-live: Flutter Flavors

app name per ios config

Run the app again with the dev and live flavors

Delete the existing “flutter_flavors” app from your iPhone and run it again with each flavor. You should now have both apps with the correct name on your phone:

app icons on the iPhone

See all changes from this step in the Git commit.

Add your own flavor-specific settings

If you need another flavor-specific setting, you have to know if it is a platform-specific setting, that needs to be integrated directly into the android and ios folders or if it is a setting that is only required in your Flutter code.

For platform-specific settings, the above guides for setting the app name and bundle id should help.

For settings that you only need in your Flutter-code, just add them to the FlavorSettings-class that we’ve created above.

Summary

We’ve now set up our Flutter project to have multiple flavors. We use those flavors to separate our app environments (DEV & LIVE). This way we don’t need to e.g. manually comment out code to switch our API URL or any other settings. We can also have both versions installed side by side, which makes development and support much easier as we can develop on the DEV version while we still can use the LIVE version which is deployed to the stores.

What’s next

I’d like to add a guide that shows how to set up Firebase for each flavor. I’d also like to add another guide that sets up automated builds and single-click deployments to the Google/Apple stores via Azure Pipelines.

Hopefully, it won’t take as long as the time between my last blog post and this one.

Read More

Find projects which are missing in your "All Projects" solution

Do you use a Visual Studio solution which contains all of your projects to do daily builds? If you have lots of projects and if many people are involved it’s very likely that somebody forgets to add his project to this solution.

This small program helps you by showing you all csproj-files that are not part of your solution file!

class Program
{
    static void Main(string[] args)
    {
        // Parameters
        string baseFolder = @"C:\path\to\solution\";
        string slnFile = "AllProjects.sln";
        string outputFile = "MissingProjects.txt";

        string slnContent = File.ReadAllText(Path.Combine(baseFolder, slnFile));

        string[] projectFiles = Directory.GetFiles(baseFolder, "*.csproj", SearchOption.AllDirectories);

        List<string> missingProjects = projectFiles
            .Where(fullPath => slnContent.IndexOf(fullPath.Replace(baseFolder, ""), StringComparison.OrdinalIgnoreCase) < 0)
            .ToList();

        File.WriteAllLines(outputFile, missingProjects);

        Console.WriteLine("Projects missing in solution: " + missingProjects.Count);
        Console.WriteLine("Details: " + outputFile);
        Console.ReadLine();
    }
}
Read More

Feature Folders: Controllers and Views

The first step in our process to a better folder structure for our MVC projects is to make sure, MVC can resolve our Controllers and Views. This is our target structure:

  • (Project Root)
    • Areas
      • (AreaName)
        • (FeatureName)
          • (FeatureName)Controller.cs
          • Index.cshtml
          • Edit.cshtml
        • … (other features)
        • Shared
          • … (area specific shared views like EditorTemplates, Layout-pages, …)
      • … (other areas)
      • Shared
        • … (area independent shared views like EditorTemplates, Layout-pages, …)
    • Features
      • (Feature2Name)
        • (Feature2Name)Controller.cs
        • Index.cshtml
        • Edit.cshtml
      • … (other features)
      • Shared
        • … (feature independent shared views like EditorTemplates, Layout-pages, …)

Of course, if you don’t want to use “areas” you only need the “Features” folder in your project. This also means, that if you move to this new structure, you can completely remove the old “Controllers” and “Views” folders.

Controllers

To support this structure for Controllers, you don’t have to change anything in MVC since it does not force you to place them in a special folder! You can put Controllers into whatever folder you want. Resolving them is purely depended on your RouteConfig.

Views

To support this structure for Views, you have to create a custom ViewEngine. As you can see in the following example, this can also be done very easily. Please note, that this code only supports *.cshtml-files. If you want to use *.vbhtml-files as well, you just have to duplicate the paths and change the extension to *.vbhtml.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
    public class FeatureFolderViewEngine : RazorViewEngine
    {
        public FeatureFolderViewEngine()
        {
            // {0} ActionName
            // {1} ControllerName
            // {2} AreaName

            AreaViewLocationFormats = new[]
                                    {
                                        "~/Areas/{2}/{1}/{0}.cshtml",
                                        "~/Areas/{2}/Shared/{0}.cshtml",
                                        "~/Areas/Shared/{0}.cshtml",
                                    };

            AreaMasterLocationFormats = new[]
                                        {
                                            "~/Areas/{2}/{1}/{0}.cshtml",
                                            "~/Areas/{2}/Shared/{0}.cshtml",
                                            "~/Areas/Shared/{0}.cshtml",
                                        };

            AreaPartialViewLocationFormats = new[]
                                            {
                                                "~/Areas/{2}/{1}/{0}.cshtml",
                                                "~/Areas/{2}/Shared/{0}.cshtml",
                                                "~/Areas/Shared/{0}.cshtml",
                                            };

            ViewLocationFormats = new[]
                                {
                                    "~/Features/{1}/{0}.cshtml",
                                    "~/Features/Shared/{0}.cshtml",
                                };

            MasterLocationFormats = new[]
                                    {
                                        "~/Features/{1}/{0}.cshtml",
                                        "~/Features/Shared/{0}.cshtml",
                                    };

            PartialViewLocationFormats = new[]
                                        {
                                            "~/Features/{1}/{0}.cshtml",
                                            "~/Features/Shared/{0}.cshtml",
                                        };

            FileExtensions = new[]
                            {
                                "cshtml",
                            };
        }
    }

Of course, if you use this new structure, you lose some of the built-in templating- and navigation-support in Visual Studio since VS does not recognize these folders as “Views”-folders. Therefore, the following things no longer work:

  • “Go To View” throws an error.
  • “Add View” adds the view to the old “Views”-folder.

Fortunately, ReSharper helps you with these issues since it contains built-in templates for views and also supports our custom ViewEngine!

Read More

Introducing the ASP.NET MVC “Feature Folders” Project Structure

What’s the problem with the default ASP.NET MVC folder structure?

Which of these requirements is more common?

  • Change something in every view, controller or model of your project
  • Add a new field to your model X, show this field to the user, make it editable, the value must be validated against some fancy rules, …

I guess we are on the same page if we see the second one as more common. I would go as far as to say that if you have the first requirement you’re either working on a major relaunch or you’re not using layout pages, css, abstract classes, [insert random reusability gadget here] correctly.

By default, the ASP.NET MVC project structure advices you to keep every concept in its own area – you therefore might end up with a structure like this:

  • Content
    • CustomerImages
      • AnIconSpecialToCustomers.png
    • Customers.css
  • Controllers
    • CustomersController.cs
  • Models
    • Customer.cs
  • Repositories
    • CustomerRepository.cs
  • Scripts
    • Customers.js
  • Views
    • Customers
      • Create.cshtml
      • Index.cshtml
  • ViewModels
    • Customers
      • IndexViewModel.cs
      • CreateViewModel.cs

As soon as you have more than 3 controllers, this becomes hard to navigate. ASP.NET MVC’s solution for having a better structure is to use “Areas”, however in my opinion they do not solve the problem I’m talking about. To complete the second requirement I’ve mentioned, you still have to navigate through many folders, because most probably, you don’t have a distinct views-guy, a distinct model-guy, a distinct controller-guy, … in your company. It’s a lot more common that e.g. only one or two people are working on all of these mentioned files.

Grouping files by feature

When I’m talking about a feature, I understand it as a sum of files that are needed to create a user benefit. Therefore, with structuring files by feature, the project structure could look like this:

  • Customers
    • Images
      • AnIconSpecialToCustomers.png
    • Create.cshtml
    • CreateViewModel.cs
    • Customer.cs
    • CustomerRepository.cs
    • Customers.css
    • Customers.js
    • CustomersController.cs
    • Index.cshtml
    • IndexViewModel.cs

Think again of our second requirement and of some of the advantages with this structure:

  • You immediately get an overview about how the feature might be implemented.
  • You immediately see which files might be affected by the requirement. You don’t have to check every concept folder to see if there even is a corresponding file. (there might not be a js-file for every feature, …)
  • Every affected file is in one folder. The required navigation in the Solution Explorer is kept to a minimum.
  • In your source control system, you can look at the entire change history of this feature on one folder.
  • If you have to implement a new similar feature, you can copy this one folder and use it as a starting point.

Why is there a M in ASP.NET MVC?

I would like to make an exception of my previous structure: It’s important to understand that the ASP.NET MVC framework itself (System.Web.Mvc) does NOT give you any built-in support for “models”. If you require persistent data, you are allowed to use whatever technology you want (Entity Framework, NHibernate, raw ADO.NET, …). Yes, the project templates by default already reference Entity Framework, but again, this is a separate library and ASP.NET MVC has no dependency on it.

In my opinion this is a very good thing! The traditional three-tier architecture (data, business logic, presentation) still is one of the most important concepts for structuring software systems. ASP.NET MVC clearly targets the presentation tier and shouldn’t cover responsibilities from other tiers.

For this reason, we have to move the files “Customer.cs” and “CustomerRepository.cs” into a separate library. However, everything else in our folder belongs to the presentation layer.

What’s next?

I plan to do follow-up posts that address the challenges and also possible solutions for this structure, so stay tuned!

Read More

Moving my BlogEngine.NET blog to Microsoft Azure

I thought, after 5 years since my last post it might be time to write a new one! But that’s not so easy! When you login to the backend after such a long time, everything feels just wrong: The design is ugly, the blog engine is outdated, I’m not sure if my hosting provider still is the best choice for me and I don’t like my domain anymore! So, instead of just writing a post about something, I decided to change all of these things first and tell you about this process!

Get a running BlogEngine.NET instance within minutes

I was always interested in Windows Azure Microsoft Azure but never really had the time to do something useful with it. Therefore I decided to take this opportunity and move my blog to it. I already had access to the Azure Management Portal, so I didn’t have to go through some registration process. Since I also wanted to update my blog to the latest version of BlogEngine.NET I decided to start with a new installation and migrate my data afterwards (moving data from 6 posts is not so hard :-) ). Luckily, with Azure’s Gallery, it’s extremely easy to setup an instance of BlogEngine.Net. I just followed these instructions: http://blogs.msdn.com/b/webdev/archive/2012/10/12/blogengine-net-and-windows-azure-web-sites.aspx

After that, I just had to go through a quick export/import process to move my data from my old blog to the new one. I started with a “free” web site but quickly scaled it up to a “shared” one because I wanted the site to be running on my custom domain.

Deployment with Git and the App_Data folder

The default template of BlogEngine.NET 2.9 is quite nice, however I wanted to make some minor changes. Sounds like a good opportunity to test another great feature of Microsoft Azure: Git deployment.

According to this tutorial, I setup a Git repository for my web site within the Azure portal and cloned the repository to my notebook. Again, this was very straightforward and worked immediately. This also has the advantage that you now have a backup of your remote files on your home network.

But there’s one important thing to take care of when you use Git deployment with BlogEngine.NET. BlogEngine.NET by default stores data in files within the App_Data folder. This is quite nice since you don’t have to pay for a SQL Server database! However, if you use Git deployment it overwrites this folder, since it’s now under source control. Therefore I added the “App_Data” folder to the .gitignore file. Unfortunately, the deployment still did overwrite changes. I guess this happened, because on the server, the App_Data folder was still a part of the git folder.

To overcome this, I tried a different way: First, I copied my local App_Data folder to some backup directory on my notebook. Then I removed the .gitignore file and really deleted the App_Data folder from my local and remote repository (so if you do this as well, please note that you will have a downtime!). After that, I manually copied the App_Data folder back to Azure with FTP. As a last step, I re-created the .gitignore file with the App_Data-exclusion and also moved the App_Data folder back on my machine.

As a result, the “App_Data” folder is no longer monitored by Git and will not be touched when Azure does a deployment. Of course, whenever you need a up-to-date version of your App_Data folder on your PC for development purposes, you have to manually download it from Azure.

Some warnings about this:

  • I’m pretty sure this is not the best way to handle this. As far as I know, you shouldn’t store user generated content on your server but instead use an Azure Storage account for it. Having user files on your server has several disadvantages: you can’t scale out your servers, you don’t have any replication or backups and so on.
  • I’m not sure if the current deployment behavior, which does not touch untracked folders will stay that way forever. I wouldn’t be surprised if they do a real sync someday because actually, the git repository should be 100% consistent with the web folder.

This means, I do not recommend this for anything else than completely uncritical things! If you want to do this, make sure you regularly backup your App_Data folder by e.g. doing a scheduled FTP download every day.

For this blog, I’m fine with those risks for now but if I happen to need a storage account anyway or if I will blog more in the future I will definitely move the data to a storage account.

Read More

Meet me @ BASTA! Spring 2009

Hello everyone!

Next week, I will attend the biggest German .NET conference BASTA! Spring 2009. Feel free to contact me, if you’d like to chat with me “offline” there!

What’s next on my blog?

I’m working on an ASP.Net MVC application right now and I plan to post about some of the techniques I’ve used, so expect to read from me after the conference!

Thanks for reading,
Christian Weiss

Read More

Testable and reusable cookie access with ASP.Net MVC RC

All good things come in threes, so I’m writing another post about how to access cookie or sessions. I got inspired by a comment from Chris Marisic, who suggested to use a more testable way for this stuff!

Previous posts about this topic:

Intro

Using static wrapper properties is a quick and easy way, but you can’t unit test them because they access HttpContext.Current directly. This time I will show you, how you can build a fully unit testable and strongly typed way to access your cookies. As there has been Christmas time 2 days ago (ASP.Net MVC RC1 was released *g*) I’m using the latest MVC bits for my example!

The cookie container is responsible for getting values out of and into the cookie collections. It does not know which concrete values I’m using in my application! This is implemented in a different level, so you can use this class for all of your applications!

In my implementation it’s possible to store “objects” in cookies. I’ve implemented it this way because I don’t want to convert all my DateTime, int, … cookies every time! But I also don’t want someone to save Lists or any other complex types, so my SetValue() method validates the type of the value and throws an exception, if it’s not a value type or nullable value type. That’s a little type checking, but I think it is worth it because cookies are set quite rarely!

Here’s the interface:

    public interface ICookieContainer
    {
        bool Exists(string key);
         
        string GetValue(string key);
        T GetValue<T>(string key);
         
        void SetValue(string key, object value, DateTime expires);
    }

I will just show the fundamental code here. If you want to see the whole implementation, please take a look at the code sample. (see bottom)

As you can see below, I’ve used the abstracted versions of HttpRequest and HttpResponse, which you get, if you use ASP.Net MVC. That’s just one of thousand things I love about ASP.Net MVC. These classes can be used easily in unit tests. Notice that everything can be injected here. There’s no direct access to HttpContext.Current!

    public class CookieContainer : ICookieContainer
    {
        private readonly HttpRequestBase _request;
        private readonly HttpResponseBase _response;
        
        public CookieContainer(HttpRequestBase request, HttpResponseBase response)
        {
            // "Check" is a helper class, I've got from the "Kigg" project
            Check.IsNotNull(request, "request");
            Check.IsNotNull(response, "response");
            
            _request = request;
            _response = response;
        }
        
        public string GetValue(string key)
        {
            Check.IsNotEmpty(key, "key");
            
            HttpCookie cookie = _request.Cookies[key];
            return cookie != null ? cookie.Value : null;
        }
        
        public void SetValue(string key, object value, DateTime expires)
        {
            Check.IsNotEmpty(key, "key");
            
            string strValue = CheckAndConvertValue(value);
            
            HttpCookie cookie = new HttpCookie(key, strValue) {Expires = expires};
            _response.Cookies.Set(cookie);
        }
        
        // ... see code sample for full implementation
    }

Here’s a sample unit tests that proves the testability of this code. I use Moq as my mocking framework.

    public static class Mocks
    {
        public static Mock&lt;HttpRequestBase&gt; HttpRequest()
        {
            var httpRequest = new Mock&lt;HttpRequestBase&gt;();
            httpRequest.Setup(x =&gt; x.Cookies).Returns(new HttpCookieCollection());
            return httpRequest;
        }
        
        public static Mock&lt;HttpResponseBase&gt; HttpResponse()
        {
            var httpResponse = new Mock&lt;HttpResponseBase&gt;();
            httpResponse.Setup(x =&gt; x.Cookies).Returns(new HttpCookieCollection());
            return httpResponse;
        }
    }
    
    // This method is from my CookieContainerTests class
    
    [TestMethod]
    public void SetValue_UpdatesExistingCookie()
    {
        // Arrange
        const string cookieName = "myCookie";
        const string cookieValue = "myValue";
        DateTime cookieExpires = new DateTime(2009, 1, 1, 0, 0, 0);
        
        var httpRequest = Mocks.HttpRequest();
        var httpResponse = Mocks.HttpResponse();
        var cookieContainer = new CookieContainer(httpRequest.Object, httpResponse.Object);
        
        httpResponse.Object.Cookies.Add(new HttpCookie(cookieName, "oldValue"));
        
        // Act
        _cookieContainer.SetValue(cookieName, cookieValue, cookieExpires);

        // Assert
        HttpCookie cookie = httpResponse.Object.Cookies["myCookie"];
        Assert.IsNotNull(cookie);
        Assert.AreEqual(cookie.Name, cookieName);
        Assert.AreEqual(cookie.Value, cookieValue);
        Assert.AreEqual(cookie.Expires, cookieExpires);
    }

That’s it! Now you have a testable and reusable cookie container!

How to use it in your application

It’s really easy to integrate this into your app! Just create an interface that defines all your application-specific properties you want to save in cookies and a concrete implementation of this interface that interacts with the cookie container.

    public interface IAppCookies
    {
        string UserEmail { get; set; }
        DateTime? LastVisit { get; set; }
    }
    
    public class AppCookies : IAppCookies
    {
        private readonly ICookieContainer _cookieContainer;
        
        public AppCookies(ICookieContainer cookieContainer)
        {
            _cookieContainer = cookieContainer;
        }
        
        public string UserEmail
        {
            get { return _cookieContainer.GetValue("UserEmail"); }
            set { _cookieContainer.SetValue("UserEmail", value, DateTime.Now.AddDays(10)); }
        }
        
        public DateTime? LastVisit
        {
            get { return _cookieContainer.GetValue&lt;DateTime?&gt;("LastVisit"); }
            set { _cookieContainer.SetValue("LastVisit", value, DateTime.Now.AddDays(10)); }
        }
    }

You can now inject this IAppCookies interface to your MVC Controller:

    public class HomeController : Controller
    {
        private readonly IAppCookies _cookies;

        public HomeController(IAppCookies cookies)
        {
            _cookies = cookies;
        }

        public ActionResult Index()
        {
            DateTime currentTime = DateTime.Now;

            IndexViewModel viewModel = new IndexViewModel
            {
                CurrentTime = currentTime,
                LastVisit = (_cookies.LastVisit ?? currentTime),
                UserEmail = _cookies.UserEmail
            };

            _cookies.LastVisit = currentTime;

            return View(viewModel);
        }

        public class IndexViewModel
        {
            public string UserEmail { get; set; }
            public DateTime LastVisit { get; set; }
            public DateTime CurrentTime { get; set; }
        }
    }

Wow, you’re still reading :-)

That’s all I want to show here! If you want to see more about how the IOC is set up (I’m using StructureMap) or anything else, take a look at the full code:

I looking forward to hearing your feedback on this!

Thanks for reading,
Christian Weiss

Read More

Use wrappers to access your cookies, sessions, ...

As described in my previous post, I will give you some more details about how you can access your cookies in a type-safe and easy way!

Update: Read the follow up post

The simplest way to do this is by using a little wrapper class like this one:

    using System;
    using System.Globalization;
    using System.Web;
     
    namespace CookieWrapper.Web
    {
        public static class MyCookies
        {
            public static string UserEmail
            {
                get { return GetValue("UserEmail"); }
                set { SetValue("UserEmail", value, DateTime.Now.AddDays(10)); }
            }
     
            public static DateTime? LastVisit
            {
                get
                {
                    string strDate = GetValue("LastVisit");
                    if (String.IsNullOrEmpty(strDate))
                        return null;
                    return DateTime.Parse(strDate, CultureInfo.InvariantCulture);
                }
                set
                {
                    string strDate = value.HasValue ? value.Value.ToString(CultureInfo.InvariantCulture) : null;
                    SetValue("LastVisit", strDate, DateTime.Now.AddDays(10));
                }
            }
     
            private static string GetValue(string key)
            {
                HttpCookie cookie = HttpContext.Current.Request.Cookies[key];
                if (cookie != null)
                    return cookie.Value;
                return null;
            }
     
            private static void SetValue(string key, string value, DateTime expires)
            {
                HttpContext.Current.Response.Cookies[key].Value = value;
                HttpContext.Current.Response.Cookies[key].Expires = expires;
            }
        }
    }

All you have to do is create a static property for every cookie that you would like to work with. As you can see you also have the Expires-times administrated in one single place!

Now you can access the values as seen below and you don’t have to worry about the cookie implementation-details in every place.

    tbLastVisit.Text = MyCookies.UserEmail;
    MyCookies.LastVisit = DateTime.Now;

Of course, you can also use this same approach for working with session data or any other key-based collection.

Thanks for reading,
Christian Weiss

Read More

Don't use Response.Cookies[string] to check if a cookie exists!

Update: Follow Up Posts

The short explanation, if you don’t like to read the entire story

If you use code like if (Response.Cookies["mycookie"] != null) { … }, ASP.Net automatically generates a new cookie with the name “mycookie” in the background and overwrites your old cookie! Always use the Request.Cookies-Collection to read cookies!

The long explanation and some useful advices at the end

You can access the Cookies-Collection in two different places in ASP.Net:

  • Request.Cookies gives you the cookies that are sent from the browser to your server.
  • With Response.Cookies, you can send cookies from your server to the browser.

Storing cookies

To make your life easier (or harder, as you will see later), ASP.Net gives you the possibility to add a cookie to the browser this way:

Response.Cookies["mycookie"].Value = "some value";
Response.Cookies["mycookie"].Expires = DateTime.Now.AddDays(10);

As you can see here, the .NET framework automatically generates the HttpCookie instance with the name “mycookie” in the background and adds it to the collection.

HttpCookie.Get

Sending cookies to the browser this way is perfectly fine.

Reading Cookies

The important thing you have to know when reading cookies is, that the Response.Cookies collection is empty at the beginning of every request. The cookie with the name “mycookie” can only be found in the Request.Cookies-Collection!

If you use the following line to check the cookie, a new cookie with the name “mycookie” and an empty value gets added to the Response.Cookies Collection and this overwrites your old cookie! (See framework code above!)

if (Response.Cookies["mycookie"] != null)
{
  // This automatically overwrites the existing cookie with an empty value!!!
}

The correct way to access cookies is by using the Request.Cookies-Collection:

if (Request.Cookies["mycookie"] != null)
{
  // This is fine
}

So remember the following rules

  • To read cookies, ALWAYS use the Request.Cookies-Collection
  • Only use the Response.Cookies-Collection to set or change cookies

How can I make it better and more beautiful?

Accessing the Request.Cookies or Response.Cookies collection directly is lame! You can’t easily test this code and you don’t have all the other cool stuff like type safety and IntelliSense for the keys.

I will write a follow-up post right after this one with an detailed example on how you can do it better!

Thanks for reading,
Christian Weiss

Read More

I'd like to announce ... myself!

I don’t know why, but due to some reasons, you stumbled upon this page! I’m happy to announce, that this page is going to be my attempt to make the world a better place for you – at least if you are a Microsoft .Net developer and if you – like me – love to learn new things!

“WTF? Yet another blog about programming?”

If that’s what you are thinking right now, then my answer is “Maybe! But hopefully not!”. I will give my best to provide you with high quality information and if it saves you some hours then it served its purpose!

What can you expect from this blog?

I’m a web developer and software architect. The things I like working with are constantly changing, as I really like to dive into new technologies! Currently I’m learning a lot about ASP.Net MVC and all the ALT.Net topics, so these will be the first topics I’m going to write about.

Your English is crap! Stop hurting my eyes!

I’m from Austria and I speak German (with some crazy dialect)! That means, English is not my first language! This blog is a great opportunity for me to get my hands dirty and just improve my English knowledge! Please feel free to write comments and correct me, if you have found some disastrous mistakes.

Stop talking, start writing!

That’s enough said for my first post! Hopefully, I’ve awaken your interest a little bit and you will visit my page again. Expect to reading from me soon!

Thanks for reading,
Christian Weiss

Read More