2015-07-28 60 views
1

我有一個c#應用程序公開一個API(通過WebAPI或WCF)和一個單獨的DLL合同。合約DLL由NuGet公開,以便調用者在調用應用程序時可以使用合約對象。如何生成簡單的合同類

但是,契約類使用自定義驗證屬性進行修飾,其中許多屬性都依賴於存儲庫dll等,我不想將它們包含在合同NuGet中。我基本上希望發佈合約的簡化形式,我可以接收它並反序列化爲原始合同對象(如果需要,可以使用ValueInjecter)。

我無法找到這個開箱即用的解決方案,所以我開始編寫一個t4轉換文件來運行合約程序集並創建新的簡化類,但沒有所有驗證屬性。這變得非常乏味,我希望有人在那裏有更好的解決方案。我很難相信我是第一個遇到這個問題的人。

編輯:感謝您的反饋意見。現在,我寫的T4工作得很好,只是生成我需要的簡單類。好的是雖然調用者正在發送簡化類,但它正在被反序列化爲原始類。

我已經包含了低於基準.ttinclude文件中的代碼:

<#@ assembly name="System.Core" #> 
<#@ assembly name="System.Runtime.Serialization, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" #> 
<#@ assembly name="System.Xml" #> 
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #> 
<#@ import namespace="System.Linq" #> 
<#@ import namespace="System.IO" #> 
<#@ import namespace="System.Collections.Generic" #> 
<#@ import namespace="System.Reflection" #> 
<#@ import namespace="System.Text"#> 
<#@ include file="EF.Utility.CS.ttinclude"#> 

