7
\$\begingroup\$

Any comments are welcome. However I'd like to call specific attention to my... interpretation of the N-Tier application architecture and how I'm consuming data. Note that the namespacing and inline comments have been factored out for brevity.

The Data Tier (DataModel.csproj)

// Base class for EF4 Entities
public abstract class EntityBase {
    public int Id { get; set; }
}

// Entities
public class Account : EntityBase {
    public string UserName { get; set; }
    public string Password { get; set; }
    public byte[] Salt { get; set; }
    public string EmailAddress { get; set; }
}

public class Entry : EntityBase {
    public string Title { get; set; }
    public DateTime Created { get; set; }
    public Account Owner { get; set; }
}

// EF4 Code-First Data Context
public class DataContext : DbContext {
    public DbSet<Account> Accounts { get; set; }
    public DbSet<Entry> Entries { get; set; }
}

// Repository Pattern
public interface IRepository<TEntity>
    where TEntity : EntityBase {
    TEntity Create();
    void Delete(TEntity entity);
    TEntity Get(int id);
    TEntity Get(Expression<TEntity,bool>> where);
    void Insert(TEntity entity);
    void Save();
    IQueryable<TEntity> Where(Expression<TEntity,bool>> where);
}

public class Repository<T> : IRepository<T>
    where T : EntityBase, new() {
    private readonly DataContext context;
    private readonly DbSet<T> set;

    public Repostory(DataContext context) {
        // context injected via ioc.
        this.context = context;
        this.set = this.context.Set<T>();
    }

    // Implementation of IRepository<T>
}

Business Logic Tier (Runtime.csproj)

// IOC Container
public static class IOC {
    public static readonly IWindsorContainer Container = new WindsorContainer();

    static IOC() {
        InstallAssembly("Presentation");
        InstallAssembly("Runtime");
        InstallAssembly("DataModel");
    }

    public static void InstallAssembly(string name) {
        Container.Install(FromAssembly.Named(name));
    }
}

// Also includes System.Configuration ConfigSections, HttpModules and Provider classes
// which are strongly tied to IRepository<>.

Presentation Layer (Mvc App) (Presentation.csproj)

protected void Application_Start() {
    AreaRegistration.RegisterAllAreas();
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    // Controller Factory instance makes initial reference to IOC class, causing
    // the installers to run.
    ControllerBuilder.Current.SetControllerFactory(new Runtime.Mvc.WindsorControllerFactory());
}

// Data-Driven Controller
public class DashboardController : SecureController {
    private readonly IRepository<Entry> entries;

    public DashboardController(IRepository<Entry> entries) {
        // entries injected via ioc.
        this.entries = entries;
    }

    [ChildActionOnly, HttpGet]
    public ActionResult RecentEntries() {
        var viewModel = new RecentEntriesViewModel( entries.Where( stuffHappens ) );
        return PartialView(viewModel);
    }
}

I feel like letting IRepository<> reach the presentation layer may be a violation of the pattern. Exposition of an IQueryable<> through IRepository<>.Where seems like a bad idea at the Presentation level. There's no way to moderate the use of IQueryable and could lead to brittle code. Inputs?

\$\endgroup\$
1
\$\begingroup\$

I agree that exposing IRepository<> to the presentation layer is bad.

The reason it's bad is that your Business Logic Tier (BLT) might want to enforce business-y rules on the access of data. That's what the business layer is for, after all.

Your heart is in the right place when you restrict use of the repository to .Where in the presentation layer. However, you still have that entries field exposed for another programmer to come along and abuse later. To prevent this from happening, you could expose only an Expression in the presentation layer. Pass that Expression to the BLT, which would then instantiate the IRepository<> and use the Expression. That would also provide a hook for the BLT to add additional conditions to the Expression for access control, etc.

\$\endgroup\$
  • \$\begingroup\$ -1, If exposing a Repository interface in the presentation layer is bad, then exposing its methods would be bad too, right? Assuming that we are using a DDD approach, the "Save" method for any entity or list of entities is probably going to be located in the repository, and defined in this interface. Think of how many applications would completely cease to be functional if all "Save" buttons were removed from the UI. Repository implementation details should be buried deep in the infrastructure layer, but the interface is pure domain and must be accessible. \$\endgroup\$ – smartcaveman Sep 21 '12 at 13:08
1
\$\begingroup\$

If you want to read a good discussion about architecture layouts... check out Onion Architecture.

http://blog.giatechnology.com/2011/05/24/a-breakdown-of-onion-architecture-use-in-enterprise-applications-part-1/

\$\endgroup\$
  • \$\begingroup\$ I'm also a big fan of using PetaPoco as a Data/ORM layer technology. You can use domain POCO objects in your data layer without needing to expose things like IQueryable to the presentation layer. Also I have found that I have written less code and that it runs faster (versus EntityFramework) \$\endgroup\$ – Jeremy Bell Sep 29 '12 at 6:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.