0

I have an ASP.NET website project that until recent had all code in App_Code folder. It uses Entity Framework 4 as ORM. Application is divided into three "sections" (let's say one for each customer). Each section has it's own database (but same schema). This is due to performance reasons, databases are over 10GB each with millions of rows.

Each time a context object is created a Session variable which holds section ID is called and proprietary connection string is chosen for this context.

It looks like this (following are members of static Connection class):

public static MyEntities GetEntityContext()
{
    if (HttpContext.Current.Session["section"] == null)
    {
        HttpContext.Current.Response.Redirect("~/Login.aspx");
    }
    var context = new MyEntities(GetEntityConnectionStringForSection((int)HttpContext.Current.Session["section"]);
    return context;
}

private static string GetEntityConnectionStringForSection(int section)
{
    switch (section)
    {
        case 1: return ConfigurationManager.ConnectionStrings["entity_1"].ConnectionString;
        case 2: return ConfigurationManager.ConnectionStrings["entity_2"].ConnectionString;
        case 3: return ConfigurationManager.ConnectionStrings["entity_3"].ConnectionString;
        default: return ConfigurationManager.ConnectionStrings["entity_1"].ConnectionString;
    }
}

It works very good and also handles situation when session timed out everytime any data access is performed.

Recently as I needed to share DB classes among two websites I moved all DB classes to separate class library and referenced System.Web library which I know is bad practice, but it's working.

Now the next step is to include unit and module tests which as I read is very difficult or impossible when using HttpContext in library, so I want to get rid of System.Web references. What is the best practice for this situation?

I think I can't just pass HttpContext to GetEntityContext() as it is also called from within my entity classes. Although this probably can be refactored. So maybe this is where I should go?

I also wondered if is it possible to somehow pass current section ID to this whole library? It cannot be just static property because as far as I understand it would be common for all users using the application. This should be user-specific.

Reassuming the objective is to make automated testing possible without loosing transparent Connection String choosing and session timeouts handling.

If I do something fundamentally wrong at this stage please also let me know. I can look again at this question tomorrow morning (8.00 am UTC) so please don't be discouraged by my silence till then.

EDIT:

Example of usage of Connection class in the library:

public partial class Store
{
    public static List<Store> GetSpecialStores()
    {
        using (var context = Connection.GetEntityContext())
        {
          return context.Stores.Where(qq => qq.Type > 0).OrderBy(qq => qq.Code).ToList();
        }
    }
}
Episodex
  • 4,479
  • 3
  • 41
  • 58

1 Answers1

1

You can declare interface IContextProvider inside your library ans use it to retrieve context. Something like:

public interface IContextProvider
{
   MyEntities GetEntityContext();
}

This will make your library testable. In your web project you can inject IContextProvider implementation into your library.

public class WebContextProvider : IContextProvider
{
    public MyEntities GetEntityContext()
    {
        if (HttpContext.Current.Session["section"] == null)        
            HttpContext.Current.Response.Redirect("~/Login.aspx");

        int sectionId = (int)HttpContext.Current.Session["section"];
        string connectionString = GetEntityConnectionStringForSection(sectionId);
        var context = new MyEntities(connectionString);
        return context;
    }

   private static string GetEntityConnectionStringForSection(int section)
   {
       switch (section)
       {
          case 1: return ConfigurationManager.ConnectionStrings["entity_1"].ConnectionString;
          case 2: return ConfigurationManager.ConnectionStrings["entity_2"].ConnectionString;
          case 3: return ConfigurationManager.ConnectionStrings["entity_3"].ConnectionString;
          default: return ConfigurationManager.ConnectionStrings["entity_1"].ConnectionString;
       }
   }
}

Inject this interface to repositories or other data access classes.

public partial class Store
{
    private IContextProvider contextProvider;

    public Store(IContextProvider contextProvider)
    {
        this.contextProvider = contextProvider;
    }

    public List<Store> GetSpecialStores()
    {
        using (var context = contextProvider.GetEntityContext())
        {
          return context.Stores.Where(qq => qq.Type > 0).OrderBy(qq => qq.Code).ToList();
        }
    }
}
Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
  • So I should put WebContextProvider in my both web projects? I'm ok with that, but I just like to know if this is right way. And then in my tests I create and use provider with fixed connection string settings? – Episodex Jul 20 '12 at 07:34
  • Nope, just declare `IContextProvider` interface in your DB classes library. Reference this library by your web project, then create `IContextProvider` implementation in your web project. – Sergey Berezovskiy Jul 20 '12 at 08:46
  • As interface implementation can't be static I created singleton class implementing it in my web project and it seems to be fine. But I use my old `Connection` class also in DB library - please look at my question update. I can't use this interface in such way, so should I always pass `IContextProvider` to every method in DB library? – Episodex Jul 20 '12 at 08:53
  • Hm... I use Entity Framework. How/where can I pass parameter to `EntityObject` constructor? Additionally the methods in DB library using context are not always static. Thank you for all your help! I just can't get all pieces working yet... – Episodex Jul 20 '12 at 09:41
  • I think it's better to split business logic and data access logic. Move all data-related logic to repository classes (StoreRepsotitory in your case), allow your entities be persistence ignorant. – Sergey Berezovskiy Jul 20 '12 at 10:16