Is it possible to have a relation where the foreign key is also the primary key? - sql-server

I have a table in a SQL database which should have a relation with one of two tables, but not both.
My manager solved it by using the ID field of the table Competitor as both primary key and as foreign key for the two other tables. None of the tables have autoincremental IDs.
Problem is, when I try to add a new compeitor, it fails unless there is an entry in both of the other tables with the specified ID, which is the opposite of what I want.
Here is an illustration to show you how it's done:
I am sorry if this has been posted or answered before. I could not find anything while searching.
Best Regards
Kajac

The only way to get this right is by subtyping the two Equestrian classes:
public class EquestrianBase
{
public int Id { get; set; }
public virtual Competitor Competitor { get; set; }
}
public class Equestrian : EquestrianBase
{
// Other properties
}
public class TeamEquestrian : EquestrianBase
{
public int TeamLeaderPersonId { get; set; }
// Other properties
}
This could be the competitor class:
public class Competitor
{
public int Id { get; set; }
public virtual EquestrianBase EquestrianBase { get; set; }
// Other properties
}
And the essential configuration:
modelBuilder.Entity<EquestrianBase>()
.HasRequired(e => e.Competitor)
.WithOptional();
Using this model, you'll see that EF adds a Discriminator column to the Equestrian table (of which there is only one now) which discriminates between the two types. Now the database schema enforces that a Competitor have only one Equestrian of either type.
If you want to further fine-tune the inheritance schema, which is called table per hierarchy, look here.

Sure just set the key as Foreign and Primary on the "dependant properties". The Competitor should have the primary key.
public class Competitor
{
[Key]
public int Id { get; set; }
}
public class Equestrain
{
[Key]
[ForeignKey("Competitor")]
public int Id{ get; set; }
public Competitor Competitor { get; set; }
}
public class TeamEquestrain
{
[Key]
[ForeignKey("Competitor")]
public int Id{ get; set; }
public Competitor Competitor { get; set; }
}
MSDN - Configuring a Required to Optional Relationship (One to Zero or One)

Related

Why doesn't this SQL table/relationship work for [0..1] to [0..1] relationships?

