DAO is the anachronism of Data Access Object. On the cloud you can find various definitions of what is a DAO. The follow come from Java ecosystem and is the most complete I found:
The DAO implements the access mechanism required to work with the data source. The data source could be a persistent store like an RDBMS, an external service like a B2B exchange, a repository like an LDAP database, or a business service accessed via CORBA Internet Inter-ORB Protocol (IIOP) or low-level sockets. The business component that relies on the DAO uses the simpler interface exposed by the DAO for its clients. The DAO completely hides the data source implementation details from its clients. Because the interface exposed by the DAO to clients does not change when the underlying data source implementation changes, this pattern allows the DAO to adapt to different storage schemes without affecting its clients or business components.As you can read there isn’t something exactly about how should look a DAO and a DAO can be defined to access data coming from any external source :
Essentially, the DAO acts as an adapter between the component and the data source.You are completely free to define what each adapter should expose. For example you may have a DAO interface for a UserDAO where its implementations may work with an underling RDBMS, or with Active Directory and so on.
For CRUD operations, with a RDBMS a generic DAO may look as:
public interface IDao<TEntity, TIdentity> where TEntity: IEntity<TIdentity>I have used a DAO like that with an ADO.NET implementation. Write the generic implementation for NHibernate is a simple joke.
{
TEntity Get(TIdentity identity);
TEntity MakePersistent(TEntity entity);
void MakeTransient(TEntity entity);
}
The problem with DAO is that was defined before powerful persistent-layers, as NHibernate, come in place. Modern persistent layer are tracking any modification you are applying to a persistent Entity and may auto-persist the modification without an explicit call to the DAO. Try to think the difference you have between an implementation using ADO.NET and using NHibernate; using ADO.NET nobody will update an entity state without an explicit call to the MakePersistent method where with NHibernate the modification can be persisted even without call it… a problem ? may be, all depend on how you are writing high layers.
Because the pattern does not define how a DAO should look, two DAOs as the follows are perfectly legal and may coexist in the same application:
public interface IProductDaoAs you can see there are various difference and there isn’t a common interface.
{
Product Get(int id);
IEnumerable<Product> FindByDecription(string template);
IEnumerable<Product> FindByPrice(IRange<decimal> priceRange);
void Save(Product product);
void Update(Product product);
void Delete(Product product);
}
public interface ICustomerDao
{
Customer Get(string id);
IEnumerable<Customer> FindByName(string name);
Customer Update(Customer customer);
}
As said in the previous post you can use the Query object pattern to easy extend a DAO but, again, you can even not use it (nobody wrote “Client objects construct query specifications declaratively and submit them to Repository for satisfaction”). If you does not have time to define a good generic IQuery<TEntity> interface, you can use a more simple Criteria object. A Criteria object may look as:
public class CustomerAddressCriteriaThe DAO is responsible to create the correct query for the underling data-source so construct an SQL, an HQL or even use LINQ is a matter solved inside the DAO implementation (probably this was the reason because many of us are not so worried about a powerful LINQ provider).
{
public string PartialCustomerName { get; set; }
public string StreetName { get; set; }
public CustomerOrder Order { get; set; }
}
public enum CustomerOrder
{
ByNameAscending,
ByNameDescending
}
Which is the end of this “powerful” and “flexible” pattern ?
As said before the DAO was born some years ago and the IT evolving very fast. If you want stay with a completely switchable interface there is no problem but if you want stop writing methods as FindByDecription, FindByPrice, FindByName, FindByCriteria the next step, at least, is something like:
IEnumerable<TEntity> Retrieve(Expression<Func<TEntity, bool>> predicate);and here is where our old friend DAO die because “switchable”, in this case, mean switch between underling system are supporting LINQ… perhaps in these days it mean again any systems… long life to DAO ? ;)
int Count(Expression<Func<TEntity, bool>> predicate);
Previous post Repository.
Great series... I know you are refering to Ayende's take on Repository pattern.. Would you please give us your recommendation or what you generally use? I know it depends on the context but which one do you like most? Repository, DAO or Query Object approach like Ayende proposed..
ReplyDeleteI wrote these two post only because requested by some users.
ReplyDeleteI'm using DAOs, so far... and is well know as you can see here
http://nhforge.org/blogs/nhibernate/archive/2009/09/07/part-8-daos-repositories-or-query-objects.aspx
Just a quick question, in the above post that you linked, you are checking if transaction is null. I can never get Transaction to be null. Do we need to actually check if Trasaction.IsActive?
ReplyDeleteWhich post ?
ReplyDeletehttp://nhforge.org/blogs/nhibernate/archive/2009/09/07/part-8-daos-repositories-or-query-objects.aspx
ReplyDeleteThat is not my post.
ReplyDeleteIf you want know how I'm checking the transaction usage the code is here.
excellent post Fabio, you're a gem and you know it. Just sometimes I feel you throw up very valuable info that few can understand.
ReplyDeletewe all know that YOU know it ;) Being a bit more verbose would help, but then, It's free and I know you have your company.
just one question, shouldn't Retrieve method on DAO layer return IQueryable instead of IEnumerable?
It _can_ construct SQL with right specification passed in, but where do I handle paging and such?
I should not handle paging in DAO, nor in specification object passed in (or query object or whatever the word is), and constructing it as IEnumerable brings all results to memory without the chance to alter generated SQL.
Thanks for the answer
cowgaR
@Turin
ReplyDeleteI'm my company.
IMO you should handle the pagination in DAO and pagination info., as pageNum and pageSize, should be two of information of query-object.
Hello. Nice post .. But i have a question. In disconnected environment i want for example to do something like a custom update. Update entity set col=true where colx=false and parent_id=2 .This would return how many changed. But using something generic like this how is it possible to do something like this? This matters especially when using a mvvc database like postgresql or oracle etc .. because you can easily handle concurrency issues..?
ReplyDeleteWould you create a criteria for update ?