My problem looks simple. I need to implement a relationships between items in the database. For example: relationship between entities like computer and software shows users that computer stores a specific software and similarly - a software is installed in the specific computer. I think I should implement an entity with source id and target id or something similar. I wrote some code using code first in EntityFramework 6. Here are two classes:
public class ConfigurationItem
{
public int Id { get; set; }
public String Name { get; set; }
public String DeploymentState { get; set; }
public String IncidentState { get; set; }
[DataType(DataType.MultilineText)]
public String Description { get; set; }
[DataType(DataType.MultilineText)]
public String Note { get; set; }
public virtual ICollection<Relationship> Relationship { get; set; }
}
public class Relationship
{
[Key]
public int RelationshipId { get; set; }
[ForeignKey("ConfigurationItem")]
public int SourceId { get; set; }
[ForeignKey("ConfigurationItem")]
public int TargetId { get; set; }
public String Type { get; set; }
public virtual ConfigurationItem Source { get; set; }
public virtual ConfigurationItem Target { get; set; }
}
This solution doesn't work. I need a tip or something what should I try to make it work properly. EF throws an error about foreign key:
The ForeignKeyAttribute on property 'SourceId' on type 'cms_1.Models.Relationship' is not valid. The navigation property 'ConfigurationItem' was not found on the dependent type 'cms_1.Models.Relationship'. The Name value should be a valid navigation property name.
When I try to resolve it EF throws an error about cascade deleting. I know how to disable it but I just don't want to. I need a proper solution with that feature but I think I don't know how to do a model representing given scenario.
Simply - I need to store two foreign keys from entity "A" in the entity "B". How is it possible?
from a quick review , I can tell that you need 3 tables :
first : Computer
second : Software
third : a table , lets call it ComputerSoftware which tell which software has in what computer ( or you can also see it - which computer use what software ), which has ComputerID column and SoftwareID column.
example (source)
class Country
{
public int Id { get; set; }
public virtual ICollection<CountryCurrency> CountryCurrencies { get; set; }
}
class Currency
{
public int Id { get; set; }
}
class CountryCurrency
{
[Key, Column(Order=0)]
public virtual int CountryId { get; set; }
[Key, Column(Order=1)]
public virtual int CurrencyId { get; set; }
public virtual Country Country { get; set; }
public virtual Currency Currency { get; set; }
}
Your issue could be that in the migration file creating those tables, it will have something like
.ForeignKey("dbo.Relationship", t => t.Id, cascadeDelete: true)
This will be set on both tables, ConfigurationItem and Relationship of their Primary Key fields. When you delete one, that config tells SQL Server to delete the relationships as well and the relationship probably has a cascadeDelete: true to the parent. This will cause the cyclical cascading delete issue you are experiencing.
After the migration has been generated, go in and change one or all to cascadeDelete: false and this will fix that issue. This is what EF generates by default if I recall.
Related
As you know that developers mostly mock the relationship between tables instead of using physical relationships between table (yeah, the line drawn from one table to another if you put a foreign key constraint on the column).
But I believe that Entity Framework doesn't work properly if physical relationships aren't there for navigational properties.
So, is there any way around?
My classes:
public class Phones
{
public int ID { get; set; }
public string Model { get; set; }
public string Manufacturer { get; set; }
public List<Users> Users { get; set; }
}
public class Sims
{
public int ID { get; set; }
public string Code { get; set; }
}
This creates a 1-M relationship from User -> Sims.
But what if I drop the foreign key constraint and leave it as it is, how will the navigational properties work then?
At this case better to remove references from both classes and handle relations manually outside of these classes:
public class Sims
{
public int ID { get; set; }
public string Code { get; set; }
//public User User { get; set; }
public int UserID { get; set; }
}
I am having an issue mapping my tables together. I get the error:
Invalid column name 'Film_Id'.
Here are my Entities:
public class Film
{
[Key]
public Int32 Id { get; set; }
public String Title { get; set; }
public virtual ICollection<NormComparableFilm> NormComparableFilms { get; set; }
}
public class NormComparableFilm
{
[Key]
public Int32 Id { get; set; }
public Int32 FilmId { get; set; }
public Int32 ComparableFilmId { get; set; }
[ForeignKey("FilmId")]
public virtual Film Film { get; set; }
[ForeignKey("ComparableFilmId")]
public virtual Film ComparableFilm { get; set; }
}
Is there a custom mapping in the OnModelCreating() function that I need? I tried adding the following but it fails with a slightly different error:
modelBuilder.Entity<Film>()
.HasMany(f => f.NormComparableFilms)
.WithMany().Map(t => t.MapLeftKey("FilmId")
.MapRightKey("ComparableFilmId")
.ToTable("NormComparableFilms"));
The above gives this error:
Invalid object name 'dbo.NormComparableFilms1'.
I think I'm close but can't seem to get it just right. Any help would be appreciated.
The first error happened because you are creating two relationships between the same entities and Code First convention can identify bidirectional relationships, but not when there are multiple bidirectional relationships between two entities.The reason that there are extra foreign keys (Film_ID) is that Code First was unable to determine which of the two properties in NormComparableFilm that return a Film link up to the ICollection<NormComparableFilm> properties in the Film class. To resolve this Code First needs a little of help . You can use InverseProperty data annotation to specify the correct ends of these relationships, for example:
public class NormComparableFilm
{
[Key]
public int Id { get; set; }
public int FilmId { get; set; }
public int ComparableFilmId { get; set; }
[ForeignKey("FilmId")]
[InverseProperty("NormComparableFilms")]
public virtual Film Film { get; set; }
[ForeignKey("ComparableFilmId")]
public virtual Film ComparableFilm { get; set; }
}
Or remove the data annotation you already are using and add just these configurations:
modelBuilder.Entity<NormComparableFilm>()
.HasRequired(ncf=>ncf.Film)
.WithMany(f=>f.NormComparableFilms)
.HasForeignKey(ncf=>ncf.FilmId);
modelBuilder.Entity<NormComparableFilm>()
.HasRequired(ncf=>ncf.ComparableFilm)
.WithMany()
.HasForeignKey(ncf=>ncf.ComparableFilmId);
If in the second relationship, the ComparableFilm navigation property is optional, you need to change the type of the corresponding FK as nullable:
public class NormComparableFilm
{
//...
public int? ComparableFilmId { get; set; }
}
And use this configuration:
modelBuilder.Entity<NormComparableFilm>()
.HasOptional(ncf=>ncf.ComparableFilm)
.WithMany()
.HasForeignKey(ncf=>ncf.ComparableFilmId);
About the second error, you are trying to call the Film table as NormComparableFilms that is the default name that EF will give by convention to the table represented by the NormComparableFilm entity.
if you need to rename one of your tables, you can use this configuration:
modelBuilder.Entity<Film>().ToTable("Films"));
Writing a model for situation where I have two tables which are customers and users. Each user record might have an optional related customer record and vice versa, but none of them is a must. I figured out that FK Associations are not what I need, but Independent Associations are. But I just can find a way to make it work, I keep getting the 'Unable to determine the principal end...The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.' exception.
My models are very simple:
public class User
{
[Key]
public int Id { get; set; }
[StringLength(20)]
public string CustomerId { get; set; }
public string Password { get; set; }
public bool Locked { get; set; }
//[ForeignKey("CustomerId")]
public virtual Customer Customer { get; set; }
}
public class Customer
{
[Key]
[Column("Id", TypeName = "nvarchar")]
[StringLength(20)]
public string Id { get; set; } // nvarchar 20
[Required]
public string GivenName { get; set; } // nvarchar 100
[Required]
public string Surname { get; set; } // nvarchar 100
//[InverseProperty("Customer")]
public virtual User User { get; set; }
}
I've tried to add the ForeignKeyAttribute and InversePropertyAttribute, which are currently commented out, but they didn't help either. I would prefer to use data annotations and not fluent API, if it's possible in my case.
In one-to-one relation one end must be principal and second end must be dependent. Principal end is the one which will be inserted first and which can exist without the dependent one. Dependent end is the one which must be inserted after the principal because it has foreign key to the principal. When configuring one-to-one relationships, Entity Framework requires that the primary key of the dependent also be the foreign key.This problem is most easily solved by using a ForeignKey annotation on the dependent class to identify that it contains the foreign key. In your case, Customer could be the dependent and its key, Customer.UserId, should also be the foreign key. But both Keys must be declared using the same type:
public class User
{
[Key]
public int Id { get; set; }
public virtual Customer Customer { get; set; }
}
public class Customer
{
[Key, ForeignKey("User")]
public int UserId { get; set; }
public virtual User User{ get; set; }
}
I don't know how to resolve your problem using Data Annotations, but if you want to use Fluent Api, I think the configuration of the relationship would be like this:
modelBuilder.Entity<User>().HasOptional(u => u.Customer).WithOptionalPrincipal(c => c.User);
Update
I understand your escenario, but if you have the same columns that you show in your model, I think you should have a one-to-many relationship mapped in DB instead one-to-one. Try to map your relationship this way:
public class User
{
[Key]
public int Id { get; set; }
public string Password { get; set; }
public bool Locked { get; set; }
public string CustomerId { get; set; }
[ForeignKey("CustomerId")]
public virtual Customer Customer { get; set; }
}
public class Customer
{
[Key]
[Column("Id", TypeName = "nvarchar")]
[StringLength(20)]
public string Id { get; set; } // nvarchar 20
[Required]
public string GivenName { get; set; } // nvarchar 100
[Required]
public string Surname { get; set; } // nvarchar 100
public virtual ICollection<User> Users { get; set; }
}
Remember map your properties with the same column'names that you have in DB.
public class Product
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public bool IsInStock { get; set; }
public string ImageUrl { get; set; }
public List<ProductOption> ProductOptions { get; set; }
public virtual Category Category { get; set; }
}
public class ProductOption
{
[Key]
public int Id { get; set; }
public string ProductOptionName { get; set; }
public string ProductOptionDescription { get; set; }
}
Now I know when your using Code First EF, so that the tables are created correctly. You need to do something like this.
modelBuilder.Entity<Product>().HasMany(p => p.ProductOptions).WithMany().Map(m =>
{
m.MapLeftKey("ProductId").MapRightKey("ProductOptionId").ToTable("SelectedProductOptionsInOrderedItem");
});
So....
Does this mean that if I do something like Product.ProductOptions I will be able to access all associated productoptions.
Is this the best way to set it up, or is there another way?
To enable lazy load and EF can create derived proxy types for your collection, that property should be declared this way:
public virtual ICollection<ProductOptions> ProductOptions { get; set; }
That should be enought. Other aspect is the mapping approach that you use. You choose fluent api, i prefer mapping by convention, but that is a matter of personal taste anyway.
Ok, Mapping by Conventions:
Is the ability of EF that from the name of entities and their properties along with their types, to map our model with the underlying data without providing any other information.
for example
public class Customer {
public long CustomerID {get; September;}
public string CustomerName {get; September;}
public Employee AssignedTo {get; September;}
}
With the previous model EF will map database with a table named Customer with:
. CustomerID bigint primary key column
. CustomerName nvarchar column
. Customer_EmployeeID foreign key to Employee table, with the datatype Corresponding to EmployeeID in that table.
You can read more Here
I'm trying to create a simple entity framework code first application. I have these classes:
public class User
{
public int UserId { get; set; }
public string Username { get; set; }
public virtual ActivationTicket ActivationTicket { get; set; }
}
public class ActivationTicket
{
public int ActivationTicketId { get; set; }
public virtual User User { get; set; }
public string Ticket { get; set; }
}
When I try to create a new user and save it to the database (a user without a ActivationTicket that is) I receive an exception
The INSERT statement conflicted with the FOREIGN KEY constraint "ActivationTicket_User". The conflict occurred in database "Test", table "dbo.ActivatioTickets", column 'ActivationTicketId'. The statement has been terminated.
I assume EF treats the mapping between User and ActivationTicket as 1-1 but it should be 1-0..1
What do I have to do to get this to work?
You will need a mapping rule like this:
modelBuilder
.Entity<User>()
.HasOptional<ActivationTicket>(u => u.ActivationTicket)
.WithOptionalPrincipal();
This will give you an ActivationTickets table with a UserId that is nullable.
#b3n
It should be enough to do this, at least with VS 2013:
public class User
{
public int UserId { get; set; }
public string Username { get; set; }
public int? ActivationTicketId { get; set;}
public virtual ActivationTicket ActivationTicket { get; set; }
}
This is the important part:
public int? ActivationTicketId { get; set;}
This will specify the foreignkey in your "User" table for the ActivasionTicket and define that it's optional.
credits go to:
http://forums.asp.net/t/1948351.aspx?Change+Foreign+Key+to+nullable+using+Code+First#5554732
I had the same problem and it instantly worked for me.
Also as a note i marked all my primary keys with the data annotation "[Key]". This might be necessary in order to make this work.