folks!
Suppose we have set of interfaces that presents problem' domain: IUser, IAddressBook, IComment
and so on. The assume that IUser
is defined as follows:
public interface IUser : IAmIdentifiedEntity<int>, IHaveName
{
string FullName { get; set; }
string Email { get; set; }
bool ReceiveNotification { get; set; }
}
public interface IHaveName
{
string Name { get; set; }
}
In my application I use only mentioned contracts, for example:
public IUser GetUser(string userName)
{
return Warehouse.GetRepository<IUser>().GetAll()
.First(u => u.Name == userName);
}
As you can see, I'm using some gateway to get data. Repository' method GetAll()
returns IQueryable<TEntity>
, so it is possible to build some complex query and use all benefits of lazy loading. When I was introducing it, I thought about appliance of Linq2Sql in future.
For some time, while developing client code, we were using 'in-memory' implementation of data storage. So everything works fine. But now it's a time to bind domain to SQL Server. So I've started to implement all the infrastructure upon Linq2Sql... And when made simple solution (inspired by article of Fredrik Kalseth) and got first exception, I've realized all sadness of that idea... Here is implmentation of IUser
:
public partial class USER : IUser
{
int IHaveID<int>.ID
{
get { return USERID; }
}
string IHaveName.Name
{
get { return USERNAME; }
set { USERNAME = value; }
}
string IUser.FullName
{
get { return USERFULLNAME; }
set { USERFULLNAME = value; }
}
// ... same approach for other properties
}
And here - exception:
Exception: System.NotSupportedException:
The member 'Data.IHaveName.Name' has no supported translation to SQL.
This is obvious exception - Linq2Sql provider doesn't understand external interfaces, but how should I resolve it? I can't change domain interfaces to return Linq.Expression like:
Expression<Func<string>> IHaveName.Name
{
get { return (() => USERNAME); }
set { USERNAME = value(); }
}
because it breaks all the current code usage of domain interfaces and moreover i can't replace int
with Expression<Func<int>>
because of religious beliefs :)
Maybe, I should write custom Expression visitor to skip in query' AST calling of IUser.SomeProperty
and replace it with it's inner sub-AST...
Can you provide your thoughts on it?
UPDATE. Here I should write something about Repository
implementation. Look at GetAll()
source:
public IQueryable<TEntity> GetAll()
{
ITable table = GetTable();
return table.Cast<TEntity>();
}
protected ITable GetTable()
{
return _dataContext.GetTable(_implType);
}
In my example, TEntity <=> IUser
and _implType <=> typeof(USER)
UPDATE 2. I've found related question, where OP has an alike problem: need some bridge between concrete ORM entities and it's domain entities. And I've found interesting the answer: author suggested to create expression visitor, which will perform convertion between entities (ORM <=> Domain).
It seems, I've found a way how to solve my problem. Today I've found nice series of post named "Advanced Domain Model queries using Linq". Author presents his way to support more convenient(pretty) Lambda syntax to work with entities. And he uses Expression Tree convertions. So I will follow this approach, and if I succeed I will post more detailed answer (maybe via my blog).
What do you think about that idea? Should I spend time implementing Expression provider?
UPDATE. I've failed with custom Expression vistior - it requires to many conversion through expression tree. So I've decieded move all Linq logic into "storage" classes, which incapsulates complex querying and persistence managing.