люди!
Предположим, у нас есть набор интерфейсов, представляющих проблемную область: IUser, IAddressBook, IComment
и так далее. Предположим, что IUser
определяется следующим образом:
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; }
}
В своем заявлении я использую только упомянутые контракты, например:
public IUser GetUser(string userName)
{
return Warehouse.GetRepository<IUser>().GetAll()
.First(u => u.Name == userName);
}
Как видите, я использую какой-то шлюз для получения данных. Метод репозитория GetAll()
возвращает IQueryable<TEntity>
, поэтому можно построить сложный запрос и использовать все преимущества отложенной загрузки. Когда я представлял его, я подумал об использовании Linq2Sql в будущем.
Некоторое время при разработке клиентского кода мы использовали реализацию хранения данных в памяти. Так что все работает отлично. Но теперь пришло время привязать домен к SQL Server. Итак, я начал внедрять всю инфраструктуру на Linq2Sql ... И когда сделал простое решение (вдохновленное статья Фредрика Калсета) и получил первое исключение, я понял, что все грусть этой идеи ... Вот воплощение 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
}
А здесь - исключение:
Exception: System.NotSupportedException:
The member 'Data.IHaveName.Name' has no supported translation to SQL.
Это очевидное исключение - провайдер Linq2Sql не понимает внешние интерфейсы, но как мне его разрешить? Я не могу изменить доменные интерфейсы для возврата Linq.Expression, например:
Expression<Func<string>> IHaveName.Name
{
get { return (() => USERNAME); }
set { USERNAME = value(); }
}
потому что это нарушает все текущее использование кода интерфейсами домена и, кроме того, я не могу заменить int
на Expression<Func<int>>
из-за религиозных убеждений:)
Может быть, мне следует написать пользовательский посетитель Expression, чтобы пропустить запрос AST для запроса IUser.SomeProperty
и заменить его внутренним суб-AST ...
Можете ли вы высказать свои мысли об этом?
ОБНОВЛЕНИЕ. Здесь я должен написать кое-что о реализации Repository
. Посмотрите на GetAll()
источник:
public IQueryable<TEntity> GetAll()
{
ITable table = GetTable();
return table.Cast<TEntity>();
}
protected ITable GetTable()
{
return _dataContext.GetTable(_implType);
}
В моем примере TEntity <=> IUser
и _implType <=> typeof(USER)
ОБНОВЛЕНИЕ 2. Я нашел связанный вопрос , где у OP аналогичная проблема: нужен какой-то мост между конкретными сущностями ORM и его доменными сущностями. И я нашел интересным ответ : автор предложил создать выражение для посетителя, которое будет выполнять преобразование между сущностями (домен ORM <=>).