Is it posible to define a compound foreign primary key without defining scalar properties?
public class A
{
public virtual int AID { get; set; }
}
public class B
{
public virtual int BID { get; set; }
}
public class CompoundKeyClass
{
public virtual A AObject { get; set; }
public virtual B BObject { get; set; }
}
How can I make properties AObject and BObject the compound PK of CompoundKeyClass without defining the scalar ID properties of for A and B types using the FluentAPI?
You can't. Both AID and BID must be in your CompoundKeyClass mapped as complex primary key and each of them must be mapped as foreign key to related table.
I suspect you can't; the closest I can find to documentation on this is this:
KeyAttribute
KeyAttribute is used to specify that a property/column is part of the primary key of the entity and applies to scalar properties only.
You should, however, be able to name the foreign key properties "_AID" and "_BID", and/or mark them as [Browsable(false)]. This should ensure you cannot use or even see them accidentally.
Related
In EF6 we have two ways to declare relationship between two tables:
Annotations Attributes
Fluent API
Today (by accident) I removed one relationship between two tables and everything kept working well. I was very surprised because there was no way EF would know how those two tables are connected.
Tables look like that:
[Table("item")]
public class Item
{
public Item()
{
ItemNotes = new HashSet<ItemNote>();
}
[Key]
[Column("itemID", TypeName = "int")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int itemID { get; set; }
public ICollection<ItemNote> ItemNotes { get; set; }
}
[Table("itemNotes")]
public class ItemNote
{
[Key]
[Column("id", TypeName = "int")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Column("itemID", TypeName = "int")]
public int ItemId { get; set; }
[Column("note", TypeName = "varchar")]
[MaxLength(255)]
public string Note { get; set; }
}
Fluent API:
public class MyContext : DbContext
{
public MyContext()
: base("name=MyContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer<MyContext>(null);
//I removed this relationship:
//modelBuilder.Entity<Item>().HasMany(i => i.ItemNotes).WithRequired().HasForeignKey(i => i.ItemId);
base.OnModelCreating(modelBuilder);
}
}
Here is the test I made: It's an integration test, that connects to the real database, gets items with notes and tests the EF:
[TestCase]
public void QueryItemWithItemNotesTest()
{
FniContext fniContext = new FniContext();
int itemId = fniContext.Database.SqlQuery<int>("SELECT TOP(1) itemId FROM item WHERE itemId IN (SELECT itemId FROM dbo.itemNotes)").FirstOrDefault();
var item = fniContext.Items.AsNoTracking()
.Include(i => i.ItemNotes)
.Where(i => i.itemID == itemId).FirstOrDefault();
Assert.IsNotNull(item);
Assert.Greater(item.ItemNotes.Count, 0);
}
It passes! It loads all notes! How come?!
I kept investigating and it turned out that in case of 1:many relationship I totally don't have to have any relationship in the code. The only time I need it is with 1:1 relationship. Am I'm missing something? Most of relationships are 1:many, so does it mean Fluent API is used for 1:1 most of the time?
Entity Framework has some conventions that you do not need to define explicitly.
From https://msdn.microsoft.com/en-us/library/jj679962(v=vs.113).aspx#Anchor_2
In addition to navigation properties, we recommend that you include
foreign key properties on the types that represent dependent objects.
Any property with the same data type as the principal primary key
property and with a name that follows one of the following formats
represents a foreign key for the relationship: '<navigation property
name><principal primary key property name>', '<principal class
name><primary key property name>', or '<principal primary key property
name>'. If multiple matches are found then precedence is given in the
order listed above. Foreign key detection is not case sensitive. When
a foreign key property is detected, Code First infers the multiplicity
of the relationship based on the nullability of the foreign key. If
the property is nullable then the relationship is registered as
optional; otherwise the relationship is registered as required.
I`d like to add foreign key on unique not null attribute in second table. Here how it look like:
public class T_AlarmTresholds
{
[Key]
public int Id { get; set; }
....
....
public Guid MeasurementGuid { get; set; }
[ForeignKey("MeasurementGuid")]
public virtual T_Measurements Measurement { get; set; }
}
public partial class T_Measurements
{
public int Id { get; set; }
[Index("UC_Guid", IsUnique = true)]
public Guid GUID { get; set; }
}
Here is model builder:
modelBuilder.Entity<T_Measurements>()
.HasMany(x => x.T_AlarmTresholds)
.WithRequired(x => x.Measurement)
.HasForeignKey(x => x.MeasurementGuid);
Entity framework throws error while SQL Server accept this solution. Here is error in Visual Studio while debugging:
{"One or more validation errors were detected during model
generation:\r\n\r\nT_Measurements_T_AlarmTresholds_Source_T_Measurements_T_AlarmTresholds_Target:
: The types of all properties in the Dependent Role of a referential
constraint must be the same as the corresponding property types in the
Principal Role. The type of property 'MeasurementGuid' on entity
'T_AlarmTresholds' does not match the type of property 'ID' on entity
'T_Measurements' in the referential constraint
'T_Measurements_T_AlarmTresholds'.\r\n"}
You don't put the foreign key on the virtual object itself you need to create a new property in T_AlarmTresholds:
public int T_MeasurementsId{get; set;}
to act as the foreign key. The dependency property (the virtual one) will then link up automatically. You also need to remove the ForiegnKey attribute from the virtual property.
Edit:
Just spotted that your code doesn't use the Id column as the primary key but instead uses the Guid, so you'll instead want to add the foreign key attribute to the MeasurementGuid property.
tl;dr Do I need to have a foreign key id field as a property in the related class with EF code-first?
Following ScottGu's advice, i have created a model to reflect an existing database. Two of the tables in the db are: Project and ProjectType with a foreign key relationship. Each Project has a ProjectType.
I have added the necessary virtual fields to the model classes to reflect the relationship:
public class Project {
public int ProjectID { get; set; }
public string ProjectName { get; set; }
public DateTime CreationDate { get; set; }
...
public virtual ProjectType ProjectType {get; set; }
...
}
public class ProjectType {
public int ProjectTypeID { get; set; }
...
public virtual ICollection<Project> Projects { get; set;}
...
}
According to Scott (as shown in the image below), there is no need for the actual (foreign key) ID field to be exposed in the dependent class, ie, I don't need a public int ProjectTypeID { get; set; } property in the Project class.
However, when I try a call to retrieve the data, I hit an EntityCommandExecutionException with an InnerException of: Invalid column name 'ProjectType_ProjectTypeID'
Initial googling suggested adding a [ForeignKey] annotation to the ProjectType property. I tried both [ForeignKey("ProjectType")] and [ForeignKey("ProjectTypeID")] but neither worked.
Further googling suggested using FluentAPI with a call to:
modelBuilder.Entity<Project>().HasRequired<ProjectType>(p => p.ProjectType)
.WithMany(pt => pt.Projects)
in an OnModelCreating method, but this falls over with the same Invalid column name error.
So, do I need to have the ProjectTypeID field as a property in the Project class? If not, how do I tell EF to use the ProjectTypeID as the foreign key?
What is the foreign key column name in the existing database? You don't need to add a foreign key field but you do need to configure your modelBuilder so that foreign key names match.
modelBuilder.Entity<Project>()
.HasRequired<ProjectType>(p => p.ProjectType)
.WithMany(pt => pt.Projects)
.Map(p => p.MapKey("FK_NAME_IN_EXISTING_DB"));
You can also choose the option to have EF generate code first from database.
I have the following class in my Entity framework Code First context
public class DataBinding
{
[Key]
public DataSource Source
{ get; set; }
[Key]
public DataType Type
{ get; set; }
.....
}
Both DataSource and DataType are part of the same context, but when i try to create the database i get the following error EntityType 'DataBinding' has no key defined. Define the key for this EntityType.
DataBindings: EntityType: EntitySet 'DataBindings' is based on type 'DataBinding' that has no keys defined. i have defined two keys why am i getting this error?
The problem is you are using two complex type as PKs,which is not allowed. Key members can be only scalar properties directly in the entity. Complex type is represented as complex property which is not supported.
Check this discussion and this post.
To solve your problem you could do something like this:
public class DataSource
{
public int Id { get; set; }
//...
}
public class DataType
{
public int Id { get; set; }
//...
}
public class DataBinding
{
[Key,ForeignKey("Source"),Column(Order = 0)]
public int DataSourceId {get;set;}
[Key,ForeignKey("Type"),Column(Order = 1)]
public int DataTypeId {get;set;}
public DataSource Source { get; set; }
public DataType Type { get; set; }
//...
}
Total rewriting:
If i understood well, you want to realize an entity with composite key based as foreign keys on other entities in the same context.
You can not link the entity directly, but you can link the primary keys of this entities following the example.
composite key from foreign keys
You must in this case write explicitly the primary keys of the entities that are foreign key in the (simple types) you want to introduce on your class, as in the example before, and then add the navigation property.
When you build a composite key (in any case), you have to give an ordering to the keys.
I want to add an Entity Framework layer to an existing application with an existing database schema (no way to change this).
I want to define a simple one-to-many relationship with the difference, that the foreign key and type is different in table a from b.
Class/Table A (Event)
[Table("Events")]
public class Event
{
public Event()
{
Participants= new List<MemberCollection>();
}
[Key]
public int EventNumber { get; set; }
public Guid? MemberCollectionId{ get; set; }
[ForeignKey("CollectionId")]
public virtual ICollection<MemberCollection> Participants{ get; set; }
}
Class/Table B (MemberCollection)
[Table("MemberCollections")]
public class MemberCollection
{
[Key]
public Guid CollectionId { get; set; }
public int MemberNumber { get; set; }
[StringLength(30)]
public string RoleId { get; set; }
}
The code above leads to the following exception.
The types of all properties in the Dependent Role of a referential constraint must be the same as the corresponding property types in the Principal Role. The type of property 'CollectionId' on entity 'MemberCollections' does not match the type of property 'EventNumber' on entity 'Event' in the referential constraint 'Event_MemberCollections'.
How can I tell Entity Framework that the mapping is done through MemberCollectionId and not through EventNumber?
Thanks!
If the database already exists, is there any reason you're not using the Database First Mechanism? It will automatically create all entities and relationships for you.
All your classes are created as partials so you can still extend the classes etc in your EF layer.
I always tend to create my tables like this as I've always preferred setting up my database structure outside of EF as I was more experienced with SQL and I can concentrate on optimizing my DB first.