Linq to SQL: класс поддержки «многие ко многим» - PullRequest
0 голосов
/ 12 апреля 2011

Мне бы хотелось иметь класс поддержки, чтобы помочь с отношением «многие ко многим».

На мой взгляд, это может быть двойной универсальный класс, который вы определили бы в одной или обеих частичных сущностях.классы, окружающие отношения.

Получение доступа к другой таблице без особого упоминания таблицы отношений должно быть простым.Однако добавление или удаление из коллекции несколько сложнее.Вам бы также хотелось добавить строку в таблицу отношений и зафиксировать ее или удалить, основываясь на том, что сделано.

Можно ли это сделать с помощью функции, которая передается в этот универсальный класс?

Существует ли такой класс, и если нет, то это что-то, что можно сделать жизнеспособно?

Ответы [ 2 ]

2 голосов
/ 12 апреля 2011

Вы можете создать IManyToManySet<TEntity> интерфейс, который может быть возвращен из вашего свойства many to many и иметь реализацию ManyToManySet<TSource, TCross, TDestination> с функциями вставки и удаления запроса.

Интерфейс может выглядеть следующим образом:

public interface IManyToManySet<TEntity> : IEnumerable<TEntity>
    where TEntity : class
{
    int Count { get; }
    void Add(TEntity entity);
    bool Remove(TEntity entity);
    void AddRange(IEnumerable<TEntity> collection);
}

И реализация может выглядеть следующим образом:

public class ManyToManySet<TSource, TCross, TDestination>
    : IManyToManySet<TDestination>, IEnumerable<TDestination>
    where TDestination : class
    where TSource : class
    where TCross : class
{
    private TSource source;
    private EntitySet<TCross> crossSet;
    private Func<TCross, TDestination> destinationSelector;
    private Func<TSource, TDestination, TCross> crossFactory;

    public ManyToManySet(TSource source, 
        EntitySet<TCross> crossSet,
        Func<TCross, TDestination> destinationSelector,
        Func<TSource, TDestination, TCross> crossFactory)
    {
        this.source = source;
        this.crossSet = crossSet;
        this.destinationSelector = destinationSelector;
        this.crossFactory = crossFactory;
    }

    public int Count
    {
        get { return this.crossSet.Count; }
    }

    public void Add(TDestination entity)
    {
        var newEntity = this.crossFactory(this.source, entity);
        this.crossSet.Add(newEntity);
    }

    public bool Remove(TDestination entity)
    {
        var existingEntity = (
            from c in this.crossSet
            where this.destinationSelector(c) == entity
            select c)
            .SingleOrDefault();

        if (existingEntity != null)
        {
            return this.crossSet.Remove(existingEntity);
        }

        return false;
    }

    public void AddRange(IEnumerable<TDestination> collection)
    {
        foreach (var entity in collection)
        {
            this.Add(entity);
        }
    }

    public IEnumerator<TDestination> GetEnumerator()
    {
        return this.crossSet.Select(this.destinationSelector)
            .GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

В этой реализации необходимо указать несколько вещей:

  1. TSourceэкземпляр, который указывает на сущность, которая определяет свойство.
  2. EntitySet<TCross>, который указывает на список сущностей, которые определяют перекрестную таблицу.
  3. Функция проекции, позволяющая преобразовыватьот TCross до TDestination.
  4. Заводская функция, позволяющая создавать новые TCross на основе TSource и TDestination.

Translatingэто на практическом примере (с использованием Product и Order) даст вам следующее свойство в сущности Order:

private IManyToManySet<Product> products;
public IManyToManySet<Product> Products
{
    get
    {
        if (this.products != null)
        {
            this.products = new ManyToManySet<Order, OrderProduct, Product>(
                this, this.OrderProducts, op => op.Product,
                (o, p) => new OrderProduct { Order = o, Product = p });
        }

        return this.products;
    }
}

и следующее свойство в сущности Product:

private IManyToManySet<Order> orders;
public IManyToManySet<Order> Orders
{
    get
    {
        if (this.orders == null)
        {
            this.orders = new ManyToManySet<Product, OrderProduct, Order>(
                this, this.OrderProducts, op => op.Order,
                (p, o) => new OrderProduct { Order = o, Product = p });
        }

        return this.orders;
    }
}

Интерфейс IManyToManySet<T> фактическиdundant, потому что вы можете вернуть ManyToMany<TSource, TCross, TDestination> напрямую.Однако интерфейс скрывает аргументы типа TSource и TCross, что делает его немного более читаемым для пользователя этого свойства.

Обратите внимание, что эта реализация имеет такое же поведение при загрузке, что и LINQ to SQL EntitySet<T>;Когда он используется, он загружает полный набор объектов в память.Как и в случае EntitySet<T> с использованием where или First в коллекции, все еще загружается полная коллекция из базы данных.Вы должны знать об этом.

Важное отличие состоит в том, что LINQ to SQL понимает EntitySet<T> свойства в запросах LINQ.Наличие IManyToManySet<T> внутри запроса LINQ с треском провалится.

Надеюсь, это поможет.

1 голос
/ 12 апреля 2011

Трудно (возможно, даже невозможно) создать решение в LINQ to SQL, которое выглядит как нечто родное, потому что такое решение должно работать следующим образом:

  1. Моделирование множества должно быть смоделированокак свойство в другой сущности.
  2. Он должен поддерживать вставки, обновления и удаления.
  3. Должен работать при написании запросов LINQ.

Это простонайти решение для пункта 1. Возьмем, к примеру, модель с классом Product с отношением «многие ко многим» с Order.Вы можете определить следующее свойство для класса Order:

public IEnumerable<Product> Products
{
    get { return this.OrderProducts.Select(op => op.Product); }
}

Это, однако, не работает с пунктами 2 и 3. Хотя мы могли бы создать универсальную коллекцию, которая позволяет вставлять, LINQ to SQL никогда не будетбыть в состоянии перевести использование этого свойства обратно в запрос SQL.Например, следующий запрос LINQ выглядит невинным:

var bigOrders =
    from order in context.Orders
    where order.Products.Any(p => p.Price > 100)
    select order;

К сожалению, этот запрос не будет выполнен, поскольку LINQ to SQL не знает, как сопоставить свойство Products с SQL.

Если вы хотите эту функцию изначально, вы должны рассмотреть возможность перехода на Entity Framework (4.0 или выше).EF поддерживает это изначально.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...