이를 사용하여 C#에서 람다 쿼리의 상당 부분을 교체하는 방법을 생각합니다. 지원되는 언어의 양이 늘어날 수 있기 때문에 모든 제품에는 Culture (예 : 'en-US')와 모든 번역 가능한 속성이 포함 된 ProductTranslation 컬렉션이 있습니다.엔티티 프레임 워크 :</p> <p>우리의 응용 프로그램은 제품의 테이블을 가지고, 번역 이름을 가지고 : ExpressionVisitor
도메인은 다음과 같습니다
/// <summary>
/// Marks a class as translatable, i.e. there are properties that need to be different per language, such as a name or description.
/// </summary>
/// <typeparam name="TTranslation"></typeparam>
public interface ITranslatable<TTranslation> where TTranslation: ITranslation
{
/// <summary>
/// Gets or sets the translations
/// </summary>
TranslationCollection<TTranslation> Translations { get; set; }
}
/// <summary>
/// Marks this class as a translation of another class.
/// </summary>
public interface ITranslation
{
/// <summary>
/// Gets or sets the culture
/// </summary>
string Culture { get; set; }
}
public class Product : ITranslatable<ProductTranslation>
{
private TranslationCollection<ProductTranslation> _translations;
/// <summary>
/// Gets or sets the code.
/// </summary>
public virtual string Code { get; set; }
/// <summary>
/// Gets or sets the price.
/// </summary>
public virtual decimal? Price { get; set; }
public virtual TranslationCollection<ProductTranslation> Translations
{
get { return _translations ?? (_translations = new TranslationCollection<ProductTranslation>()); }
set { _translations = value; }
}
}
/// <summary>
/// Contains the translatable properties for <see cref="Product"/>
/// </summary>
public class ProductTranslation: ITranslation
{
/// <summary>
/// Gets or sets the culture of this translation
/// </summary>
public string Culture { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
public virtual string Name { get; set; }
}
당신이다시피
, 내가 번역에 대한 사용자 지정 컬렉션 클래스를 사용하고 있습니다. (대신 기본은 ICollection의 TranslationCollection가)
이 클래스는 컬렉션을 확장하지만 현재의 UI 문화와 일치하는 번역 반환 '현재'유틸리티 속성을 추가합니다
/// <summary>
/// Contains specific methods to work with translations
/// </summary>
/// <typeparam name="TTranslation"></typeparam>
public class TranslationCollection<TTranslation>: Collection<TTranslation> where TTranslation: ITranslation
{
/// <summary>
/// Initializes an empty <see cref="TranslationCollection{TTranslation}"/>
/// </summary>
public TranslationCollection()
{
}
/// <summary>
/// Initializes a new <see cref="TranslationCollection{TTranslation}"/> with the given <paramref name="list"/> as its contents
/// </summary>
/// <param name="list"></param>
public TranslationCollection(IList<TTranslation> list) : base(list)
{
}
/// <summary>
/// Returns the translation that has the same culture as the current UI culture.
/// </summary>
public TTranslation Current
{
get
{
return this.SingleOrDefault(t => t.Culture == CultureInfo.CurrentUICulture.Name);
}
}
}
당신이 볼 수 있듯이을 거의있다 여기에 있지만, 나중에 사용자 정의 컬렉션 클래스가 필요하다는 생각이 듭니다. 나중에 표시 및 양식을위한 사용자 정의 HTML 구성 요소를 만들고 싶을 때 유용 할 수 있습니다.
이제 질문 : 우리는 제품 테이블을 쿼리 할 때
, 그 이름으로 검색은 다음과 같이 보일 것입니다 :
var products = dbContext.Products.Where(p => p.Translations.Where(t => t.Culture == CultureInfo.CurrentUICulture).Any(t => t.Name.ToLower().Contains("abc")))
그러나,로 보는 것은 번역의 많은있을 것 테이블 (앞으로는 상당히 큰 응용 프로그램입니다.)을 작성하면 매우 흥미로울 것입니다.
var products = dbContext.Products.Where(p => p.Translations.Current.Name.ToLower().Contains("abc"))
이 코드를 실행하면 '현재'속성의 매핑이 해제되고 Entity Framework에서 예외가 throw됩니다. 그러나, 자동으로 나는 첫 번째 시도를했습니다 ExpressionVisitor (또는 다른 것)
를 사용하여, 뭔가 다른 '현재'전화를 변환하는 것이 가능하지만 조금 붙어 것 :
public class CurrentTranslationVisitor: ExpressionVisitor
{
protected override Expression VisitMember(MemberExpression node)
{
if(node.Member.MemberType != MemberTypes.Property)
return base.VisitMember(node);
var propertyInfo = node.Member as PropertyInfo;
if (propertyInfo == null)
return base.VisitMember(node);
if (!typeof (ITranslation).IsAssignableFrom(propertyInfo.PropertyType))
return base.VisitMember(node);
if (!string.Equals(propertyInfo.Name, "Current"))
return base.VisitMember(node);
// at this point we can be confident that the property is [someTranslatableObject].Translations.Current
}
}
현재 시점에서 Current 속성에 대해 작성된 코드에 어떻게 액세스합니까? 표현은 내가
.Name.ToLower().Contains("abc")
제안 및 지원에 액세스 할 수있는 방법을
p => p.Translations.Current.Name.ToLower().Contains("abc")
입니다
예를 들어은 많이 주시면 감사하겠습니다!
아주 재미있는 물건을! 나는 내일 아침에 너에게 먼저 돌아갈거야, 내 머리는 오늘 표정 나무로 깊숙히 뛰어 드는 것에서 조금 흐려있다. :-) 어쨌든 당신의 대답에 감사드립니다! – Moeri
여러분의 아이디어를 완벽하게 구현하지는 못했지만 올바른 방식은 번역 속성 선택기의 표현식과 논리가 포함 된 표현식을 분리하는 것입니다. 필자는 작성 기능 대신 LinqKit을 사용했는데 (필자의 작성 패러다임보다 뛰어나고 확장하는 아이디어가 맘에 든다.) 나는 이렇게 사용자 정의 필터 클래스에 전달한다 : productFilter.Where (p => p.Translations, pt => pt.Name.Contains ("abc")) 올바른 경로에 나를 설정해 주셔서 감사합니다! – Moeri