I'd like to do something similar to
EF Code First 0..1 to 0..1 relationship
but in EF Core rather than EF 5.
Essentially I want the following relationships, given these models, without using a junction table
class foo {
public int FooId { get; set; }
public string FooName { get; set; }
public int Foo_BarId { get; set; }
}
class bar {
public int BarId { get; set; }
public string BarName { get; set; }
public int Bar_FooId { get; set; }
}
Why is it not possible to do something like the following?
Note, FooId and BarId are Primary Keys [PK] and I'd like to set a FK relationship from Foo_BarId to BarId and similarly and FK relationship from Bar_FooId to FooId. Essentially, Foo_BarId and Foo_BarId can be null while FooId and BarId is required.
The resulting table for foo and bar would look like (i used a picture of html because its too hard to post html rendered stuff here)
Why is this not allowed? Or if it is, what are the magic commands for builder.HasOne.WithOne?
You specify a Bar_FooId value of 22 in two records in the Bar table. This is 1-to-many, not 1-to-1, from Foo to Bar. The 1 to [0..1] constraints will be violated.
A 1 to [0..1] relationship can use an unique index filtered for NOT NULL on the right. The filter allows more than one record with NULL for the "0" part of the relationship. Except for these NULLs, the values on the right are unique.
The index on the right side of a 1-to-many relationship can't be unique.
The other relationship, so far, is not 1-to-many. But it's too soon to tell.
EDIT:
Okay, I had it backwards. These relationships need to be in place and the filtered indexes are desired because you don't want the 1-to-many. Here is a total WAG as to what I'd do. I expect it to be wrong because I'm not a EF person.
class foo {
public int FooId { get; set; }
public string FooName { get; set; }
[ForeignKey("BarId")]
public int Foo_BarId { get; set; }
}
class bar {
public int BarId { get; set; }
public string BarName { get; set; }
[ForeignKey("FooId")]
public int Bar_FooId { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Bar>()
.HasKey(z => z.BarID)
.HasOne(a => a.Foo)
.WithOne(b => b.Bar)
.HasForeignKey<Bar>(b => b.Bar_FooId);
modelBuilder.Entity<Foo>()
.HasKey(z => z.FooID)
.HasOne(a => a.Bar)
.WithOne(b => b.Foo)
.HasForeignKey<Foo>(b => b.Foo_BarId);
}
Check the database to insure you have the filtered unique indexes required. They are supported if adding manually according to this link. The filter on NULL is included automatically if .IsUnique() is specified. (You have to remove it if you don't want it filtered.)
https://learn.microsoft.com/en-us/ef/core/modeling/indexes
I'm old fashioned. This is easier to see in T-SQL for me. Feel free to mark me down for the blunder(s).

Auto assign column value when row is created

I am using EF6 and SQL Server 2014.
I have something like the following simplified model:
public class Order
{
public int Id { get; set; }
public DateTime Created { get; set; }
public IEnumerable<OrderItem> OrderItems { get; set; }
}
public class OrderItem
{
public int Id { get; set; }
public int OrderId { get; set; }
public Order Order { get; set; }
public string ItemName { get; set; }
public int OrderItemNumber { get; set; }
}
The Id properties are standard auto-increment primary keys.
The OrderItem.OrderItemNumber property uniquely identifies an OrderItem within the context of a given Order.
When a new OrderItem row is inserted it needs to be assigned the next OrderItemNumber for the parent Order, something like
SELECT COALESCE(MAX(OrderItemNumber),0) FROM dbo.OrderItems WHERE OrderId = 2
Is there a means using EF6/SQL Server to auto-assign the OrderItemNumber at the point a row is inserted?
Although this proved an interesting learning experience on EF support for triggers (see https://github.com/NickStrupat/EntityFramework.Triggers) I eventually decided to re-model.
In this case my question was a result of incorrect modelling. The OrderItemNumber property was redundant so I removed it; the sequence the OrderItems were created can be inferred from the Id value, and there was no real requirement to allow re-sequencing.

Entity Framework Code First - One to Many relations from multiple tables

I have a BlobEntity table that contains paths to files for many other tables (tableX, tableY, tableZ, etc...) in my application.
The relation between all the other tables to BlobEntity table is one to many.
Example:
tableX -> BlobTable (OTM)
tableY -> BlobTable (OTM)
tableZ -> BlobTable (OTM)
and the relation is:
public virtual ICollection<BlobEntity> BlobEntity { get; set; }
I'm not sure if this is an issue, but entity framework Code First creates a new FK column in BlobEntity table for each source table.
In my case, BlobEntity contains three FK columns for tableX, tableY and tableZ.
In order to be efficiency, i rather create one column in BlobEntity that contains the FK for the source tables.
Is it reasonable?
Please advise...
Thanks.
No, you can't do this even in plain old SQL.
You can have a foreing key pointing to more than one table; that's why you need
three columns.
If you want to do a "trick" like this, you have to manually manage the relation (I mean, no real FK), but you can't map it into EF.
What about this?
public class EntityA
{
public int Id { get; set; }
public int MyFileID {get;set;}
public virtual MyFiles MyFile { get; set; }
}
public class EntityB
{
public int Id { get; set; }
public int MyFileID {get;set;}
public virtual MyFiles MyFile { get; set; }
}
public class MyFiles
{
public MyFiles()
{
// ReSharper disable once VirtualMemberCallInContructor
FilesForEntityA = new List<EntityA>();
// ReSharper disable once VirtualMemberCallInContructor
FilesForEntityB = new List<EntityB>();
}
public int Id { get; set; }
public int? EntityAId {get;set;}
public int? EntityBId {get;set;}
public virtual ICollection<EntityA> FilesForEntityA { get; set; }
public virtual ICollection<EntityB> FilesForEntityB { get; set; }
}
This way you can have the FK in place and you can easily manager multiple entities.
Obviously if you have many files for each entity, you can go with a N-to-N relationship, like this.

Entity Framework 6 composite key (DateTime + foreign key)

I have to store the following entities in a database:
devices
measuring points
measured values
where each device has one or more measuring points. The measured values table will store values for each measuring point every 10 minutes. This table will have millions of records after a few years and must be efficiently searchable by measuring point and timestamp. How would you design this with EF6 Code-First?
My first approach was:
public class Device
{
public int ID { get; set; }
public int DeviceNumber { get; set; }
...
public virtual ICollection<MeasuringPoint> MeasuringPoints { get; set; }
}
public class MeasuringPoint
{
public int ID { get; set; }
public int MeasuringPointNumber { get; set; }
...
// Foreign key
public int DeviceID { get; set; }
public virtual Device Device { get; set; }
}
public class MeasuredValue
{
//public int Id { get; set; } ????
public DateTime TimeStamp { get; set; }
// Foreign key
public int MeasuringPointID { get; set; }
public double Value;
public virtual MeasuringPoint MeasuringPoint { get; set; }
}
All values in the measured values table must be unique in the combination of TimeStamp and MeasuringPointID. What primary key should I define for MeasuredTable?
this is not uniquely code_first/EF related.
With EF you may choose a PK that will lead to the creation of a cluster index. Then you may/must create specific index(es) for the querie(s)
Imho, you may :
use an identity pk (faster insert)
create a (covering) index (point, date)
create a (covering) index (date, point)
use a pk (point, counter), where counter is identity or something like (max for point) + 1, or even the date (but what if 2 measures in the same millisecond ?).
create an index (date, point)
According to your transactional and concurrency pressure the maxsolution may be quite hard to implement.
In this last case you'll have a configuration like:
public class MeasuredValueConfiguration : EntityTypeConfiguration<MeasuredValue>{
public MeasuredValueConfiguration()
: base() {
/* ... */
HasKey(e => new {e.MeasuringPointID, e.Id});
/* ... */
}
}

Model Entity Framework many-many plus shared relation

In Entity Framework (model-first), I'm implementing a many-many (e.g. Composition-Anthology) relationship, with an additional relation (Composer) that must match within related sets.
How can I correctly create this model in EF?
I currently have two bad ideas:
Bad Idea #1
By making the primary keys on Composition and Anthology composites, containing both ComposerId and the local Identity, EF constrains this correctly. However this causes immediate problems:
All tables related to Composition and Anthology now also have ComposerId for FKs; painful for DBA.
I can't use EF 5.0's EntitySet.Find() based simply on the unique Identity, etc.
Bad Idea #2
I could materialize the CompositionAnthology pivot table in the designer, adding ComposerId to it, and add a constraint directly to SQL. However this:
Breaks EF database creation/updating
Breaks entity navigation/addition
Note: My data actually models a much less intuitive "engagement" model, but this metaphor holds up quite well.
EDIT: I'm posting a portion of my actual model here by request, on the chance that my goals can be met with a different schematic representation. (I removed the HashSet assignments for brevity.) Logically, Composition represents Engagement in this model, because there must be a related Engagement (with matching Account) for an Endorsement to exist.
public partial class Account
{
public int Id { get; set; }
public string PrimaryEmail { get; set; }
public virtual ICollection<Endorsement> EndorsementsGiven { get; set; }
public virtual ICollection<Endorsement> EndorsementsReceived { get; set; }
public virtual ICollection<Engagement> Engagements { get; set; }
public virtual ICollection<EngagementEndorsement> EngagementEndorsements { get; set; }
}
public partial class EngagementEndorsement
{
public int Endorsement_Id { get; set; }
public int Engagement_Id { get; set; }
public int Account_Id { get; set; }
public virtual Endorsement Endorsement { get; set; }
public virtual Engagement Engagement { get; set; }
public virtual Account Account { get; set; }
}
public partial class Engagement
{
public int Id { get; set; }
public System.DateTime Start { get; set; }
public System.DateTime End { get; set; }
public string JobFunction { get; set; }
public virtual Account Account { get; set; }
public virtual ICollection<EngagementEndorsement> EngagementEndorsements { get; set; }
}
public partial class Endorsement
{
public int Id { get; set; }
public EndorsementStatus Status { get; set; }
public EndorserRole EndorserRole { get; set; }
public string Note { get; set; }
public virtual Account Endorsee { get; set; }
public virtual Account Endorser { get; set; }
public virtual ICollection<EngagementEndorsement> EngagementEndorsements { get; set; }
}
I'm currently doing "Bad Idea #2" (see above) - After the database is created, I apply the additional relations/constraints:
-- --------------------------------------------------
-- Ensure Engagement-to-Endorsement AccountId match
-- --------------------------------------------------
ALTER TABLE [dbo].[Engagements]
ADD CONSTRAINT [UK_EngagementIdAccountId]
UNIQUE NONCLUSTERED
([Id], [Account_Id])
WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
ALTER TABLE [dbo].[EngagementEndorsements]
ADD CONSTRAINT [FK_EngagementIdAccountId]
FOREIGN KEY ([Engagement_Id], [Account_Id])
REFERENCES [dbo].[Engagements]
([Id], [Account_Id])
ON DELETE NO ACTION ON UPDATE NO ACTION;
ALTER TABLE [dbo].[Endorsements]
ADD CONSTRAINT [UK_EndorsementIdAccountId]
UNIQUE NONCLUSTERED
([Id], [Endorsee_Id])
WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
ALTER TABLE [dbo].[EngagementEndorsements]
ADD CONSTRAINT [FK_EndorsementIdAccountId]
FOREIGN KEY ([Endorsement_Id], [Account_Id])
REFERENCES [dbo].[Endorsements]
([Id], [Endorsee_Id])
ON DELETE NO ACTION ON UPDATE NO ACTION;
GO
Ultimately, based on good data schema feedback (and lack of EF feedback) in two related questions, I proceeded very much as shown in "Bad Idea #2", above.
I've been working with this and it meets all my current needs.
See these for further details on the other bits of the implementation:
SQL constraint: https://dba.stackexchange.com/questions/41387/manymany-with-shared-relation
EF5 navigation: Custom Entity Framework many-to-many navigation property

Resources