in src/AutoRest.CSharp/Common/Output/Models/Types/ModelTypeProviderFields.cs [37:157]
public ModelTypeProviderFields(IReadOnlyList<InputModelProperty> properties, string modelName, InputModelTypeUsage inputModelUsage, TypeFactory typeFactory, ModelTypeMapping? modelTypeMapping, InputType? additionalPropertiesValueType, bool isStruct, bool isPropertyBag)
{
var fields = new List<FieldDeclaration>();
var fieldsToInputs = new Dictionary<FieldDeclaration, InputModelProperty>();
var publicParameters = new List<Parameter>();
var serializationParameters = new List<Parameter>();
var parametersToFields = new Dictionary<string, FieldDeclaration>();
var visitedMembers = new HashSet<ISymbol>(SymbolEqualityComparer.Default);
foreach (var inputModelProperty in properties)
{
var originalFieldName = BuilderHelpers.DisambiguateName(modelName, inputModelProperty.Name.ToCleanName(), "Property");
var propertyType = GetPropertyDefaultType(inputModelUsage, inputModelProperty, typeFactory);
// We represent property being optional by making it nullable (when it is a value type)
// Except in the case of collection where there is a special handling
var optionalViaNullability = inputModelProperty is { IsRequired: false} && inputModelProperty.Type is not InputNullableType &&
!propertyType.IsCollection;
var existingMember = modelTypeMapping?.GetMemberByOriginalName(originalFieldName);
var serializationFormat = SerializationBuilder.GetSerializationFormat(inputModelProperty.Type);
var field = existingMember is not null
? CreateFieldFromExisting(existingMember, propertyType, GetPropertyInitializationValue(propertyType, inputModelProperty), serializationFormat, typeFactory, inputModelProperty.IsRequired, optionalViaNullability)
: CreateField(originalFieldName, propertyType, inputModelUsage, inputModelProperty, isStruct, isPropertyBag, optionalViaNullability);
if (existingMember is not null)
{
visitedMembers.Add(existingMember);
}
fields.Add(field);
fieldsToInputs[field] = inputModelProperty;
var parameterName = field.Name.ToVariableName();
var parameterValidation = GetParameterValidation(field, inputModelProperty);
var parameter = new Parameter(
Name: parameterName,
Description: FormattableStringHelpers.FromString(BuilderHelpers.EscapeXmlDocDescription(DocHelpers.GetDescription(inputModelProperty.Summary, inputModelProperty.Doc) ?? string.Empty)),
Type: field.Type,
DefaultValue: null,
Validation: parameterValidation,
Initializer: null);
parametersToFields[parameter.Name] = field;
// all properties should be included in the serialization ctor
serializationParameters.Add(parameter with { Validation = ValidationType.None });
// for classes, only required + not readonly + not constant + not discriminator could get into the public ctor
// for structs, all properties must be set in the public ctor
if (isStruct || inputModelProperty is { IsRequired: true, IsDiscriminator: false, ConstantValue: null })
{
// [TODO]: Provide a flag to add read/write properties to the public model constructor
if (Configuration.Generation1ConvenienceClient || !inputModelProperty.IsReadOnly)
{
publicParameters.Add(parameter with { Type = parameter.Type.InputType });
}
}
}
if (additionalPropertiesValueType is not null)
{
// We use a $ prefix here as AdditionalProperties comes from a swagger concept
// and not a swagger model/operation name to disambiguate from a possible property with
// the same name.
var existingMember = modelTypeMapping?.GetMemberByOriginalName("$AdditionalProperties");
var type = CreateAdditionalPropertiesPropertyType(typeFactory, additionalPropertiesValueType);
if (!inputModelUsage.HasFlag(InputModelTypeUsage.Input))
{
type = type.OutputType;
}
var name = existingMember is null ? "AdditionalProperties" : existingMember.Name;
var declaration = new CodeWriterDeclaration(name);
declaration.SetActualName(name);
var accessModifiers = existingMember is null ? Public : GetAccessModifiers(existingMember);
var additionalPropertiesField = new FieldDeclaration($"Additional Properties", accessModifiers | ReadOnly, type, type, declaration, null, false, SerializationFormat.Default, true);
var additionalPropertiesParameter = new Parameter(name.ToVariableName(), $"Additional Properties", type, null, ValidationType.None, null);
// we intentionally do not add this field into the field list to avoid cyclic references
serializationParameters.Add(additionalPropertiesParameter);
if (isStruct)
{
publicParameters.Add(additionalPropertiesParameter with { Validation = ValidationType.AssertNotNull });
}
parametersToFields[additionalPropertiesParameter.Name] = additionalPropertiesField;
AdditionalProperties = additionalPropertiesField;
}
// adding the leftover members from the source type
if (modelTypeMapping is not null)
{
foreach (var existingMember in modelTypeMapping.GetPropertiesWithSerialization())
{
if (visitedMembers.Contains(existingMember))
{
continue;
}
var existingCSharpType = BuilderHelpers.GetTypeFromExisting(existingMember, typeof(object), typeFactory);
// since the property doesn't exist in the input type, we use type of existing member both as original and field type
// the serialization will be generated for this type and it might has issues if the type is not recognized properly.
// but customer could always use the `CodeGenMemberSerializationHooks` attribute to override those incorrect serialization/deserialization code.
var field = CreateFieldFromExisting(existingMember, existingCSharpType, null, SerializationFormat.Default, typeFactory, false, false);
var parameter = new Parameter(field.Name.ToVariableName(), $"", field.Type, null, ValidationType.None, null);
fields.Add(field);
serializationParameters.Add(parameter);
}
}
_fields = fields;
_fieldsToInputs = fieldsToInputs;
_parameterNamesToFields = parametersToFields;
PublicConstructorParameters = publicParameters;
SerializationParameters = serializationParameters;
}