Skip to content

Commit

Permalink
CSharp: add support for excluding models from a proxy by referencing …
Browse files Browse the repository at this point in the history
…external assembly

- Change CustomSettings to be dictionary of object to allow for array
items
- Change CSharpGenerator to be able to reference external assemblies and
namespaces when assembly is passed in on commandline (like WCF svcutil).
  • Loading branch information
jhancock93 committed Jun 13, 2016
1 parent 31e5a52 commit 01bb98d
Show file tree
Hide file tree
Showing 17 changed files with 389 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public override async Task Generate(ServiceClient serviceClient)
// Service client
var serviceClientTemplate = new AzureServiceClientTemplate
{
Model = new AzureServiceClientTemplateModel(serviceClient, InternalConstructors),
Model = new AzureServiceClientTemplateModel(serviceClient, InternalConstructors, ReferencedNamespaces),
};
await Write(serviceClientTemplate, serviceClient.Name + ".cs");

Expand All @@ -99,15 +99,15 @@ public override async Task Generate(ServiceClient serviceClient)
{
var extensionsTemplate = new ExtensionsTemplate
{
Model = new AzureExtensionsTemplateModel(serviceClient, null, SyncMethods),
Model = new AzureExtensionsTemplateModel(serviceClient, null, SyncMethods, ReferencedNamespaces),
};
await Write(extensionsTemplate, serviceClient.Name + "Extensions.cs");
}

// Service client interface
var serviceClientInterfaceTemplate = new ServiceClientInterfaceTemplate
{
Model = new AzureServiceClientTemplateModel(serviceClient, InternalConstructors),
Model = new AzureServiceClientTemplateModel(serviceClient, InternalConstructors, ReferencedNamespaces),
};
await Write(serviceClientInterfaceTemplate, "I" + serviceClient.Name + ".cs");

Expand All @@ -117,21 +117,21 @@ public override async Task Generate(ServiceClient serviceClient)
// Operation
var operationsTemplate = new AzureMethodGroupTemplate
{
Model = new AzureMethodGroupTemplateModel(serviceClient, group),
Model = new AzureMethodGroupTemplateModel(serviceClient, group, ReferencedNamespaces),
};
await Write(operationsTemplate, operationsTemplate.Model.MethodGroupType + ".cs");

// Service client extensions
var operationExtensionsTemplate = new ExtensionsTemplate
{
Model = new AzureExtensionsTemplateModel(serviceClient, group, SyncMethods),
Model = new AzureExtensionsTemplateModel(serviceClient, group, SyncMethods, ReferencedNamespaces),
};
await Write(operationExtensionsTemplate, operationExtensionsTemplate.Model.ExtensionName + "Extensions.cs");

