diff --git a/Application/EdFi.Ods.Common/Models/Domain/Association.cs b/Application/EdFi.Ods.Common/Models/Domain/Association.cs
index 26a68d61c5..e5d1cbfb1e 100644
--- a/Application/EdFi.Ods.Common/Models/Domain/Association.cs
+++ b/Application/EdFi.Ods.Common/Models/Domain/Association.cs
@@ -80,7 +80,6 @@ internal Association(DomainModel domainModel, AssociationDefinition associationD
IsIdentifying = associationDefinition.IsIdentifying;
IsRequired = associationDefinition.IsRequired;
- IsRequiredCollection = associationDefinition.Cardinality == Cardinality.OneToOneOrMore;
ConstraintByDatabaseEngine = associationDefinition.ConstraintNames;
PotentiallyLogical = associationDefinition.PotentiallyLogical;
}
@@ -100,8 +99,6 @@ internal Association(DomainModel domainModel, AssociationDefinition associationD
public bool IsIdentifying { get; }
- public bool IsRequiredCollection { get; }
-
///
/// Indicates whether or not the association should be enforced (this allows a reference to be un-resolvable).
///
@@ -146,6 +143,9 @@ public bool IsSelfReferencing
=> PrimaryEntityFullName.Name.EqualsIgnoreCase(SecondaryEntityFullName.Name)
&& PrimaryEntityFullName.Schema.Equals(SecondaryEntityFullName.Schema);
+ ///
+ /// Indicates that the values of the association are required on the receiving entity.
+ ///
public bool IsRequired { get; }
///
diff --git a/Application/EdFi.Ods.Common/Models/Domain/AssociationView.cs b/Application/EdFi.Ods.Common/Models/Domain/AssociationView.cs
index c860d746ea..ac12b0e017 100644
--- a/Application/EdFi.Ods.Common/Models/Domain/AssociationView.cs
+++ b/Application/EdFi.Ods.Common/Models/Domain/AssociationView.cs
@@ -107,10 +107,10 @@ internal AssociationView(DomainModel domainModel, Association association, bool
.Where(av => av != null)
.Any(av =>
// An optional collection
- (av.AssociationType == AssociationViewType.ManyToOne && !av.Association.IsRequiredCollection)
+ (av.AssociationType == AssociationViewType.ManyToOne && !av.Inverse.IsRequiredCollection)
// An optional incoming one-to-one reference
- || (av.AssociationType == AssociationViewType.OneToOneIncoming && !av.IsRequired));
+ || (av.AssociationType == AssociationViewType.OneToOneIncoming && !av.IsRequiredIncomingAssociation));
if (isContainingEntityPresenceOptional)
{
@@ -121,7 +121,7 @@ internal AssociationView(DomainModel domainModel, Association association, bool
if (AssociationType == AssociationViewType.ManyToOne
|| AssociationType == AssociationViewType.OneToOneIncoming)
{
- return !IsRequired;
+ return !Association.IsRequired;
}
// All other associations do not represent dependencies
@@ -436,14 +436,33 @@ public string RoleName
///
public AssociationView Inverse { get; internal set; }
- public bool IsRequired
+ ///
+ /// Indicates that the properties of an incoming association are required on the receiving entity (for outgoing associations,
+ /// see and ).
+ ///
+ public bool IsRequiredIncomingAssociation
{
- get { return Association.IsRequired; }
+ get => Association.IsRequired && AssociationType.IsIncoming();
}
+ ///
+ /// Indicates that the association view represents an outgoing one-to-one relationship (single object) with an entity that must exist.
+ ///
+ public bool IsRequiredEmbeddedObject
+ {
+ get =>
+ AssociationType == AssociationViewType.OneToOneOutgoing
+ && Association.Cardinality == Cardinality.OneToOne;
+ }
+
+ ///
+ /// Indicates that the association view represents an outgoing one-to-many relationship (collection) that must contain at least one item.
+ ///
public bool IsRequiredCollection
{
- get { return Association.IsRequiredCollection; }
+ get =>
+ AssociationType == AssociationViewType.OneToMany
+ && Association.Cardinality == Cardinality.OneToOneOrMore;
}
FullName IHasNameContext.ParentFullName
diff --git a/Application/EdFi.Ods.Common/Models/Domain/AssociationViewType.cs b/Application/EdFi.Ods.Common/Models/Domain/AssociationViewType.cs
index baaa8822b7..8c6dfe890a 100644
--- a/Application/EdFi.Ods.Common/Models/Domain/AssociationViewType.cs
+++ b/Application/EdFi.Ods.Common/Models/Domain/AssociationViewType.cs
@@ -16,4 +16,23 @@ public enum AssociationViewType
ToExtension,
FromCore
}
+
+ public static class AssociationViewTypeExtensions
+ {
+ public static bool IsIncoming(this AssociationViewType associationViewType)
+ {
+ return (associationViewType == AssociationViewType.FromBase
+ || associationViewType == AssociationViewType.FromCore
+ || associationViewType == AssociationViewType.OneToOneIncoming
+ || associationViewType == AssociationViewType.ManyToOne);
+ }
+
+ public static bool IsOutgoing(this AssociationViewType associationViewType)
+ {
+ return (associationViewType == AssociationViewType.ToDerived
+ || associationViewType == AssociationViewType.ToExtension
+ || associationViewType == AssociationViewType.OneToMany
+ || associationViewType == AssociationViewType.OneToOneOutgoing);
+ }
+ }
}
diff --git a/Application/EdFi.Ods.Common/Models/Resource/Reference.cs b/Application/EdFi.Ods.Common/Models/Resource/Reference.cs
index 402d44a698..217c682fe5 100644
--- a/Application/EdFi.Ods.Common/Models/Resource/Reference.cs
+++ b/Application/EdFi.Ods.Common/Models/Resource/Reference.cs
@@ -82,7 +82,7 @@ public Resource ReferencedResource
public bool IsRequired
{
- get { return Association.IsRequired; }
+ get { return Association.IsRequiredIncomingAssociation; }
}
///
diff --git a/Application/EdFi.Ods.Features/OpenApiMetadata/Factories/OpenApiMetadataDefinitionsFactory.cs b/Application/EdFi.Ods.Features/OpenApiMetadata/Factories/OpenApiMetadataDefinitionsFactory.cs
index 7882a9ee4b..b4408da046 100644
--- a/Application/EdFi.Ods.Features/OpenApiMetadata/Factories/OpenApiMetadataDefinitionsFactory.cs
+++ b/Application/EdFi.Ods.Features/OpenApiMetadata/Factories/OpenApiMetadataDefinitionsFactory.cs
@@ -156,7 +156,7 @@ private Schema CreateResourceChildSchema(ResourceChildItem resourceChildItem, Op
resourceChildItem.EmbeddedObjects.Select(
e => new
{
- IsRequired = e.Association.IsRequired,
+ IsRequired = e.Association?.IsRequiredEmbeddedObject ?? false,
Key = e.JsonPropertyName,
Schema = CreateEmbeddedObjectSchema(e, openApiMetadataResource)
})).ToList();
@@ -324,6 +324,7 @@ private Schema CreateResourceSchema(OpenApiMetadataResource openApiMetadataResou
x => new PropertySchemaInfo
{
PropertyName = x.JsonPropertyName,
+ IsRequired = x.Association?.IsRequiredEmbeddedObject ?? false,
Sort = SortOrder(x.PropertyName, false),
Schema = CreateEmbeddedObjectSchema(x, openApiMetadataResource)
})).Concat(
diff --git a/Application/EdFi.Ods.Tests/EdFi.Ods.Common/Models/Domain/AssociationViewTests.cs b/Application/EdFi.Ods.Tests/EdFi.Ods.Common/Models/Domain/AssociationViewTests.cs
index 124438ac04..6bc238d8f5 100644
--- a/Application/EdFi.Ods.Tests/EdFi.Ods.Common/Models/Domain/AssociationViewTests.cs
+++ b/Application/EdFi.Ods.Tests/EdFi.Ods.Common/Models/Domain/AssociationViewTests.cs
@@ -2522,25 +2522,25 @@ protected override void Act()
.ToList();
_nonNavigableOptionalManyToOneAssociations = allAssociations.Where(a =>
- !a.IsNavigable && !a.IsSelfReferencing && !a.IsRequired
+ !a.IsNavigable && !a.IsSelfReferencing && !a.Association.IsRequired
&& a.AssociationType == AssociationViewType.ManyToOne)
.ToList();
_nonNavigableOptionalOneToOneIncomingAssociations = allAssociations.Where(a =>
- a.AssociationType == AssociationViewType.OneToOneIncoming && !a.IsNavigable && !a.IsRequired)
+ a.AssociationType == AssociationViewType.OneToOneIncoming && !a.IsNavigable && !a.Association.IsRequired)
.ToList();
_topLevelRequiredManyToOneAssociations = allAssociations.Where(
a => a.AssociationType == AssociationViewType.ManyToOne
&& !a.IsNavigable
- && a.IsRequired
+ && a.Association.IsRequired
&& a.ThisEntity.IsAggregateRoot)
.ToList();
var childRequiredManyToOneAssociations = allAssociations.Where(
a => a.AssociationType == AssociationViewType.ManyToOne
&& !a.IsNavigable
- && a.IsRequired
+ && a.Association.IsRequired
&& !a.ThisEntity.IsAggregateRoot)
.ToList();
@@ -2564,37 +2564,37 @@ protected override void Act()
_requiredEmbeddedObjectsWithRequiredAssociations = childRequiredManyToOneAssociations
.Where(a => a.ThisEntity.ParentAssociation.AssociationType == AssociationViewType.OneToOneIncoming)
.Where(a =>
- a.ThisEntity.ParentAssociation.Inverse.IsRequired
+ a.ThisEntity.ParentAssociation.Inverse.Association.IsRequired
// Check one level up for required child items (if it's in the there)
&& (a.ThisEntity.Parent.Parent == null
|| (a.ThisEntity.Parent.ParentAssociation.AssociationType == AssociationViewType.ManyToOne
&& a.ThisEntity.Parent.ParentAssociation.Inverse.IsRequiredCollection)
|| (a.ThisEntity.Parent.ParentAssociation.AssociationType == AssociationViewType.OneToOneIncoming
- && a.ThisEntity.Parent.ParentAssociation.Inverse.IsRequired))
+ && a.ThisEntity.Parent.ParentAssociation.Inverse.Association.IsRequired))
// Check two levels up for required child items (if it's in the there)
&& (a.ThisEntity.Parent.Parent?.Parent == null
|| (a.ThisEntity.Parent.Parent.ParentAssociation.AssociationType == AssociationViewType.ManyToOne
&& a.ThisEntity.Parent.Parent.ParentAssociation.Inverse.IsRequiredCollection)
|| (a.ThisEntity.Parent.Parent.ParentAssociation.AssociationType == AssociationViewType.OneToOneIncoming
- && a.ThisEntity.Parent.Parent.ParentAssociation.Inverse.IsRequired)))
+ && a.ThisEntity.Parent.Parent.ParentAssociation.Inverse.Association.IsRequired)))
.ToList();
_optionalEmbeddedObjectsWithRequiredAssociations = childRequiredManyToOneAssociations
.Where(a => a.ThisEntity.ParentAssociation.AssociationType == AssociationViewType.OneToOneIncoming)
.Where(a =>
- !a.ThisEntity.ParentAssociation.Inverse.IsRequired
+ !a.ThisEntity.ParentAssociation.Inverse.Association.IsRequired
// Check one level up for optional child items (if it's in the there)
|| (a.ThisEntity.Parent.Parent != null
&& ((a.ThisEntity.Parent.ParentAssociation.AssociationType == AssociationViewType.ManyToOne && !a.ThisEntity.Parent.ParentAssociation.Inverse.IsRequiredCollection)
- || (a.ThisEntity.Parent.ParentAssociation.AssociationType == AssociationViewType.OneToOneIncoming && !a.ThisEntity.Parent.ParentAssociation.Inverse.IsRequired)))
+ || (a.ThisEntity.Parent.ParentAssociation.AssociationType == AssociationViewType.OneToOneIncoming && !a.ThisEntity.Parent.ParentAssociation.Inverse.Association.IsRequired)))
// Check two levels up for optional child items (if it's in the there)
|| (a.ThisEntity.Parent.Parent?.Parent != null
&& ((a.ThisEntity.Parent.Parent.ParentAssociation.AssociationType == AssociationViewType.ManyToOne && !a.ThisEntity.Parent.Parent.ParentAssociation.Inverse.IsRequiredCollection)
- || (a.ThisEntity.Parent.Parent.ParentAssociation.AssociationType == AssociationViewType.OneToOneIncoming && !a.ThisEntity.Parent.Parent.ParentAssociation.Inverse.IsRequired))))
+ || (a.ThisEntity.Parent.Parent.ParentAssociation.AssociationType == AssociationViewType.OneToOneIncoming && !a.ThisEntity.Parent.Parent.ParentAssociation.Inverse.Association.IsRequired))))
.ToList();
_untestedAssociations = allAssociations
@@ -2884,7 +2884,7 @@ public void Should_indicate_that_a_required_reference_on_a_required_embedded_obj
{
var association = _resourceModel.GetAllResources()
.SelectMany(res => res.EmbeddedObjects)
- .Where(eo => eo.Association.IsRequired)
+ .Where(eo => eo.Association.Association.IsRequired)
.SelectMany(eo => eo.ObjectType.References.Where(r => r.IsRequired))
.Select(r => r.Association)
.FirstOrDefault();
diff --git a/Utilities/CodeGeneration/EdFi.Ods.CodeGen/Generators/DatabaseMetadataProvider.cs b/Utilities/CodeGeneration/EdFi.Ods.CodeGen/Generators/DatabaseMetadataProvider.cs
index 3bc2f927ba..ab0fb6c80f 100644
--- a/Utilities/CodeGeneration/EdFi.Ods.CodeGen/Generators/DatabaseMetadataProvider.cs
+++ b/Utilities/CodeGeneration/EdFi.Ods.CodeGen/Generators/DatabaseMetadataProvider.cs
@@ -29,6 +29,7 @@ protected override object Build()
bool AssociationIsNotOneToOne(Association a)
=> a.Cardinality != Cardinality.OneToOne
+ && a.Cardinality != Cardinality.OneToZeroOrOne
&& a.Cardinality != Cardinality.OneToOneInheritance
&& a.Cardinality != Cardinality.OneToOneExtension;
@@ -80,7 +81,7 @@ DatabaseMetadata DatabaseMetadataForAssociation(Association a)
return new
{
NamespaceName = EdFiConventions.BuildNamespace(BaseNamespaceName, TemplateContext.SchemaProperCaseName),
- IndexMetaData = databaseMetadata.OrderBy(x => x.TableName).ThenBy(x => x.Name)
+ IndexMetadata = databaseMetadata.OrderBy(x => x.TableName).ThenBy(x => x.Name)
};
}
}
diff --git a/Utilities/CodeGeneration/EdFi.Ods.CodeGen/Generators/Entities.cs b/Utilities/CodeGeneration/EdFi.Ods.CodeGen/Generators/Entities.cs
index 72eabfb40a..112c748abe 100644
--- a/Utilities/CodeGeneration/EdFi.Ods.CodeGen/Generators/Entities.cs
+++ b/Utilities/CodeGeneration/EdFi.Ods.CodeGen/Generators/Entities.cs
@@ -357,6 +357,7 @@ private IEnumerable