2016-08-05 4 views
2

Я пытаюсь обрабатывать несколько языков в приложении ASP.NET Webforms (.NET 4.5, C#).Автоматическая обработка многоязычных свойств в .NET-классе

В принципе, некоторые из моих сущностей в моей базе данных SQL Server 2012 имеют такие свойства, как Name или Description, которые существуют на трех языках - немецком, французском, итальянском.

Данные хранятся в колонках Name_De (немецкий), Name_Fr (французский) и Name_It (итальянский).

Когда я создаю свои объекты .NET из базы данных, конечно, я также получаю эти три свойства в классе сущности. Но для отображения на экране, например, в сетке было бы неплохо, если бы я как-то «волшебным образом» всегда показывал «правильный» язык. Это должно быть основано на Thread.Current.CurrentUICulture.TwoLetterISOLanguageName (который возвращает de, fr или it, в зависимости от языковых настроек браузера).

Так что я надеялся как-то создать, например. атрибут .NET, который позволил бы мне сделать что-то вроде этого:

Base "Модуль" сущностей - сгенерированный из существующей базы данных SQL Server:

public partial class Module 
{ 
    public string ModuleCode { get; set; } 

    public string Name_De { get; set; } 
    public string Name_Fr { get; set; } 
    public string Name_It { get; set; } 

    ... // other properties 
} 

Частичное расширение в отдельном файле

public partial class Module 
{ 
    [Multilingual] 
    public string Name { get; set; } 
} 

Основная идея: я могу получить доступ к свойству Module.Name, и в зависимости от текущей настройки CurrentUICulture либо значение Name_De, Name_Fr или Name_It было бы выбрано, wh ru Я получаю доступ к получателю свойства Name.

Может ли что-то подобное сделать на C#? Я просмотрел множество пользовательских реализаций атрибутов, но ничто никогда не делало что-то вроде этого ...

ответ

2

Предполагая, что вы используете два отдельных объекта (один из которых генерируется вашими сущностями SQL и одним «бизнес-объектом», который только содержит свойство Name), вы можете использовать что-то вроде AutoMapper?

Если вы хотите, вы можете настроить свою функцию разрешения для отображения объекта в зависимости от текущей культуры потоков.

switch(Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName.ToUpperInvariant()) 
    { 
     case "DE": 
      return dto.Name_De; 
     case "FR": 
      return dto.Name_Fr; 
     // ... 
     default : 
      return String.Empty; 
    } 

который будет работать для вашего сценария.

Если это решение, которое может работать для вас, я думаю, что этот вопрос очень близко к тому, что вы ищете: Automapper Mapping for Localization Resolver in a Multi-Language Website

Если идти вниз маршрут пользовательского атрибута, вы будете иметь дело Я боюсь, что с материалом Reflection и синтаксисом строк. AFAIK, нет встроенного способа сделать это с помощью функций локализации, предоставляемых .NET. AutoMapper скроет это от вас.

Проблема с пользовательскими атрибутами в этом случае заключается в том, что вы по-прежнему пытаетесь получить доступ к свойству Name. Вы пытаетесь «затенять» поведение свойства по умолчанию, предоставляя ему доступ к другим свойствам.Если я правильно понимаю, что вы хотите Multilingual пользовательский атрибут, чтобы превратить вашу собственность в:

public String Name 
{ 
    get 
    { switch(Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName.ToUpperInvariant()) 
     { 
      case "DE": 
       return dto.Name_De; 
      case "FR": 
       return dto.Name_Fr; 
      // ... 
      default : 
       return String.Empty; 
     } 
    } 
} 

Если это правильно, то вы не будете в состоянии сделать это легко с атрибутами, просто потому, что атрибут никогда не будет знать существование свойства Name_De.

+0

Я уверен, что смогу это сделать, но сделать это для нескольких классов, и до трех групп свойств для каждого класса кажется слишком утомительным. И да - я думал об использовании рефлексии в этом атрибуте - зная, что он привязан к свойству «Имя», он может тогда искать свойства «Name_De» и т. Д. И возвращать «правильное» значение. Но я не нашел подходящего базового класса для атрибута «Атрибут», который каким-то образом позволил бы мне узнать, когда вызывается получатель этого свойства .... –

+0

На самом деле это два отдельных файла, но оба являются «общедоступными» partial class Module ", поэтому компилятор C# объединит их вместе в единый объект в конце –

+1

Да, это утомительно. Пользовательские атрибуты - это метаданные поверх вашего свойства, но вы не сможете выполнять «Object.Name» и вызываете свой код пользовательского атрибута. Я попытаюсь подумать о другом способе сделать это без automapper –

1

Другой вариант, который до сих пор не совсем то, что вы ищете:

void Main() 
{ 
    Module mod = new Module(); 

    mod.Name_De = "name"; 
    mod.Name_Fr = "nom"; 

    // This is the unfortunate nasty bit. I address the property using its name 
    // in a String which is just bad. I don't think there is a way 
    // you will be able to address the ".Name" property directly and have 
    // it return the localized value through your custom attribute though 
    String localizedValue = mod.GetLocalizedProperty("Name"); 
} 


[AttributeUsage(AttributeTargets.Property)] 
public sealed class MultilingualAttribute : Attribute 
{ 
    public MultilingualAttribute() 
    { 

    } 
} 

public static class ModuleExtensions 
{ 
    public static String GetLocalizedProperty(this Module module, String propName) 
    { 
     var type = typeof(Module); 
     var propInfo = type.GetProperty(propName); 

     // Make sure the prop really is "Multilingual" 
     if(Attribute.IsDefined(propInfo, typeof(MultilingualAttribute))) 
     { 
      String localizedPropName = propInfo.Name; 
      switch(Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName.ToUpperInvariant()) 
      { 
       case "DE": 
        localizedPropName += "_De"; 
        return type.GetProperty(localizedPropName).GetValue(module, null).ToString(); 
       case "FR": 
        localizedPropName += "_Fr"; 
        return type.GetProperty(localizedPropName).GetValue(module, null).ToString(); 
      } 
     } 

     return String.Empty; 
    } 
} 

public class Module 
{ 
    public String Name_De {get; set;} 
    public String Name_Fr {get; set;} 

    [Multilingual] 
    public String Name {get; set;} 

    public Module() 
    { 

    } 
} 

Я не знаю более мощный способ использовать пользовательские атрибуты для того, что вы ищете, к сожалению. Честно говоря, я не думаю, что это хорошее решение, только опубликовано, потому что я пытался понять, что я могу сделать с пользовательскими атрибутами. Нет смысла использовать этот код для более «нормального» свойства, которое будет делать то же самое более четко (без атрибутов). Как вы говорите в своем первоначальном вопросе, ваша цель - перехватить вызов свойства Name, и это не достигает его.

+0

Спасибо - да, это то, что я достиг до сих пор - это работает, и это проще, чем писать нудный код для каждого отдельного свойства. Таким образом, кажется, что ASP.NET Webforms не имеет никакого способа обработки действий с атрибутами (например, атрибут HandleError в ASP.NET MVC) - правильно? Большое спасибо за ваши материалы! –

+0

Исправить. ActionFilters в MVC на самом деле являются вводящими в заблуждение, потому что они кажутся атрибутами, которые выполняют код (чего вы пытаетесь достичь здесь). Это не так. ActionFilters работают, потому что MVC-движок запускает события, которые ищут атрибуты и выполняют код в зависимости от их присутствия (или нет). –