1

I try to set a unique constraint on multiple columns of which one also is a foreign key. I have tried a few things and are out of ideas by now.

To get a better understanding of what the context is and what I am trying to do, please consider the follwing example:

Let's say we have two classes:

    [Table(nameof(DeviceModel), Schema="DevModel")]
    public class DeviceModel
    {
        [Key]
        public int Id {get;set;}

        [Required]
        public string Name {get; set;}
        
        [Required]
        public string Vendor { get; set; }

        public string Description {get; set;}
    }

    [Table(nameof(Device), Schema="DevModel")]
    public class Device
    {
        [Key]
        public int Id {get; set;}

        [Required]
        public DeviceModel Model {get;set;}

        [Required]
        public int SerialNumber {get;set;}

        [Required]
        public int InventoryNumber {get;set;}
    }

These two classes are mapped to a table Device and a table DeviceModel. Each of the tables has a primary key Id. Device references DeviceModel via a foreign key column ModelId.

As each device of a certain type should obtain a unique inventory number as well as has to have a unique serial number per DeviceModel I would like to define a uniqueness constraint on Device.InventoryNumber as well as on Device.{Model, SerialNumber} (pseudo syntax).

For achieving this in the corresponding DbContext instance I override the OnModelCreating method as shown below:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Device>()
            .HasIndex(i => new { i.Model, i.SerialNumber }).IsUnique();
        modelBuilder.Entity<Device>()
            .HasIndex(i => i.InventoryNumber).IsUnique();
    }

While setting up the unique constraint for InventoryNumber works just fine, setting up the unique constraint for the column pair {Model, Serialnumber} fails.

The corresponding exception text is:

The property 'Device.Model' is of type 'DeviceModel' which is not supported by the current database provider. Either change the property CLR type, or ignore the property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.

As the constraint actually should apply to the {ModelId, SerialNumber} columns, I also tried to set up the unique constraint like this:

modelBuilder.Entity<Device>().HasIndex(i => new { i.Model.Id, i.SerialNumber }).IsUnique();

This results in error message:

The expression 'i => new <>f__AnonymousType0`2(Id = i.Model.Id, SerialNumber = i.SerialNumber)' is not a valid member access expression. The expression should represent a simple property or field access: 't => t.MyProperty'. When specifying multiple properties or fields, use an anonymous type: 't => new { t.MyProperty, t.MyField }'. (Parameter 'memberAccessExpression')

Also I thought about defining a custom conversion from Model to int but this will most likely result in figuring out, how to convert a primary key to an DeviceModel instance which is kind of reimplementing what entity framework should do for me.

So far I am out of ideas how to accomplish a unique constraint on multiple columns that contain at least one foreign key column.

Any ideas on how I could get this accomplished?

BDelker
  • 21
  • 3
  • Not nice, but the only way in case you have no explicit FK property - resort to magic strings `.HasIndex("ModelId", "SerialNumber").IsUnique()` – Ivan Stoev Jul 18 '22 at 10:35
  • 1
    Yes, this works. And for the serial number I at least can use `nameof(Device.SerialNumber)`. Edit: Thanks. – BDelker Jul 18 '22 at 10:41

0 Answers0