Автогенерация класса приложений с сильным типом

StackOverflow https://stackoverflow.com/questions/1583642

Вопрос

Вот вопрос сначала:

Это возможно? Я черпаю вдохновение из Работа Джо Вробеля (Redux забытого Кодеплекс проект) Здесь вы выполняете свою работу по созданию своего профиля для поставщика, и он выполняет работу по созданию сильной печати для него, эффективно создавая фасад для класса профиля.

А теперь блюда!

Я действительно не люблю Волшебные струны. Анкет Они довольно плохие и могут вызвать серьезные проблемы, когда дело доходит до обновления вашего приложения. Работая на таких языках, как PHP и ColdFusion, я знаю, что их легко поместить в ваше приложение и забыть о них, пока вам не понадобится его. И тогда вам приходится охотиться на каждый вариант их вниз и изменить их соответственно.

.Net действительно не намного лучше, если вы следите за шаблонами приложения «Out of the Box». Многие примеры используют приложения в Web.Config для хранения различных настройки. Это действительно прекрасное место для хранения и идеально подходит для большинства приложений. Проблемы начинают возникают, однако, когда вы начинаете их напрямую - например, ConfigurationManager.AppSettings["MyAppSetting"]. Анкет Тогда вам на самом деле не лучше, чем пользователь PHP, когда вы возвращаетесь к магическим струнам.

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

Теперь, вместо использования web.config для содержания моих приложений, я использую базу данных для их удержания. При запуске приложения извлекаются комбинации имени/значения, а затем последовательно добавляются в ConfigurationManager.AppSettings с помощью Set. Анкет Нет Biggie (кроме проблема У меня было раньше!).

Этот «фасад приложения» доступен по сложу данных, слою сервиса и уровня презентации, и удерживает такие вещи, как режим приложения, которая обслуживает конечную точку для использования Yada Yada Yada и ограничивает необходимость охоты на множество волшебных строк, до двух магических Строки - один (имя) в фасаде, а другой (имя и значение) в точке творения (что для меня - БД).

Этот фасадный класс в конечном итоге станет довольно большим, и я в конечном итоге устану обновить их обоих.

Итак, я хотел бы провести класс ApplicationFacade, который автоматически генерирует каждый раз, когда сборка готовится. А теперь вернемся к началу ... это возможно?

Это было полезно?

Решение

Вы также можете использовать шаблоны кодов для этой цели. Преимущество состоит в том, что вы можете установить в свойствах шаблонов, которые будут регенерированы в каждой сборке (SET BuildAction = "Comment")

ОтредактированЯ также искал такое решение. После Googling я нашел шаблон базового T4 для создания такого класса. Я переработал его, и вы можете найти его ниже.

Шаблон генерирует класс обертки для раздела AppSetting из вашего файла web.config/app.config

Предположим, у вас есть следующие строки настроек в файле конфигурации

  <appSettings>
    <add key="PageSize" value="20" />
    <add key="CurrentTheme" value="MyFavouriteTheme" />
    <add key="IsShowSomething" value="True" />
  </appSettings>

После шаблона обработки вы получите следующее занятие

namespace MyProject.Core
{
    /// <remarks>
    /// You can create partial class with the same name in another file to add custom properties
    /// </remarks>
    public static partial class SiteSettings 
    {
        /// <summary>
        /// Static constructor to initialize properties
        /// </summary>
        static SiteSettings()
        {
            var settings = System.Configuration.ConfigurationManager.AppSettings;
            PageSize = Convert.ToInt32( settings["PageSize"] );
            CurrentTheme = ( settings["CurrentTheme"] );
            IsShowSomething = Convert.ToBoolean( settings["IsShowSomething"] );
        }

        /// <summary>
        /// PageSize configuration value
        /// </summary>
        public static readonly int PageSize;

        /// <summary>
        /// CurrentTheme configuration value
        /// </summary>
        public static readonly string CurrentTheme;

        /// <summary>
        /// IsShowSomething configuration value
        /// </summary>
        public static readonly bool IsShowSomething;

    }
}

