A generic interface with a primary key generic type parameter used with two different primary key types causes a domain build failure
When the following two entities are declared:
[HierarchyRoot(InheritanceSchema.SingleTable)]
[TableMapping("a_list")]
public sealed class AList : LegacyUserTrackedEntity, ICodebookEntity<short>
{
[Key(0)]
[Field]
[FieldMapping("id_list")]
public short Id { get; set; }
}
[HierarchyRoot(InheritanceSchema.SingleTable)]
[TableMapping("c_system")]
public sealed class CSystem : LegacyUserTrackedEntity, ICodebookEntity<int>
{
[Key]
[Field]
[FieldMapping("id")]
public int Id { get; set; }
}
DataObjects throws this error: Implementors of ICodebookEntity interface belong to hierarchies with different key structure: AList & CSystem.
However, the interfaces are ICodebookEntity<short> and ICodebookEntity<int> — two different things.
Using version 7.0.3.
Hello @ondrejtucny,
I think you have misunderstood the error. The problem is not with generic interface itself. DO counts implementors of ICodeBookEntity
Following model I have created works just fine:
public interface ISomeEntity<TEntityKey> : IEntity
{
[Field]
TEntityKey Id { get; }
[Field]
string Name { get; set; }
[Field]
DateTime CreationDate { get; set; }
}
[HierarchyRoot(InheritanceSchema.SingleTable)]
public sealed class IntImplementor : Entity, ISomeEntity<int>
{
[Field, Key(0)]
public int Id { get; private set; }
[Field]
public string Name { get; set; }
[Field]
public DateTime CreationDate { get; set; }
}
[HierarchyRoot(InheritanceSchema.SingleTable)]
public sealed class LongImplementor : Entity, ISomeEntity<long>
{
[Field, Key]
public long Id { get; private set; }
[Field]
public string Name { get; set; }
[Field]
public DateTime CreationDate { get; set; }
}
[HierarchyRoot(InheritanceSchema.SingleTable)]
public sealed class ShortImplementor : Entity, ISomeEntity<short>
{
[Field, Key(0)]
public short Id { get; private set; }
[Field]
public string Name { get; set; }
[Field]
public DateTime CreationDate { get; set; }
}
I believe that you have various key fields number within each interface.
You can take a look at https://github.com/DataObjects-NET/dataobjects-net/blob/f598e2aa7d38836d60555f28940222685fb77894/Orm/Xtensive.Orm/Orm/Building/ModelInspector.cs#L153-L175
you can see calling for validation if count is more that 1
and the validation itself https://github.com/DataObjects-NET/dataobjects-net/blob/f598e2aa7d38836d60555f28940222685fb77894/Orm/Xtensive.Orm/Orm/Building/Validator.cs#L271-L297
Hello @ondrejtucny,
Any updates on this? Did my answer help to lid a problem with model? Should I dig dipper for this? In this case I need your assistance to reproduce the issue.
Update on when exactly this problem occurs: If the generic interface inherits from a normal interface which in turn inherits from IEntity, then this error occurs. If the generic interface directly inherits from IEntity, then it works.
The following declaration causes the error:
public interface ICodebookEntity : IEntity
{
}
public interface ICodebookEntity<out TKey> : ICodebookEntity
where TKey : struct
{
[Field]
TKey Id { get; }
[Field]
string Code { get; }
[Field]
DateTime DateLastModified { get; }
}
However, when declared as follows, the hierarchy works just fine:
public interface ICodebookEntity
{
}
public interface ICodebookEntity<out TKey> : IEntity, ICodebookEntity
where TKey : struct
{
[Field]
TKey Id { get; }
[Field]
string Code { get; }
[Field]
DateTime DateLastModified { get; }
}
@ondrejtucny,
Thank you for the update, I will re-check and give you my update a bit later.
Hello @ondrejtucny ,
I've created two models according to your latest examples, they look like so
namespace Xtensive.Orm.Tests.Issues.IssueGithub0332_GenericIntefaceCausesErrorOnBuildModel
{
namespace GoodExample
{
public interface ICodebookEntity
{
}
public interface ICodebookEntity<out TKey> : IEntity, ICodebookEntity
where TKey : struct
{
[Field]
TKey Id { get; }
[Field]
string Code { get; }
[Field]
DateTime DateLastModified { get; }
}
public class IntCodeBookEntity : Entity, ICodebookEntity<int>
{
[Field, Key]
public int Id { get; private set; }
[Field]
public string Code { get; set; }
[Field]
public DateTime DateLastModified { get; set; }
}
public class LongCodeBookEntity : Entity, ICodebookEntity<long>
{
[Field, Key]
public long Id { get; private set; }
[Field]
public string Code { get; set; }
[Field]
public DateTime DateLastModified { get; set; }
}
}
namespace BadExample
{
public interface ICodebookEntity : IEntity
{
}
public interface ICodebookEntity<out TKey> : ICodebookEntity
where TKey : struct
{
[Field]
TKey Id { get; }
[Field]
string Code { get; }
[Field]
DateTime DateLastModified { get; }
}
public class IntCodeBookEntity : Entity, ICodebookEntity<int>
{
[Field, Key]
public int Id { get; private set; }
[Field]
public string Code { get; set; }
[Field]
public DateTime DateLastModified { get; set; }
}
public class LongCodeBookEntity : Entity, ICodebookEntity<long>
{
[Field, Key]
public long Id { get; private set; }
[Field]
public string Code { get; set; }
[Field]
public DateTime DateLastModified { get; set; }
}
}
}
And I've created two tests that build domain with these types in model like so
public class IssueGithub0332_GenericIntefaceCausesErrorOnBuild
{
[Test]
public void WorkingCase()
{
//the factory just creates configuration with connection to test storage environment
var configuration = DomainConfigurationFactory.Create();
configuration.UpgradeMode = DomainUpgradeMode.Recreate;
configuration.Types.Register(typeof(Models.GoodExample.IntCodeBookEntity));
configuration.Types.Register(typeof(Models.GoodExample.LongCodeBookEntity));
configuration.Types.Register(typeof(Models.GoodExample.ICodebookEntity));
configuration.Types.Register(typeof(Models.GoodExample.ICodebookEntity<>));
using (var domain = Domain.Build(configuration)) { }
}
[Test]
public void NotWorkingCase()
{
//the factory just creates configuration with connection to test storage environment
var configuration = DomainConfigurationFactory.Create();
configuration.UpgradeMode = DomainUpgradeMode.Recreate;
configuration.Types.Register(typeof(Models.BadExample.IntCodeBookEntity));
configuration.Types.Register(typeof(Models.BadExample.LongCodeBookEntity));
configuration.Types.Register(typeof(Models.BadExample.ICodebookEntity));
configuration.Types.Register(typeof(Models.BadExample.ICodebookEntity<>));
using (var domain = Domain.Build(configuration)) { }
}
}
Both tests have completed successfully.
I also tried not to declare [Field] in classes and tried to move one of non-key fields to non-generic ICodebookEntity interface. Works just fine.
The tests were run on the latest for 7.0 branch version - 7.0.6 version.
Can you guide me to what I should change to have such error as you're getting? Also, if you can, please, verify that your case still doesn't work on v7.0.6.