Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Data Access Layer architecture 1

Status
Not open for further replies.

dinger2121

Programmer
Sep 11, 2007
439
US
Hello,
I am stepping back and taking a look at my data access layer and I was hoping someone could give me some guidance in regards to it.
Currently I am using nhibernate to get all of my data mapped to objects. my dal has 2 projects, one called data access layer and one called data transfer. The data transfer project has my fluent mapping files and my object definitions. That piece is ok.
my second project is where everything happens. I have 2 classes in this project - my data provider class and my session manager class. The session manager class is pretty straightforward - that manages my session factory. My question is in regards to my data provider class.

Currently, my data provider class is where I get all of my data from. It has various methods which are actually my nhibernate criteria queries. I've got many different criteria queries each represented by a separate method.

My question is this - is it correct to have all of these queries in one class? basically (for instance), if I have a product table and a manufacturer table, I've got various queries for each. Generally, I return an IList from my methods, so I may have various criteria queries for product - ie., query by product number, query by product type, etc., and similar queries for manufacturer.

Does it make sense to create a product and manufacturer interface and then have each implementation represent a different criteria query, or is that overkill?

I hope that all makes sense. I am just looking to set up my DAL using "best practices" and was just hoping someone can either validate what I am doing or point me in a better direction.

Thanks for any thoughts.

carl
MCSD, MCTS:MOSS
 
Carl, I see 2 sides to this
1. conceptually (layering, services, architecture)
2. implementation (nhibernate specifically)

from a conceptual point of view I would have a problem with 1 provider for all queries. this class has too many reasons for change. Any time you would add a query you would need to modify this class. A class should have exactly 1 reason to change (single responsibility principle) and it should be closed for changes but available for extensibility (open/closed principle)

From a conceptual stand point I would handle data access with either query specific commands or an specific entity repository. These concepts can be used together as well, it doesn't need to be an either or. There are examples of each scattered on the web. the idea of a query specific command is to have an interface that is designed for exactly 1 specific query. the interface would have 1 method on it for example
Code:
interface ICustomerWithOrder
{
   IEnumerable<Customer> Fetch(...);
}

interface ICustomersInAState
{
   IEnumerable<Customer> Fetch(string state);
}
a repository would look more like this
Code:
interface ICustomerRepository
{
   Customer Get(long id);
   IEnumerable<Customer> All();
   IEnumerable<Customer> Page(int page, int pageSize);
   IEnumerable<Customer> CustomersWithOrders();
   IEnumerable<Customer> ICustomersInAState(string state);
}
you can see by the code that event the repository concept can become a large collection of queries.

on to the technical details. I do not abstract NH behind a DAL. There is alot of power within NH. NH futures being the most common. placing this behind a generic repository/query interface reduces alot of the power available to the client code. therefore I expose NH directly to the application and service components.

In the past I have used both a Repository implementation with NH (Rhino.Commons) and a query specific implementation (sub classing detached criteria and defining named queries). I much prefer the query specific approach. it allows me to explicitly express the intent of the query while keeping the object to a single responsibility that I can be extended if necessary.

Jason Meckley
Programmer
Specialty Bakers, Inc.

faq855-7190
faq732-7259
 
Thanks for the response Jason.
I hope I understand your comments correctly....
My thinking was that if I need to use the same query in multiple places, I would be better off having that in a DAL, so that I can change it in one place if the need arises. Are you saying that you just create your queries in separate classes within your app when needed?

Would you recommend keeping the session manager in a separate project?

carl
MCSD, MCTS:MOSS
 
Are you saying that you just create your queries in separate classes within your app when needed?
yes, if the queries are complex enough to warrant separate queries. for example say I want all products ordered within a date range. I would use a named query like this
Code:
<query name="ProductsWithinADataRange">
   <param-def name="start" type="DateTime"/>
   <param-def name="end" type="DateTime"/>
   select distinct l.Product 
   from Order o join o.Lines l
   where o.OrderDate between :start and :end
</query>
or with detached criteria
Code:
class ProductsWithinADataRange : DetachedCriteria
{
   pubic ProductsWithinADataRange(DateTime start, DateTime end)
      : base(typeof(Product))
   {
      var products = DetachedCriteria
         .For<Order>()
         .SetProjection(Projections.Distinct(Projections.Property("p.id")))
         .Add(Restrictions.Between("OrderDate", start, end))
         .CreateAlias("l", "Lines")
         .CreateAlias("p", "l.Product");

      Add(SubQueries.In("id", products));
   }
}
</query>

as for the session manager. no, i wouldn't keep that in a separate project. What value is there in a separate project? What value is there in a session manager when NH has one built in?

here is an example for a website. the context is configurable to other options as well (thread static, call context, etc)
Code:
<session-factory>
   driver...
   dialect...
   proxy factory...
   <property name="current_session_context">web</property>
</session-factory>
Code:
class Global : HttpApplication
{
   public static ISessionFactory factory {get; private set;}

   public Global()
   {
      BeginRequest += (s, e) => SessionContext.Bind(factory.OpenSession());
      EndRequest += (s, e) => SessionContext.Unbind(factory).Dispose();
   }

   public void Application_Start(object sender, EventArgs e)
   {
       factory = new Configuration().Configure().BuildSessionFactory();
   }

   public void Application_End(object sender, EventArgs e)
   {
       factory.Dispose();
   }
}

Jason Meckley
Programmer
Specialty Bakers, Inc.

faq855-7190
faq732-7259
 
As always, thanks for the info.

carl
MCSD, MCTS:MOSS
 
I'm not sure why there is no benefit to having my sessionmanager in a separate project. I am using fluent nhibernate, and this sessionmanager holds all of my connection info. I will be using this in multiple applications, so wouldn't it be beneficial to have it in a separate project so I can just add it in and use the same info all of the time instead of having to reenter the info for each project?
Plus, if my connection info ever changes, I can just update it in one place and recompile.

carl
MCSD, MCTS:MOSS
 
if the mappings are used across multiple projects, and the projects require the same domain mappings, then it makes sense to have a project explicitly for mapping the domain. I don't see the need for session manager object. one already exists within the session factory itself.

there are only 2 reasons why I wouldn't create a separate project for the mappings
1. not all applications need the entire domain model or all the mapped tables.
2. centralizing the data access on a middle tier and having applications communicate with the domain through this tier. only a single domain deployment is required, thus negating the need for a unique mapping project.

Jason Meckley
Programmer
Specialty Bakers, Inc.

faq855-7190
faq732-7259
 
That makes sense - mapping your domain for each specific project ensures that you are only bringing the information that you need into each project, instead of having an entire domain mapped and included for every single project that it is used in.
That especially makes sense, especially when working with large amounts of data or very large schemas.

Thanks again

carl
MCSD, MCTS:MOSS
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top