<#+ 
    private class DtoGenerator 
    { 
     private static readonly Lazy<System.Resources.ResourceManager> ResourceManager = 
      new Lazy<System.Resources.ResourceManager>(
       () => new System.Resources.ResourceManager("System.Data.Entity.Design", typeof(MetadataItemCollectionFactory).Assembly), isThreadSafe: true); 

     private readonly TextTransformation textTransform; 
     private readonly EntityFrameworkTemplateFileManager fileManager; 
     private readonly ITextTemplatingEngineHost Host; 

     public DtoGenerator(object textTransform, ITextTemplatingEngineHost host) 
     { 
      this.textTransform = textTransform as TextTransformation; 
      this.fileManager = EntityFrameworkTemplateFileManager.Create(textTransform); 
      this.Host = host; 
     } 
     public void Write(string text) 
     { 
      this.textTransform.Write(text); 
     } 

     public void Generate(string folderName, string assemblyName) 
     { 
      var inputAssembly = GetAssembly(folderName, assemblyName); 

      if (inputAssembly == null) 
      { 
       Write("// No assembly found"); 
       return; 
      } 

      var classes = new Dictionary<Type, string>(); 
      var types = inputAssembly.GetTypes() 
       .Where(type => type.GetCustomAttributes(false).Any(o => o.GetType().Name == "GenerateDtoAttribute")) //This is a custom attribute that marks classes to be generated 
       .ToList(); 

      if (!types.Any()) 
      { 
       Write("// No types found"); 
       return; 
      } 
      foreach (var type in types) 
      { 
       var baseExtension = ""; 
       var baseClass = type.BaseType; 
       if (baseClass != null && baseClass != typeof(object)) 
       { 
        baseExtension = @" : " + baseClass; 
       } 

       var currentClass = string.Concat(@" 
namespace ", type.Namespace, @" 
{ 
    public class ", type.Name, baseExtension, @" 
    { 
", WritePublicProperties(type), @" 
    } 
}"); 
       classes.Add(type, currentClass); 
      } 

      foreach (var @class in classes) 
      { 
       fileManager.StartNewFile(@class.Key.Name + ".cs"); 
       Write(GetHeader()); 
       Write(@class.Value); 
      } 

      fileManager.Process(); 
     } 

     private Assembly GetAssembly(string folderName, string assemblyName) 
     { 
      var relativePathTemplate = string.Format(@"..\{0}\bin\{{0}}\{1}.dll", folderName, assemblyName); 
      string debugPath, 
       releasePath, 
       debugRelativePath = string.Format(relativePathTemplate, "debug"), 
       releaseRelativePath = string.Format(relativePathTemplate, "release"); 

      try 
      { 
       debugPath = Path.GetFullPath(Host.ResolvePath(debugRelativePath)); 
      } 
      catch (FileNotFoundException) 
      { 
       debugPath = null; 
      } 
      try 
      { 
       releasePath = Path.GetFullPath(Host.ResolvePath(releaseRelativePath)); 
      } 
      catch (FileNotFoundException) 
      { 
       releasePath = null; 
      } 

      if (debugPath == null && releasePath == null) 
      { 
       return null; 
      } 

      var debugDllDate = debugPath == null ? DateTime.MinValue : File.GetLastWriteTime(debugPath); 
      var releaseDllDate = releasePath == null ? DateTime.MinValue : File.GetLastWriteTime(releasePath); 

      var assemblyDllPath = debugDllDate > releaseDllDate ? debugPath : releasePath; 
      if (assemblyDllPath == null) return null; 
      var assembly = Assembly.LoadFrom(assemblyDllPath); 

      return assembly; 
     } 

     private string GetHeader() 
     { 
      var header = string.Format(
       @"//------------------------------------------------------------------------------ 
// <auto-generated> 
// {0} 
// 
// {1} 
// {2} 
// </auto-generated> 
//------------------------------------------------------------------------------", 
       ResourceManager.Value.GetString("Template_GeneratedCodeCommentLine1", null), 
       ResourceManager.Value.GetString("Template_GeneratedCodeCommentLine2", null), 
       ResourceManager.Value.GetString("Template_GeneratedCodeCommentLine3", null)); 

      return header; 
     } 

     private string WritePublicProperties(Type type) 
     { 
      var properties = 
       type.GetProperties() 
        .Where(
         info => info.DeclaringType == type 
          && info.GetMethod != null && info.GetMethod.IsPublic 
          && info.SetMethod != null && info.SetMethod.IsPublic) 
        .ToList(); 

      var propertyStrings = (from propertyInfo in properties 
       let typeString = GetTypeName(propertyInfo.PropertyType) 
       select string.Concat(@"  public ", typeString, " ", propertyInfo.Name, @" { get; set; }")).ToList(); 

      return string.Join("\r\n", propertyStrings); 
     } 

     private string GetTypeName(Type type) 
     { 
      if (type.IsGenericType) 
       return type.ToString().Split('`')[0] + "<" + string.Join(", ", type.GetGenericArguments().Select(GetTypeName).ToArray()) + ">"; 

      return type.ToString(); 
     } 
    } 

    #> 

回答

0

我有一個similair那種情況下,我有模型和我送這個模型從後臺,在服務總線,直到一個前臺和網站。我所做的是在3個不同的命名空間中有3個幾乎相同的POCO類。

現在,主辦單位中的模型具有來自JSON.NET命名空間的屬性,這些屬性決定了它如何被序列化。 servicebus命名空間中的模型可能有一些其他屬性,如iscached或其他。類似地,後端模型具有用於在持久性[Required]和事物上進行驗證的屬性。

很顯然,我不希望JSON.NET屬性保存在數據庫或服務總線上。我也不想在前端或服務總線中使用System.ComponentModel.DataAnnotation屬性。因此,我將每層的模型映射到下一層的名稱空間(取決於數據流的方向)。

使用每個圖層上的AutoMapper完成映射 - 如果模型非常相似,則這可以是單線圖。因此,來自JSON的傳入消息被反序列化爲FrontOffice模型,然後轉換爲ServiceBus模型並放置在總線上。 BackOffice反序列化ServiceBus模型並將其映射到BackOffice模型......無論如何 - 你明白了。

你也許可以做同樣的設置 - 你的NuGet庫是非常乾淨的POCO庫,幾乎沒有依賴關係。在它後面有一個高度相似的模型圖層,您可以將傳入的消息映射到該圖層上。然後,您對其執行豐富的驗證。

它看起來像重複,但對我來說它工作得很好,因爲我們有解耦模型。例如,這也會將BackOffice的ALM與FrontOffice解耦(只要映射仍然有效)。如果您採用T4方法 - 您的API模型與您的其他模型緊密耦合,這可能會成爲未來的問題。