Сохранить следующий код в файл *.tt и включите в свой проект, где вы хотите поместить сгенерированный файл. Чтобы регенерировать класс по каждой сборке Смотрите мой ответ здесьШаблон распознает типы строки, datetime, int и bool из значений

<#@ assembly name="System.Core" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="System.Xml.Linq" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Xml.Linq" #>
<#@ import namespace="Microsoft.VisualBasic" #>
<#@ template language="VB" debug="True" hostspecific="True"  #>
<#@ output extension=".Generated.cs" #>
<#
    Dim projectNamespace as String = "MyProject.Core"
    Dim className as String = "SiteSettings"
    Dim fileName as String = "..\..\MyProject.Web\web.config"

    Init(fileName)  

#>
//------------------------------------------------------------------------------
// FileName = <#= path #>
// Generated at <#= Now.ToLocaltime() #>
//
// <auto-generated>
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
//     
//    NOTE: Please use the Add a Reference to System.Configuration assembly if 
//          you get compile errors with ConfigurationManager
// </auto-generated>
//------------------------------------------------------------------------------

using System;
using System.Configuration;

namespace <#= projectNamespace #>
{
    /// <remarks>
    /// You can create partial class with the same name in another file to add custom properties
    /// </remarks>
    public static partial class <#= className #> 
    {
        /// <summary>
        /// Static constructor to initialize properties
        /// </summary>
        static <#= className #>()
        {
            var settings = System.Configuration.ConfigurationManager.AppSettings;
<#= AddToCostructor(path) #>        }

<#= RenderApplicationSettings(path) #>  }
}

<#+ 
    Dim path as String = ""
    Dim doc as XDocument = Nothing

    Public Sub Init(fileName as String)
        Try
            path = Host.ResolvePath(fileName)
            If File.Exists(path) Then
                doc = XDocument.Load(path)
            End If
        Catch
            path = "<< App.config or Web.config not found within the project >>"
        End Try     
    End Sub

    Public Function AddToCostructor(ByVal path as String) as String                 
        If doc Is Nothing Then Return ""

        Dim sb as New StringBuilder()

        For Each result as XElement in doc...<appSettings>.<add>            
            sb.Append(vbTab).Append(vbTab).Append(vbTab)
            sb.AppendFormat("{0} = {1}( settings[""{0}""] );", result.@key, GetConverter(result.@value))
            sb.AppendLine()
        Next

        Return sb.ToString()

    End Function

    Public Function RenderApplicationSettings(ByVal path as String) as String
        If doc Is Nothing Then Return ""

        Dim sb as New StringBuilder()       

        For Each result as XElement in doc...<appSettings>.<add>    
            dim key = result.@key
            sb.Append(vbTab).Append(vbTab)
            sb.Append("/// <summary>").AppendLine()
            sb.Append(vbTab).Append(vbTab)
            sb.AppendFormat("/// {0} configuration value", key).AppendLine()            
            sb.Append(vbTab).Append(vbTab)
            sb.Append("/// </summary>").AppendLine()
            sb.Append(vbTab).Append(vbTab)
            sb.AppendFormat("public static readonly {0} {1}; ", GetPropertyType(result.@value), key)    
            sb.AppendLine().AppendLine()
        Next

        Return sb.ToString()

    End Function

    Public Shared Function GetConverter(ByVal prop as String) as String     
        If IsNumeric(prop) Then Return "Convert.ToInt32"
        If IsDate(prop) Then Return "Convert.ToDateTime"
        dim b as Boolean
        If Boolean.TryParse(prop, b) Then Return "Convert.ToBoolean"        
        Return ""
    End Function

    Public Shared Function GetPropertyType(ByVal prop as String) as String
        If IsNumeric(prop) Then Return "int"
        If IsDate(prop) Then Return "DateTime"
        dim b as Boolean
        If Boolean.TryParse(prop, b) Then Return "bool"
        Return "string"
    End Function

#>

Другие советы

Вы могли бы сделать это с предварительным шагом. Это достаточно легко сделать-просто напишите программу, сценарий или шаблон, который восстанавливает класс, и назовут ее в вашем предварительном мероприятии-но это даст вам Red Wigglies и никаких Intellisens регенерирован.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top