1

In Martin Fowler's "Patterns of Enterprise Application Architecture" is described approach for organizing DAL like a set of mappers for entities. Each has it's own IdentityMap storing specific entity.

for example in my ASP.NET WebApplication:

//AbstractMapper - superclass for all mappers in DAL
public abstract class AbstractMapper
{
    private readonly string _connectionString;
    protected string ConnectionString
    {
      get { return _connectionString; }  
    }

    private readonly DbProviderFactory _dbFactory;
    protected DbProviderFactory DBFactory
    {
        get { return _dbFactory; }
    } 

    #region LoadedObjects (IdentityMap)
    protected Hashtable LoadedObjects = new Hashtable();

    public void RegisterObject(long id, DomainObject obj)
    {
        LoadedObjects[id] = obj;            
    }

    public void UnregisterObject(long id)
    {
        LoadedObjects.Remove(id);
    }
    #endregion

    public AbstractMapper(string connectionString, DbProviderFactory dbFactory)
    {
        _connectionString = connectionString;
        _dbFactory = dbFactory;
    }

    protected virtual string DBTable
    {
        get
        {
            throw new NotImplementedException("database table is not defined in class " + this.GetType()); 
        }
    }

    protected virtual T Find<T>(long id, IDbTransaction tr = null) where T : DomainObject
    {
        if (id == 0)
            return null;
        T result = (T)LoadedObjects[id];
        if (result != null)
            return result;
        IDbConnection cn = GetConnection(tr);
        IDbCommand cmd = CreateCommand(GetFindStatement(id), cn, tr);
        IDataReader rs = null;
        try
        {
            OpenConnection(cn, tr);
            rs = cmd.ExecuteReader(CommandBehavior.SingleRow);
            result = (rs.Read()) ? Load<T>(rs) : null;                  
        }
        catch (DbException ex)
        {
            throw new DALException("Error while loading an object by id in class " + this.GetType(), ex);
        }
        finally
        {
            CleanUpDBResources(cmd, cn, tr, rs);
        }
        return result;
    }

    protected virtual T Load<T>(IDataReader rs) where T : DomainObject
    {
        long id = GetReaderLong(rs["ID"]);
        T result = (T)LoadedObjects[id];
        if (result != null) 
            return result;

        result = (T)DoLoad(id, rs);
        RegisterObject(id, result);
        return result;
    }

    // another CRUD here ...
}

// Specific Mapper for entity Account
public class AccountMapper : AbstractMapper
{
internal override string DBTable
{
    get { return "Account"; }
}

public AccountMapper(string connectionString, DbProviderFactory dbFactory) : base(connectionString, dbFactory) { }

public Account Find(long id)
{
    return Find<Account>(id);
}

public override DomainObject DoLoad(long id, IDataReader rs)
{
    Account account = new Account(id);
    account.Name = GetReaderString(rs["Name"]);
    account.Value = GetReaderDecimal(rs["Value"]);
    account.CurrencyID = GetReaderLong(rs["CurrencyID"]);
    return account;
}

// ...

}

The question is: where to store these mappers? How system services (entities) should call mappers?

I decided to create MapperRegistry containing all mappers. So services can call mappers like:

    public class AccountService : DomainService
{
    public static Account FindAccount(long accountID)
    {
        if (accountID > 0)
            return MapperRegistry.AccountMapper.Find(accountID);
        return null;
    }
    ...
}

But where can I store MapperRegistry instance? I see following variants, but don't like any of them:

  1. MapperRegistry is global for application (Singleton)

    • Not applicable because of necessity of synchronization in multi-thread ASP.NET application (at least Martin says that only mad can choose this variant)
  2. MapperRegistry per Session

    • Seems not so good too. All ORMs (NHibernate, LINQ to SQL, EntityFramework) masters advise to use DataContext (NHibernateSession, ObjectContext) per Request and not to store context in Session.
    • Also in my WebApp almost all requests are AJAX-requests to EntityController.asmx (with attribute ScriptService) returning JSON. And session is not allowed.
  3. MapperRegistry per Request

    • There are a lot of separate AJAX calls. In this case life cycle of MapperRegistry will be too small. So the data almost always will be retrieved from database, as a result - low performance.

Dear Experts, please help me with architectural solution.

Gordon
  • 312,688
  • 75
  • 539
  • 559
tabalin
  • 2,303
  • 1
  • 15
  • 27
  • Is there any reason why you could not use a ORM framework? There are some lightweight ones if basically just want a mapping that reflects your database scheme. A lightweight one is Simple.Data – Mharlin Jan 10 '12 at 14:28
  • Unfortunately, yes. Main part of the system is already written and tested. It's even in use but not so actively. Soon amount of users will be increased, this caused concerns about performance and common architectural correctness. Hope there is less time-consuming solution... – tabalin Jan 10 '12 at 17:15

0 Answers0