2011-04-12 3 views
0

다 대 다 관계를 돕기위한 지원 클래스를 갖고 싶습니다.Linq to SQL : 다 대다 지원 클래스

이제는 관계를 둘러싼 엔티티 부분 클래스 중 하나 또는 둘 모두에서 정의 할 수있는 이중 제네릭 클래스가 될 수 있습니다.

특별히 관계 테이블을 언급하지 않고도 다른 테이블에 액세스 할 수 있도록 허용하면 쉽게 수행 할 수 있습니다. 그러나 컬렉션에서 추가 또는 제거하는 것은 다소 까다 롭습니다. 관계 테이블에 행을 추가하고 완료하거나 수행 된 작업에 따라 제거해야합니다.

이 일반 클래스로 전달되는 함수를 통해 수행 할 수 있습니까?

이와 같은 클래스가 존재합니까? 그렇지 않은 경우 생존 할 수있는 무언가입니까?

답변

2

many to many 속성에서 반환 할 수있는 IManyToManySet<TEntity> 인터페이스를 만들고 쿼리 삽입 및 삭제 기능을 사용하여 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. TCrossTDestination으로 변환 할 수있는 프로젝션 함수입니다.
  4. TSourceTDestination을 기반으로 새 TCross을 만들 수있는 팩토리 함수입니다. (ProductOrder 사용) 실제 예를 들어이 번역

, 당신에게 Order 기업에 대한 다음과 같은 속성을 줄 것이다 다음 Product 기업에 대한

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; 
    } 
} 

그리고 다음 등록 정보 :

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> 인터페이스는 실제로 ManyToMany<TSource, TCross, TDestination>을 반환 할 수 있기 때문에 중복되어 있습니다. 인터페이스는 TSourceTCross 형식 인수를 숨기므로이 속성을 사용자가 좀 더 쉽게 읽을 수 있습니다.

이 구현에는 LINQ to SQL의 EntitySet<T>과 같은 로딩 동작이 있습니다. 이 함수를 사용하면 메모리에 전체 개체 집합이로드됩니다. 컬렉션에 where 또는 First을 사용하는 EntitySet<T>처럼 데이터베이스에서 전체 컬렉션을로드합니다. 당신은 그 사실을 알고 있어야합니다.

그러나 중요한 차이점은 LINQ to SQL이 LINQ 쿼리 내에서 EntitySet<T> 속성을 인식한다는 것입니다. IManyToManySet<T>을 LINQ 쿼리 내에 포함하면 비참하게 실패합니다.

이 정보가 도움이되기를 바랍니다.

+0

한가지 내가 약간의 문제가 있었는데 그 관계는 행을 제거하는 대신 크로스 테이블에서 외래 키를 null로 null을 시도 했으므로 관계를 제거하는 것이 었습니다. 부모 항목 엔티티 집합에서 제거 될 때 행이 삭제되도록하는 연관에 대해 DeleteOnNull을 설정할 수 있음을 알았습니다. –

+0

나는 계획이 함께 할 때 그것을 사랑한다! – Steven

1

그것은 이러한 솔루션은 다음과 같은 방법으로 작업을해야하기 때문에, 기본 뭔가 느낌 SQL에 LINQ에서 솔루션을 만들기 위해 (어쩌면 불가능) 어려운 : 많은 모음이로 모델링한다

  1. 다른 많은 엔티티의 속성.
  2. 삽입, 업데이트 및 삭제를 지원해야합니다.
  3. LINQ 쿼리를 작성할 때 작동해야합니다.

예를 들어 포인트 1. 테이크 Order와 다 대다 관계가있는 Product 클래스 모델에 대한 해결책을 쉽게 만들 수 있습니다. 당신은 Order 클래스에 다음과 같은 속성을 정의 할 수 있습니다 :

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

이 그러나 우리가 삽입 허용하는 일반적인 수집, SQL에 LINQ는 번역 할 수 없을 것를 할 수 있지만 포인트 2와 3에서는 작동하지 않습니다 이 속성을 SQL 쿼리에 다시 사용합니다. 예를 들어, 다음 LINQ 쿼리는 무고한 같습니다

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

불행하게도,이 쿼리가 실패합니다, SQL에 Products 속성을 매핑하는 방법을 모르는 SQL에 LINQ 때문이다.

이 기능을 기본적으로 사용하려면 Entity Framework (4.0 이상) 로의 마이그레이션을 고려해야합니다. EF는이를 기본적으로 지원합니다.

+0

나는 다음 프로젝트에서 EF를 사용할 것이다. 그러나 제한된 방법으로 다른 하나를 보지 못하게한다고 가정 해 봅시다. 기본적으로 릴레이션의 다른 쪽 (예제의 제품)에있는 객체의 목록을 가져 와서 자동으로 관계를 작성하는 콜렉션에 곧바로 삽입, 업데이트 및 삭제할 수 있습니다. Order 클래스의 함수를 추가 등을 처리하는 커스텀 컬렉션 클래스에 전달함으로써 그렇게 할 수 있을까요? –

+0

@ Ingó : 내 두 번째 대답을 참조하십시오. 내가 할 수있는 일반적인 클래스의 구현을 썼다. – Steven