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.
Related
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).
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.
I'm using Entity Framework 6 Code First.
Right now my model looks the following:
public class Region
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<City> Cities { get; set; }
}
public class City
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public virtual Region Region { get; set; }
}
The thing is, I got a list of cities.
I need to lookup the region of each city (I have a local list of regions as well).
Now, I suppose I could do the following:
foreach (var c in cities)
{
if (regions.Any(x => x.Id == c.Region.Id))
}
Here I will have to look up a region from the database for each city (lazy loading).
However, I only need the Id of the region, therefore it seems wasteful to me to look up the region row at every loop.
If I changed my City model to be the following:
public class City
{
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey("Region")]
public int RegionId { get; set; }
[Required]
public virtual Region Region { get; set; }
}
I could do the following instead:
foreach (var c in cities)
{
if (regions.Any(x => x.Id == c.RegionId)) //no region lookup at Im using the foreign id key
}
Is this correct? I mean it will save me a query for every city right?
If so, is there any reason NOT to include the foreign id keys in the model when doing Code First at all?
Is this correct? I mean it will save me a query for every city right?
It will save a join to the Regions table. You have to check it out with the SQL Profiler! For example:
List of cities with Id City1, City2 (Region not loaded City1= has RegionId 5)
Now you are looking for any City has a Region with Id 5.
// EF does not have to join the tables because you have the RegionId
if (myDbContext.Cities.Any(c => c.RegionId == 5))
{
}
If so, is there any reason NOT to include the foreign id keys in the
model when doing Code First at all?
No for me this it is a good practice! Just keep it consistent and do it for all relationships of the type 1..n/1..0or1.
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)
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});
/* ... */
}
}