Many to many в LINQ to SQL
Thursday, 19 June 08, 20:21 ZВ LINQ to SQL отсутствует понятие отношения many-to-many. Например, для таблиц Book, Author и Book2Author в БД мы получим не 2 класса, что было бы логично, а 3 и класс Books не будет содержать свойство типа EntitySet<Author>, отображающего many-to-many связь между Book и Author. Но у нас есть возможность добавить такое свойство самостоятельно.
Создаем новый класс ManyToManyList<Target, T>, который реализует интерфейс IList<T>:
public class ManyToManyList<Target, T> : IList<T>
{
}
Для доступа к данным, которые определяет реализуемое many to many отношение, создаем поле типа IQueryable<T>:
public class ManyToManyList<Target, T> : IList<T>
{
IQueryable<T> _query;
}
Реализуем некоторые методы IList<T>, не изменяющие данные:
public class ManyToManyList<Target, T> : IList<T>
{
IQueryable<T> _query;
IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _query.GetEnumerator();
}
IEnumerator<T> System.Collections.Generic.IEnumerable<T>.GetEnumerator()
{
return _query.GetEnumerator();
}
#region ICollection<T>
public bool Contains(T item)
{
return _query.Contains(item);
}
#endregion
...
}
Для изменения промежуточных таблиц в БД, обеспечивающих реализацию many to many отношения, при добавлении T в Target или удалении T из Target создаем 2 делегата Action<T> в классе:
public class ManyToManyList<Target, T> : IList<T>
{
Action<T> _onAdd;
Action<T> _onRemove;
...
}
Реализуем методы, отвечающие за добавление и удаление записей, в которых будут вызываться соответствующие делегаты
public class ManyToManyList<Target, T> : IList<T>
{
Action<T> _onAdd;
Action<T> _onRemove;
...
#region ICollection<T>
public void Add(T item)
{
if (_onAdd != null) _onAdd(item);
}
public bool Remove(T item)
{
if (_onRemove != null)
{
_onRemove(item);
return true;
}
return false;
}
public void Clear()
{
if (_onRemove != null)
{
foreach (var item in _query)
{
_onRemove(item);
}
}
}
#endregion
...
}
Добавляем конструкторы:
public class ManyToManyList<Target, T> : IList<T>
{
public ManyToManyList(Target target, IQueryable<T> query)
{
this._target = target;
this._query = query;
}
public ManyToManyList(Target target, IQueryable<T> query,
Action<T> onAdd, Action<T> onRemove) : this(target, query)
{
this._onAdd = onAdd;
this._onRemove = onRemove;
}
...
}
Пример использования класса ManyToManyList<Target, T> для классов Book и Author:
public partial class Book
{
ManyToManyList<Book, Author> _authors;
public ManyToManyList<Book, Author> Authors
{
get
{
if (_authors == null)
{
IQueryable<Author> query = DB.Book2Author.Where(
x => x.BookId == this.Id).Select(x => x.Author);
_authors = new ManyToManyList<Book, Author>(
this, query, OnAddAuthor, OnRemoveAuthor);
}
return _authors;
}
}
void OnAddAuthor(Author author)
{
Book2Author b2a = new Book2Author();
b2a.AuthorId = author.Id;
Book2Author.Add(b2a);
}
void OnRemoveAuthor(Author author)
{
Book2Author b2a = this.Book2Author.First(
x => x.AuthorId == author.Id);
DB.Book2Authors.DeleteOnSubmit(b2a);
}
...
}