diff --git a/CHANGELOG.md b/CHANGELOG.md index 15f2605cf..61d2c3c45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Fix error in .NET 9 thrown by vite development middleware if the HTTPS cert directory doesn't exist. - Fix `ViewModel.$load` and `ListViewModel.$load` not properly working with `.useResponseCaching()`. +- Entities that own multiple one-to-one relationships should no longer throw errors when generating. # 5.3.1 diff --git a/docs/modeling/model-components/data-sources.md b/docs/modeling/model-components/data-sources.md index 2496694d9..a87ce6c8a 100644 --- a/docs/modeling/model-components/data-sources.md +++ b/docs/modeling/model-components/data-sources.md @@ -258,7 +258,7 @@ Given a property and a client-provided string value, perform some filtering on t -Applies filters to the query based on the specified search term. See [[Search]](/modeling/model-components/attributes/search.md) for a detailed look at how searching works in Coalesce. +Applies predicates to the query based on the search term in `parameters.Search`. See [[Search]](/modeling/model-components/attributes/search.md) for a detailed look at how searching works in Coalesce. diff --git a/docs/modeling/model-types/entities.md b/docs/modeling/model-types/entities.md index 1af64a3ec..a2a08a3dd 100644 --- a/docs/modeling/model-types/entities.md +++ b/docs/modeling/model-types/entities.md @@ -26,6 +26,27 @@ In cases where the foreign key is not named after the navigation property with ` Collection navigation properties can be used in a straightforward manner. In the event where the inverse property on the other side of the relationship cannot be determined, `[InversePropertyAttribute]` will need to be used. [EF Core provides documentation](https://learn.microsoft.com/en-us/ef/core/modeling/relationships/mapping-attributes#inversepropertyattribute) on how to use this attribute. Errors will be displayed at generation time if an inverse property cannot be determined without the attribute. +#### One-to-one Relationships +One-to-one relationships can be represented in Coalesce, but require fairly specific configuration to satisfy both EF and Coalesce's needs. Specifically, the dependent/child side of the one-to-one (the entity whose PK is also a FK), must explicitly annotate its PK with `[ForeignKey]` pointing at the parent navigation property. For example: + +``` c# +public class OneToOneParent +{ + public int Id { get; set; } + + public OneToOneChild? Child { get; set; } +} + +public class OneToOneChild +{ + [Key, ForeignKey("Parent")] + public int ParentId { get; set; } + + public OneToOneParent? Parent { get; set; } +} +``` + + ### Attributes Coalesce provides a number of C# attributes that can be used to decorate your model classes and their properties in order to customize behavior, appearance, security, and more. Coalesce also supports a number of annotations from `System.ComponentModel.DataAnnotations`. diff --git a/src/IntelliTect.Coalesce.CodeGeneration.Vue/Generators/Scripts/TsMetadata.cs b/src/IntelliTect.Coalesce.CodeGeneration.Vue/Generators/Scripts/TsMetadata.cs index 0cf18f841..1ca3cb07e 100644 --- a/src/IntelliTect.Coalesce.CodeGeneration.Vue/Generators/Scripts/TsMetadata.cs +++ b/src/IntelliTect.Coalesce.CodeGeneration.Vue/Generators/Scripts/TsMetadata.cs @@ -242,7 +242,8 @@ private void WriteClassPropertyMetadata(TypeScriptCodeBuilder b, ClassViewModel case PropertyRole.ReferenceNavigation: // TS Type: "ModelReferenceNavigationProperty" b.StringProp("role", "referenceNavigation"); - b.Line($"get foreignKey() {{ return {GetClassMetadataRef(model)}.props.{prop.ForeignKeyProperty.JsVariable} as ForeignKeyProperty }},"); + // Note: `prop.ForeignKeyProperty.Role` might be PrimaryKey, not ForeignKey, in the case of 1-to-1 relationships. + b.Line($"get foreignKey() {{ return {GetClassMetadataRef(model)}.props.{prop.ForeignKeyProperty.JsVariable} as {prop.ForeignKeyProperty.Role}Property }},"); b.Line($"get principalKey() {{ return {GetClassMetadataRef(prop.Object)}.props.{prop.Object.PrimaryKey.JsVariable} as PrimaryKeyProperty }},"); if (prop.InverseProperty != null) diff --git a/src/IntelliTect.Coalesce.Tests/TargetClasses/TestDbContext/OneToOne.cs b/src/IntelliTect.Coalesce.Tests/TargetClasses/TestDbContext/OneToOne.cs new file mode 100644 index 000000000..a1030a3d5 --- /dev/null +++ b/src/IntelliTect.Coalesce.Tests/TargetClasses/TestDbContext/OneToOne.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IntelliTect.Coalesce.Tests.TargetClasses.TestDbContext +{ + public class OneToOneParent + { + public int Id { get; set; } + + // [InverseProperty(nameof(Child1.Parent))] + public OneToOneChild1 Child1 { get; set; } + + [InverseProperty(nameof(Child2.Parent))] // Can be specified, but not required. + public OneToOneChild2 Child2 { get; set; } + } + + public class OneToOneChild1 + { + [Key, ForeignKey("Parent")] + public int ParentId { get; set; } + public OneToOneParent Parent { get; set; } + } + + public class OneToOneChild2 + { + [Key, ForeignKey("Parent")] + public int ParentId { get; set; } + public OneToOneParent Parent { get; set; } + } +} diff --git a/src/IntelliTect.Coalesce.Tests/TargetClasses/TestDbContext/TestDbContext.cs b/src/IntelliTect.Coalesce.Tests/TargetClasses/TestDbContext/TestDbContext.cs index df8fad6ca..2a605147c 100644 --- a/src/IntelliTect.Coalesce.Tests/TargetClasses/TestDbContext/TestDbContext.cs +++ b/src/IntelliTect.Coalesce.Tests/TargetClasses/TestDbContext/TestDbContext.cs @@ -36,6 +36,10 @@ public class AppDbContext : DbContext [InternalUse] public DbSet Internals { get; set; } + public DbSet OneToOneParents { get; set; } + public DbSet OneToOneChild1s { get; set; } + public DbSet OneToOneChild2s { get; set; } + public AppDbContext() : this(Guid.NewGuid().ToString()) { } public AppDbContext(string memoryDatabaseName) diff --git a/src/IntelliTect.Coalesce.Tests/Tests/TypeDefinition/PropertyViewModelTests.cs b/src/IntelliTect.Coalesce.Tests/Tests/TypeDefinition/PropertyViewModelTests.cs index 3425eec2b..c65af941b 100644 --- a/src/IntelliTect.Coalesce.Tests/Tests/TypeDefinition/PropertyViewModelTests.cs +++ b/src/IntelliTect.Coalesce.Tests/Tests/TypeDefinition/PropertyViewModelTests.cs @@ -7,6 +7,7 @@ using System.Text; using Xunit; using static IntelliTect.Coalesce.DataAnnotations.DateTypeAttribute; +using static System.Runtime.InteropServices.JavaScript.JSType; namespace IntelliTect.Coalesce.Tests.TypeDefinition { @@ -283,5 +284,33 @@ public void Comment_IsCorrect(PropertyViewModelData data, string expected) PropertyViewModel vm = data; Assert.Equal(expected, vm.Comment, ignoreLineEndingDifferences: true); } + + + [Theory] + [PropertyViewModelData(nameof(OneToOneParent.Child1))] + [PropertyViewModelData(nameof(OneToOneParent.Child2))] + public void OneToOne_ParentNavigations_HasCorrectMetadata(PropertyViewModelData data) + { + PropertyViewModel vm = data; + Assert.Equal(PropertyRole.ReferenceNavigation, vm.Role); + Assert.Equal(vm.Parent.PropertyByName(nameof(OneToOneParent.Id)), vm.ForeignKeyProperty); + Assert.Equal(nameof(OneToOneChild1.Parent), vm.InverseProperty.Name); + } + + [Theory] + [PropertyViewModelData(nameof(OneToOneChild1.Parent))] + [PropertyViewModelData(nameof(OneToOneChild2.Parent))] + public void OneToOne_ChildNavigations_HasCorrectMetadata(PropertyViewModelData data) + { + PropertyViewModel vm = data; + Assert.Equal(PropertyRole.ReferenceNavigation, vm.Role); + Assert.Equal(vm.Parent.PropertyByName("ParentId"), vm.ForeignKeyProperty); + + // We currently assume that reverence inverses are only ever collection navigations. + // This could change in the future, but at least for now the typescript types don't allow + // for representing this here, which is fine and doesn't currently hurt anything. + Assert.Null(vm.InverseProperty); + } + } } diff --git a/src/IntelliTect.Coalesce/TypeDefinition/PropertyViewModel.cs b/src/IntelliTect.Coalesce/TypeDefinition/PropertyViewModel.cs index d58a3b356..93ff54b05 100644 --- a/src/IntelliTect.Coalesce/TypeDefinition/PropertyViewModel.cs +++ b/src/IntelliTect.Coalesce/TypeDefinition/PropertyViewModel.cs @@ -509,6 +509,15 @@ public PropertyViewModel? ForeignKeyProperty ?? Name + ConventionalIdSuffix; prop = EffectiveParent.PropertyByName(name); + + if (prop == null && InverseProperty?.Role == PropertyRole.ReferenceNavigation) + { + // If the other side of the relationship is also a reference navigation, + // this is a 1-to-1. If there's otherwise no FK specified, assume that the PK + // of this type is the FK we're looking for here. + // See test models "OneToOneParent"/"OneToOneChild1" for example. + return EffectiveParent.PrimaryKey; + } } if (prop == null || !prop.Type.IsValidKeyType || !prop.IsDbMapped) @@ -541,7 +550,7 @@ public PropertyViewModel? ReferenceNavigationProperty this.GetAttributeValue(a => a.Name) // Use the ForeignKey Attribute on the object property if it is there. - ?? EffectiveParent.Properties.SingleOrDefault(p => Name == p.GetAttributeValue(a => a.Name))?.Name + ?? EffectiveParent.Properties.FirstOrDefault(p => Name == p.GetAttributeValue(a => a.Name))?.Name // Else, by convention remove the Id at the end. ?? (Name.EndsWith(ConventionalIdSuffix) ? Name.Substring(0, Name.Length - ConventionalIdSuffix.Length) : null); diff --git a/src/IntelliTect.Coalesce/Validation/ValidateContext.cs b/src/IntelliTect.Coalesce/Validation/ValidateContext.cs index 5e8e8d5c1..1e9e01571 100644 --- a/src/IntelliTect.Coalesce/Validation/ValidateContext.cs +++ b/src/IntelliTect.Coalesce/Validation/ValidateContext.cs @@ -168,7 +168,7 @@ public static ValidationHelper Validate(ReflectionRepository repository) } catch (Exception ex) { - assert.IsTrue(false, $"Exception: {ex.Message}"); + assert.IsTrue(false, $"Exception: {ex.ToString()}"); } } diff --git a/src/coalesce-vue/src/metadata.ts b/src/coalesce-vue/src/metadata.ts index b8cc35b53..9cd948a26 100644 --- a/src/coalesce-vue/src/metadata.ts +++ b/src/coalesce-vue/src/metadata.ts @@ -490,7 +490,9 @@ export interface ModelReferenceNavigationProperty extends PropertyBase, ModelValue { readonly role: "referenceNavigation"; - readonly foreignKey: ForeignKeyProperty; + // Note: This can be a PrimaryKey in a 1-to-1 situation where the child record's + // PK is the foreign key into the parent's type. + readonly foreignKey: ForeignKeyProperty | PrimaryKeyProperty; readonly principalKey: PrimaryKeyProperty; readonly inverseNavigation?: ModelCollectionNavigationProperty; } diff --git a/src/test-targets/api-clients.g.ts b/src/test-targets/api-clients.g.ts index 62e282d40..5b127bcf3 100644 --- a/src/test-targets/api-clients.g.ts +++ b/src/test-targets/api-clients.g.ts @@ -384,6 +384,21 @@ export class EnumPkApiClient extends ModelApiClient<$models.EnumPk> { } +export class OneToOneChild1ApiClient extends ModelApiClient<$models.OneToOneChild1> { + constructor() { super($metadata.OneToOneChild1) } +} + + +export class OneToOneChild2ApiClient extends ModelApiClient<$models.OneToOneChild2> { + constructor() { super($metadata.OneToOneChild2) } +} + + +export class OneToOneParentApiClient extends ModelApiClient<$models.OneToOneParent> { + constructor() { super($metadata.OneToOneParent) } +} + + export class PersonApiClient extends ModelApiClient<$models.Person> { constructor() { super($metadata.Person) } public rename(id: number | null, name?: string | null, $config?: AxiosRequestConfig): AxiosPromise> { diff --git a/src/test-targets/metadata.g.ts b/src/test-targets/metadata.g.ts index 513bf0773..1d588d04e 100644 --- a/src/test-targets/metadata.g.ts +++ b/src/test-targets/metadata.g.ts @@ -2391,6 +2391,114 @@ export const EnumPk = domain.types.EnumPk = { dataSources: { }, } +export const OneToOneChild1 = domain.types.OneToOneChild1 = { + name: "OneToOneChild1" as const, + displayName: "One To One Child1", + get displayProp() { return this.props.parentId }, + type: "model", + controllerRoute: "OneToOneChild1", + get keyProp() { return this.props.parentId }, + behaviorFlags: 7 as BehaviorFlags, + props: { + parentId: { + name: "parentId", + displayName: "Parent Id", + type: "number", + role: "primaryKey", + hidden: 3 as HiddenAreas, + }, + parent: { + name: "parent", + displayName: "Parent", + type: "model", + get typeDef() { return (domain.types.OneToOneParent as ModelType & { name: "OneToOneParent" }) }, + role: "referenceNavigation", + get foreignKey() { return (domain.types.OneToOneChild1 as ModelType & { name: "OneToOneChild1" }).props.parentId as PrimaryKeyProperty }, + get principalKey() { return (domain.types.OneToOneParent as ModelType & { name: "OneToOneParent" }).props.id as PrimaryKeyProperty }, + dontSerialize: true, + }, + }, + methods: { + }, + dataSources: { + }, +} +export const OneToOneChild2 = domain.types.OneToOneChild2 = { + name: "OneToOneChild2" as const, + displayName: "One To One Child2", + get displayProp() { return this.props.parentId }, + type: "model", + controllerRoute: "OneToOneChild2", + get keyProp() { return this.props.parentId }, + behaviorFlags: 7 as BehaviorFlags, + props: { + parentId: { + name: "parentId", + displayName: "Parent Id", + type: "number", + role: "primaryKey", + hidden: 3 as HiddenAreas, + }, + parent: { + name: "parent", + displayName: "Parent", + type: "model", + get typeDef() { return (domain.types.OneToOneParent as ModelType & { name: "OneToOneParent" }) }, + role: "referenceNavigation", + get foreignKey() { return (domain.types.OneToOneChild2 as ModelType & { name: "OneToOneChild2" }).props.parentId as PrimaryKeyProperty }, + get principalKey() { return (domain.types.OneToOneParent as ModelType & { name: "OneToOneParent" }).props.id as PrimaryKeyProperty }, + dontSerialize: true, + }, + }, + methods: { + }, + dataSources: { + }, +} +export const OneToOneParent = domain.types.OneToOneParent = { + name: "OneToOneParent" as const, + displayName: "One To One Parent", + get displayProp() { return this.props.id }, + type: "model", + controllerRoute: "OneToOneParent", + get keyProp() { return this.props.id }, + behaviorFlags: 7 as BehaviorFlags, + props: { + id: { + name: "id", + displayName: "Id", + type: "number", + role: "primaryKey", + hidden: 3 as HiddenAreas, + }, + child1: { + name: "child1", + displayName: "Child1", + type: "model", + get typeDef() { return (domain.types.OneToOneChild1 as ModelType & { name: "OneToOneChild1" }) }, + role: "referenceNavigation", + get foreignKey() { return (domain.types.OneToOneParent as ModelType & { name: "OneToOneParent" }).props.id as PrimaryKeyProperty }, + get principalKey() { return (domain.types.OneToOneChild1 as ModelType & { name: "OneToOneChild1" }).props.parentId as PrimaryKeyProperty }, + get inverseNavigation() { return (domain.types.OneToOneChild1 as ModelType & { name: "OneToOneChild1" }).props.parent as ModelCollectionNavigationProperty }, + dontSerialize: true, + }, + child2: { + name: "child2", + displayName: "Child2", + type: "model", + get typeDef() { return (domain.types.OneToOneChild2 as ModelType & { name: "OneToOneChild2" }) }, + role: "referenceNavigation", + get foreignKey() { return (domain.types.OneToOneParent as ModelType & { name: "OneToOneParent" }).props.id as PrimaryKeyProperty }, + get principalKey() { return (domain.types.OneToOneChild2 as ModelType & { name: "OneToOneChild2" }).props.parentId as PrimaryKeyProperty }, + get inverseNavigation() { return (domain.types.OneToOneChild2 as ModelType & { name: "OneToOneChild2" }).props.parent as ModelCollectionNavigationProperty }, + dontSerialize: true, + }, + }, + methods: { + }, + dataSources: { + }, +} export const Person = domain.types.Person = { name: "Person" as const, displayName: "Person", @@ -3985,6 +4093,12 @@ export const PersonCriteria = domain.types.PersonCriteria = { dateKind: "datetime", role: "value", }, + adminOnly: { + name: "adminOnly", + displayName: "Admin Only", + type: "string", + role: "value", + }, }, } export const PositionalRecord = domain.types.PositionalRecord = { @@ -4229,6 +4343,9 @@ interface AppDomain extends Domain { InitRecordWithDefaultCtor: typeof InitRecordWithDefaultCtor InputOutputOnlyExternalTypeWithRequiredNonscalarProp: typeof InputOutputOnlyExternalTypeWithRequiredNonscalarProp Location: typeof Location + OneToOneChild1: typeof OneToOneChild1 + OneToOneChild2: typeof OneToOneChild2 + OneToOneParent: typeof OneToOneParent OutputOnlyExternalTypeWithoutDefaultCtor: typeof OutputOnlyExternalTypeWithoutDefaultCtor OutputOnlyExternalTypeWithoutDefaultCtorWithInputMappableProperties: typeof OutputOnlyExternalTypeWithoutDefaultCtorWithInputMappableProperties OutputOnlyExternalTypeWithRequiredEntityProp: typeof OutputOnlyExternalTypeWithRequiredEntityProp diff --git a/src/test-targets/models.g.ts b/src/test-targets/models.g.ts index 559f98c97..87690c128 100644 --- a/src/test-targets/models.g.ts +++ b/src/test-targets/models.g.ts @@ -377,6 +377,82 @@ export class EnumPk { } +export interface OneToOneChild1 extends Model { + parentId: number | null + parent: OneToOneParent | null +} +export class OneToOneChild1 { + + /** Mutates the input object and its descendents into a valid OneToOneChild1 implementation. */ + static convert(data?: Partial): OneToOneChild1 { + return convertToModel(data || {}, metadata.OneToOneChild1) + } + + /** Maps the input object and its descendents to a new, valid OneToOneChild1 implementation. */ + static map(data?: Partial): OneToOneChild1 { + return mapToModel(data || {}, metadata.OneToOneChild1) + } + + static [Symbol.hasInstance](x: any) { return x?.$metadata === metadata.OneToOneChild1; } + + /** Instantiate a new OneToOneChild1, optionally basing it on the given data. */ + constructor(data?: Partial | {[k: string]: any}) { + Object.assign(this, OneToOneChild1.map(data || {})); + } +} + + +export interface OneToOneChild2 extends Model { + parentId: number | null + parent: OneToOneParent | null +} +export class OneToOneChild2 { + + /** Mutates the input object and its descendents into a valid OneToOneChild2 implementation. */ + static convert(data?: Partial): OneToOneChild2 { + return convertToModel(data || {}, metadata.OneToOneChild2) + } + + /** Maps the input object and its descendents to a new, valid OneToOneChild2 implementation. */ + static map(data?: Partial): OneToOneChild2 { + return mapToModel(data || {}, metadata.OneToOneChild2) + } + + static [Symbol.hasInstance](x: any) { return x?.$metadata === metadata.OneToOneChild2; } + + /** Instantiate a new OneToOneChild2, optionally basing it on the given data. */ + constructor(data?: Partial | {[k: string]: any}) { + Object.assign(this, OneToOneChild2.map(data || {})); + } +} + + +export interface OneToOneParent extends Model { + id: number | null + child1: OneToOneChild1 | null + child2: OneToOneChild2 | null +} +export class OneToOneParent { + + /** Mutates the input object and its descendents into a valid OneToOneParent implementation. */ + static convert(data?: Partial): OneToOneParent { + return convertToModel(data || {}, metadata.OneToOneParent) + } + + /** Maps the input object and its descendents to a new, valid OneToOneParent implementation. */ + static map(data?: Partial): OneToOneParent { + return mapToModel(data || {}, metadata.OneToOneParent) + } + + static [Symbol.hasInstance](x: any) { return x?.$metadata === metadata.OneToOneParent; } + + /** Instantiate a new OneToOneParent, optionally basing it on the given data. */ + constructor(data?: Partial | {[k: string]: any}) { + Object.assign(this, OneToOneParent.map(data || {})); + } +} + + export interface Person extends Model { /** ID for the person object. */ @@ -1047,6 +1123,7 @@ export interface PersonCriteria extends Model { subCriteria: PersonCriteria[] | null gender: Genders | null date: Date | null + adminOnly: string | null } export class PersonCriteria { @@ -1272,6 +1349,9 @@ declare module "coalesce-vue/lib/model" { InitRecordWithDefaultCtor: InitRecordWithDefaultCtor InputOutputOnlyExternalTypeWithRequiredNonscalarProp: InputOutputOnlyExternalTypeWithRequiredNonscalarProp Location: Location + OneToOneChild1: OneToOneChild1 + OneToOneChild2: OneToOneChild2 + OneToOneParent: OneToOneParent OutputOnlyExternalTypeWithoutDefaultCtor: OutputOnlyExternalTypeWithoutDefaultCtor OutputOnlyExternalTypeWithoutDefaultCtorWithInputMappableProperties: OutputOnlyExternalTypeWithoutDefaultCtorWithInputMappableProperties OutputOnlyExternalTypeWithRequiredEntityProp: OutputOnlyExternalTypeWithRequiredEntityProp diff --git a/src/test-targets/viewmodels.g.ts b/src/test-targets/viewmodels.g.ts index 05fe6d3a1..4722dfe83 100644 --- a/src/test-targets/viewmodels.g.ts +++ b/src/test-targets/viewmodels.g.ts @@ -713,6 +713,71 @@ export class EnumPkListViewModel extends ListViewModel<$models.EnumPk, $apiClien } +export interface OneToOneChild1ViewModel extends $models.OneToOneChild1 { + parentId: number | null; + get parent(): OneToOneParentViewModel | null; + set parent(value: OneToOneParentViewModel | $models.OneToOneParent | null); +} +export class OneToOneChild1ViewModel extends ViewModel<$models.OneToOneChild1, $apiClients.OneToOneChild1ApiClient, number> implements $models.OneToOneChild1 { + + constructor(initialData?: DeepPartial<$models.OneToOneChild1> | null) { + super($metadata.OneToOneChild1, new $apiClients.OneToOneChild1ApiClient(), initialData) + } +} +defineProps(OneToOneChild1ViewModel, $metadata.OneToOneChild1) + +export class OneToOneChild1ListViewModel extends ListViewModel<$models.OneToOneChild1, $apiClients.OneToOneChild1ApiClient, OneToOneChild1ViewModel> { + + constructor() { + super($metadata.OneToOneChild1, new $apiClients.OneToOneChild1ApiClient()) + } +} + + +export interface OneToOneChild2ViewModel extends $models.OneToOneChild2 { + parentId: number | null; + get parent(): OneToOneParentViewModel | null; + set parent(value: OneToOneParentViewModel | $models.OneToOneParent | null); +} +export class OneToOneChild2ViewModel extends ViewModel<$models.OneToOneChild2, $apiClients.OneToOneChild2ApiClient, number> implements $models.OneToOneChild2 { + + constructor(initialData?: DeepPartial<$models.OneToOneChild2> | null) { + super($metadata.OneToOneChild2, new $apiClients.OneToOneChild2ApiClient(), initialData) + } +} +defineProps(OneToOneChild2ViewModel, $metadata.OneToOneChild2) + +export class OneToOneChild2ListViewModel extends ListViewModel<$models.OneToOneChild2, $apiClients.OneToOneChild2ApiClient, OneToOneChild2ViewModel> { + + constructor() { + super($metadata.OneToOneChild2, new $apiClients.OneToOneChild2ApiClient()) + } +} + + +export interface OneToOneParentViewModel extends $models.OneToOneParent { + id: number | null; + get child1(): OneToOneChild1ViewModel | null; + set child1(value: OneToOneChild1ViewModel | $models.OneToOneChild1 | null); + get child2(): OneToOneChild2ViewModel | null; + set child2(value: OneToOneChild2ViewModel | $models.OneToOneChild2 | null); +} +export class OneToOneParentViewModel extends ViewModel<$models.OneToOneParent, $apiClients.OneToOneParentApiClient, number> implements $models.OneToOneParent { + + constructor(initialData?: DeepPartial<$models.OneToOneParent> | null) { + super($metadata.OneToOneParent, new $apiClients.OneToOneParentApiClient(), initialData) + } +} +defineProps(OneToOneParentViewModel, $metadata.OneToOneParent) + +export class OneToOneParentListViewModel extends ListViewModel<$models.OneToOneParent, $apiClients.OneToOneParentApiClient, OneToOneParentViewModel> { + + constructor() { + super($metadata.OneToOneParent, new $apiClients.OneToOneParentApiClient()) + } +} + + export interface PersonViewModel extends $models.Person { /** ID for the person object. */ @@ -1184,6 +1249,9 @@ const viewModelTypeLookup = ViewModel.typeLookup = { ComplexModel: ComplexModelViewModel, ComplexModelDependent: ComplexModelDependentViewModel, EnumPk: EnumPkViewModel, + OneToOneChild1: OneToOneChild1ViewModel, + OneToOneChild2: OneToOneChild2ViewModel, + OneToOneParent: OneToOneParentViewModel, Person: PersonViewModel, Product: ProductViewModel, ReadOnlyEntityUsedAsMethodInput: ReadOnlyEntityUsedAsMethodInputViewModel, @@ -1206,6 +1274,9 @@ const listViewModelTypeLookup = ListViewModel.typeLookup = { ComplexModel: ComplexModelListViewModel, ComplexModelDependent: ComplexModelDependentListViewModel, EnumPk: EnumPkListViewModel, + OneToOneChild1: OneToOneChild1ListViewModel, + OneToOneChild2: OneToOneChild2ListViewModel, + OneToOneParent: OneToOneParentListViewModel, Person: PersonListViewModel, Product: ProductListViewModel, ReadOnlyEntityUsedAsMethodInput: ReadOnlyEntityUsedAsMethodInputListViewModel,