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.
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 have two table
Product and Order
public class Product
{
string name;
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid productId { get; set; } // i want this to be primary key instead default Id
}
public class Order
{
string name;
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid orderId { get; set; } // i want this to be primary key instead default Id and also want to add productId inside this table as foreign key
}
I have used following code to use code first.
DatabaseContext.cs
class DatabaseContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Order> Orders { get; set; }
}
Program.cs
static void Main(string[] args)
{
Database.SetInitializer(new DropCreateDatabaseAlways<DatabaseContext>());
using (var context = new DatabaseContext())
{
context.Product.Add(new Product() { Name = "mytest" });
context.SaveChanges();
}
Console.WriteLine("Database Created!!!");
Console.ReadKey();
}
getting exception
Additional information: Unable to determine composite primary key ordering for type '.Customer'. Use the ColumnAttribute (see http://go.microsoft.com/fwlink/?LinkId=386388) or the HasKey method (see http://go.microsoft.com/fwlink/?LinkId=386387) to specify an order for composite primary keys.
EF supports only properties so you should change your fields to properties. When you do that use {class Name} + Id as the property name and it will be picked up as the key property by convention. Alternatively you can use the [Key] attribute on a property to let EF know it should be the key property. You can find more about EF attributes here.
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'm developing an Entity Framework Code First (v. 4.4.0.0) C# library with .Net Framework 4.0.
I don't know how to set zero-to-one relationship. My model is the following:
A Talk can be created by only one user (StarterUserId).
A Talk can have only one recipient user (RecepientUserId) or only one group (RecipientGroupId).
Note: That means that RecepientUserId is null if RecipientGroupIdis not null; or RecepientUserId is not null if RecipientGroupIdis null.
A user can be a recipient of zero or n Talks, but a group can have zero or one Talk.
This is Talk class:
[DataContract]
public class Talk
{
[DataMember]
public int TalkId { get; set; }
[DataMember]
public int StarterUserId { get; set; }
[DataMember]
public int? RecipientUserId { get; set; }
[DataMember]
[ForeignKey("RecipientGroup")]
public int? RecipientGroupId { get; set; }
public DateTime DateUtcStarted { get; set; }
[DataMember]
public string DateStarted
{
get
{
return DateUtcStarted.ToString("dd/MM/yyyy HH:mm");
}
set
{
DateUtcStarted = DateTime.Parse(value);
}
}
public User StarterUser { get; set; }
public User RecipientUser { get; set; }
public Group RecipientGroup { get; set; }
}
With this TalkConfiguration class:
class TalkConfiguration : EntityTypeConfiguration<Talk>
{
public TalkConfiguration()
{
Property(t => t.StarterUserId).IsRequired();
Property(t => t.RecipientUserId).IsOptional();
Property(t => t.RecipientGroupId).IsOptional();
Property(t => t.DateUtcStarted).IsRequired();
Ignore(t => t.DateStarted);
HasRequired(t => t.StarterUser).
WithMany(u => u.TalksStarted).
HasForeignKey(t => t.StarterUserId);
HasOptional(t => t.RecipientUser).
WithMany(u => u.InTalks).
HasForeignKey(t => t.RecipientUserId);
HasOptional(t => t.RecipientGroup).WithOptionalDependent(g => g.GroupTalk);
}
}
And this is the Group class:
[DataContract]
public class Group
{
[DataMember]
public int GroupId { get; set; }
[ ... ]
public Talk GroupTalk { get; set; }
}
And the GroupConfiguration class:
class GroupConfiguration : EntityTypeConfiguration<Group>
{
public GroupConfiguration()
{
[ ... ] // Nothing related to GroupTalk
}
}
With these classes and configurations I get this Talk table at database:
I want to make Talk.RecipientGroupId as a FOREIGN KEY to Group.GroupId. But this model creates another column, Talk.RecipientGroup_GroupId as FOREIGN KEY to Group.GroupId. And, I don't want that.
How can I do it?
Optional:optional one-to-one relationships are mapped as independent associations, not as foreign key associations which means that you can't have a foreign key property in your model class. That's why you can't chain HasForeignKey after WithOptionalDependent. And I'm pretty sure that the [ForeignKey] attribute on RecipientGroupId is simply ignored and EF considers RecipientGroupId as an ordinary scalar property with no relationship purpose.
In the database schema itself the relationship has a foreign key. That's the one you are seeing with an autogenerated default name: RecipientGroup_GroupId. But it's not supported to map this foreign key to a property. However, I think you can rename the column using MapKey
HasOptional(t => t.RecipientGroup)
.WithOptionalDependent(g => g.GroupTalk)
.Map(m => m.MapKey("RecipientGroupId"));
If you do that you must remove the RecipientGroupId property from the Talk class, otherwise EF will complain about two conflicting columns with the same name.
I believe, optional:optional are the only one-to-one relationships that are independent associations, all other are foreign key associations where the foreign key property is the primary key property at the same time (according to Arthur Vickers' answer at the bottom of this thread). With optional:optional relationships this would be impossible because a primary key property cannot be nullable.
Since your RecipientGroupId has a [DataMember] attribute it looks that you want to transmit the value over some service boundary and therefore need the foreign key as property value for some reason. In this case the workaround that I would choose is mapping the Talk<->Group relationship as one-to-many relationship with either no navigation property in the Group class at all (mapping it with a parameterless WithMany() call then) or with a collection navigation property and ensure then in business logic that this collection cannot contain more than one element.