// Operation interface
var operationsInterfaceTemplate = new MethodGroupInterfaceTemplate
{
Model = new AzureMethodGroupTemplateModel(serviceClient, group),
Model = new AzureMethodGroupTemplateModel(serviceClient, group, ReferencedNamespaces),
};
await Write(operationsInterfaceTemplate, "I" + operationsInterfaceTemplate.Model.MethodGroupType + ".cs");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ namespace Microsoft.Rest.Generator.CSharp.Azure
{
public class AzureExtensionsTemplateModel : ExtensionsTemplateModel
{
public AzureExtensionsTemplateModel(ServiceClient serviceClient, string operationName, SyncMethodsGenerationMode syncWrappers)
: base(serviceClient, operationName, syncWrappers)
public AzureExtensionsTemplateModel(ServiceClient serviceClient, string operationName, SyncMethodsGenerationMode syncWrappers, IEnumerable<string> additionalNamespaces)
: base(serviceClient, operationName, syncWrappers, additionalNamespaces)
{
MethodTemplateModels.Clear();
Methods.Where(m => m.Group == operationName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ namespace Microsoft.Rest.Generator.CSharp.Azure
{
public class AzureMethodGroupTemplateModel : MethodGroupTemplateModel
{
public AzureMethodGroupTemplateModel(ServiceClient serviceClient, string methodGroupName)
: base(serviceClient, methodGroupName)
public AzureMethodGroupTemplateModel(ServiceClient serviceClient, string methodGroupName, IEnumerable<string> additionalNamespaces)
: base(serviceClient, methodGroupName, additionalNamespaces)
{
MethodGroupType = MethodGroupName + "Operations";
// Clear base initialized MethodTemplateModels and re-populate with
Expand Down Expand Up @@ -41,6 +41,8 @@ public override IEnumerable<string> Usings
{
yield return "Models";
}
foreach (var ns in AdditionalNamespaces)
yield return ns;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ namespace Microsoft.Rest.Generator.CSharp.Azure
{
public class AzureServiceClientTemplateModel : ServiceClientTemplateModel
{
public AzureServiceClientTemplateModel(ServiceClient serviceClient, bool internalConstructors)
: base(serviceClient, internalConstructors)
public AzureServiceClientTemplateModel(ServiceClient serviceClient, bool internalConstructors, IEnumerable<string> additionalNamespaces)
: base(serviceClient, internalConstructors, additionalNamespaces)
{
// TODO: Initialized in the base constructor. Why Clear it?
MethodTemplateModels.Clear();
Expand All @@ -28,7 +28,7 @@ public override IEnumerable<MethodGroupTemplateModel> Operations
{
get
{
return MethodGroups.Select(mg => new AzureMethodGroupTemplateModel(this, mg));
return MethodGroups.Select(mg => new AzureMethodGroupTemplateModel(this, mg, AdditionalNamespaces));
}
}

Expand All @@ -50,6 +50,8 @@ public override IEnumerable<string> Usings
{
yield return "Models";
}
foreach (var ns in AdditionalNamespaces)
yield return ns;
}
}
}
Expand Down
94 changes: 87 additions & 7 deletions AutoRest/Generators/CSharp/CSharp/CSharpCodeGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Globalization;
using System.Threading.Tasks;
using Microsoft.Rest.Generator.ClientModel;
using Microsoft.Rest.Generator.CSharp.Templates;
using System.Linq;
using System.Reflection;
using Microsoft.Rest.Generator.CSharp.Properties;
using Microsoft.Rest.Generator.Logging;

namespace Microsoft.Rest.Generator.CSharp
{
Expand All @@ -19,6 +24,7 @@ public class CSharpCodeGenerator : CodeGenerator
public CSharpCodeGenerator(Settings settings) : base(settings)
{
_namer = new CSharpCodeNamer();
ReferencedNamespaces = new HashSet<string>();
IsSingleFileGenerationSupported = true;
}

Expand All @@ -36,6 +42,18 @@ public CSharpCodeGenerator(Settings settings) : base(settings)
[SettingsAlias("syncMethods")]
public SyncMethodsGenerationMode SyncMethods { get; set; }

/// <summary>
/// Allows a client to reference models from another assembly instead of generating new models
/// </summary>
[SettingsInfo("ExternalModelAssemblies")]
[SettingsAlias("modelAssemblies")]
public string[] ExternalModelAssemblies { get; set; }

/// <summary>
/// Referenced namespaces
/// </summary>
public HashSet<string> ReferencedNamespaces { get; private set; }

public override string Name
{
get { return "CSharp"; }
Expand All @@ -49,7 +67,8 @@ public override string Description

public override string UsageInstructions
{
get {
get
{
return string.Format(CultureInfo.InvariantCulture,
Properties.Resources.UsageInformation, ClientRuntimePackage);
}
Expand All @@ -72,13 +91,15 @@ public override void PopulateSettings(IDictionary<string, object> settings)
/// <param name="serviceClient"></param>
public override void NormalizeClientModel(ServiceClient serviceClient)
{
ReferencedNamespaces.UnionWith(ResolveExternalReferences(serviceClient));
PopulateAdditionalProperties(serviceClient);
Extensions.NormalizeClientModel(serviceClient, Settings);
_namer.NormalizeClientModel(serviceClient);
_namer.ResolveNameCollisions(serviceClient, Settings.Namespace,
Settings.Namespace + ".Models");
}


private void PopulateAdditionalProperties(ServiceClient serviceClient)
{
if (Settings.AddCredentials)
Expand All @@ -94,6 +115,65 @@ private void PopulateAdditionalProperties(ServiceClient serviceClient)
}
}

[SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom")]
private HashSet<string> ResolveExternalReferences(ServiceClient serviceClient)
{
List<Assembly> referenceAssemblies = new List<Assembly>();
if (ExternalModelAssemblies == null)
return new HashSet<string>();
foreach (var assemblyPath in ExternalModelAssemblies)
{
var a = Assembly.LoadFrom(assemblyPath);
if (a != null)
{
referenceAssemblies.Add(a);
}
else
{
Logger.LogError(new ArgumentException("ExternalModelAssemblies"),
Properties.Resources.ReferenceAssemblyFailure, assemblyPath);
}
}
return ResolveExternalReferences(serviceClient, referenceAssemblies);
}

/// <summary>
/// Looks for model types in external assemblies. When found, the model is removed from the serviceClient ModelTypes
/// and the external namespace is added to the returned hashset.
/// This method could potentially be moved to Extensions.cs
/// </summary>
/// <param name="serviceClient"></param>
/// <param name="referenceAssemblies"></param>
/// <returns></returns>
private static HashSet<string> ResolveExternalReferences(ServiceClient serviceClient, ICollection<Assembly> referenceAssemblies)
{
HashSet<string> modelTypesToRemove = new HashSet<string>();
HashSet<string> modelNamespaces = new HashSet<string>();
HashSet<string> modelNames = new HashSet<string>(serviceClient.ModelTypes.Select(m => m.Name));

var matchingExportedTypes =
referenceAssemblies.SelectMany(t => t.ExportedTypes).Where(t => modelNames.Contains(t.Name)).ToList();
foreach(var typeGroup in matchingExportedTypes.GroupBy(t => t.Name))
{
if (typeGroup.Count() == 1)
{
modelTypesToRemove.Add(typeGroup.Key);
modelNamespaces.Add(matchingExportedTypes[0].Namespace);
}
else if (matchingExportedTypes.Count > 1)
{
Logger.LogError(new ArgumentException("ExternalModelAssemblies"),
Properties.Resources.ConflictingTypesError, typeGroup.Key,
string.Join(", ", referenceAssemblies.Select(a => a.GetName())));
}
}
foreach (var model in modelTypesToRemove)
{
serviceClient.ModelTypes.RemoveWhere(t => t.Name.Equals(model));
}
return modelNamespaces;
}

/// <summary>
/// Generates C# code for service client.
/// </summary>
Expand All @@ -104,7 +184,7 @@ public override async Task Generate(ServiceClient serviceClient)
// Service client
var serviceClientTemplate = new ServiceClientTemplate
{
Model = new ServiceClientTemplateModel(serviceClient, InternalConstructors),
Model = new ServiceClientTemplateModel(serviceClient, InternalConstructors, ReferencedNamespaces)
};
await Write(serviceClientTemplate, serviceClient.Name + ".cs");

Expand All @@ -113,15 +193,15 @@ public override async Task Generate(ServiceClient serviceClient)
{
var extensionsTemplate = new ExtensionsTemplate
{
Model = new ExtensionsTemplateModel(serviceClient, null, SyncMethods),
Model = new ExtensionsTemplateModel(serviceClient, null, SyncMethods, ReferencedNamespaces),
};
await Write(extensionsTemplate, serviceClient.Name + "Extensions.cs");
}

// Service client interface
var serviceClientInterfaceTemplate = new ServiceClientInterfaceTemplate
{
Model = new ServiceClientTemplateModel(serviceClient, InternalConstructors),
Model = new ServiceClientTemplateModel(serviceClient, InternalConstructors, ReferencedNamespaces),
};
await Write(serviceClientInterfaceTemplate, "I" + serviceClient.Name + ".cs");

Expand All @@ -131,21 +211,21 @@ public override async Task Generate(ServiceClient serviceClient)
// Operation
var operationsTemplate = new MethodGroupTemplate
{
Model = new MethodGroupTemplateModel(serviceClient, group),
Model = new MethodGroupTemplateModel(serviceClient, group, ReferencedNamespaces),
};
await Write(operationsTemplate, operationsTemplate.Model.MethodGroupType + ".cs");

// Service client extensions
var operationExtensionsTemplate = new ExtensionsTemplate
{
Model = new ExtensionsTemplateModel(serviceClient, group, SyncMethods),
Model = new ExtensionsTemplateModel(serviceClient, group, SyncMethods, ReferencedNamespaces),
};
await Write(operationExtensionsTemplate, group + "Extensions.cs");

// Operation interface
var operationsInterfaceTemplate = new MethodGroupInterfaceTemplate
{
Model = new MethodGroupTemplateModel(serviceClient, group),
Model = new MethodGroupTemplateModel(serviceClient, group, ReferencedNamespaces),
};
await Write(operationsInterfaceTemplate, "I" + operationsInterfaceTemplate.Model.MethodGroupType + ".cs");
}
Expand Down
4 changes: 3 additions & 1 deletion AutoRest/Generators/CSharp/CSharp/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,6 @@
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Scope = "member", Target = "Microsoft.Rest.Generator.CSharp.MethodTemplateModel.#.ctor(Microsoft.Rest.Generator.ClientModel.Method,Microsoft.Rest.Generator.ClientModel.ServiceClient,Microsoft.Rest.Generator.CSharp.SyncMethodsGenerationMode)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "1", Scope = "member", Target = "Microsoft.Rest.Generator.CSharp.MethodTemplateModel.#.ctor(Microsoft.Rest.Generator.ClientModel.Method,Microsoft.Rest.Generator.ClientModel.ServiceClient,Microsoft.Rest.Generator.CSharp.SyncMethodsGenerationMode)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "Microsoft.Rest.Generator.CSharp.MethodTemplateModel.#GetAsyncMethodInvocationArgs(System.String,System.String)")]

[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:Properties should not return arrays", Scope = "member",
Target = "Microsoft.Rest.Generator.CSharp.CSharpCodeGenerator.ExternalModelAssemblies",
Justification = "The array type is used only for a setting from the command-line and is not used in performance-sensitive code")]
18 changes: 18 additions & 0 deletions AutoRest/Generators/CSharp/CSharp/Properties/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions AutoRest/Generators/CSharp/CSharp/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ConflictingTypesError" xml:space="preserve">
<value>Found multiple types that match the model name {0} in assemblies: {1}</value>
</data>
<data name="ReferenceAssemblyFailure" xml:space="preserve">
<value>Could not load assembly {0}.</value>
</data>
<data name="UsageInformation" xml:space="preserve">
<value>The {0} nuget package is required to compile the generated code.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,23 @@ namespace Microsoft.Rest.Generator.CSharp
{
public class ExtensionsTemplateModel : ServiceClient
{
public ExtensionsTemplateModel(ServiceClient serviceClient, string operationName, SyncMethodsGenerationMode syncWrappers)
public ExtensionsTemplateModel(ServiceClient serviceClient, string operationName, SyncMethodsGenerationMode syncWrappers, IEnumerable<string> additionalNamespaces)
{
this.LoadFrom(serviceClient);
MethodTemplateModels = new List<MethodTemplateModel>();
ExtensionName = operationName ?? this.Name;
this.Methods.Where(m => m.Group == operationName)
.ForEach(m => MethodTemplateModels.Add(new MethodTemplateModel(m, serviceClient, syncWrappers)));
AdditionalNamespaces = new HashSet<string>(additionalNamespaces);
}


public List<MethodTemplateModel> MethodTemplateModels { get; private set; }

public string ExtensionName { get; set; }

protected HashSet<string> AdditionalNamespaces { get; private set; }

public virtual IEnumerable<string> Usings
{
get
Expand All @@ -32,6 +35,8 @@ public virtual IEnumerable<string> Usings
{
yield return "Models";
}
foreach (var ns in AdditionalNamespaces)
yield return ns;
}
}
}
Expand Down
Loading

0 comments on commit 01bb98d

Please sign in to comment.