I try to get navigational properties to work between a object that I own and maintain and a view that is controlled by a other party. Iam using a MS SQL Server database.
I have 2 models
public class Trajectory
{
public Guid Id { get; set; }
//Some more data
public Contact Contact { get; set; }
public Guid ContactId { get; set; }
}
public class Contact
{
public Guid Id { get; set; }
public string FullName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<Trajectory> Trajectories {get;set;}
}
The first one is a model which is created and migrate with entity. The second one is a external view which I have no control over.
So I did this in my model builder:
modelBuilder.Entity<Contact>()
.HasNoKey()
.ToView("SomeView");
And for my other entity:
builder.HasOne(e => e.Contact)
.WithMany(e => e.Trajectories)
.HasForeignKey(e => e.ContactId );
When I ran this migration it failed with a null pointer. When I removed the HasNoKey() from the first part I got a migration. When I ran the update-database command I got the following error:
Foreign key 'FK_Trajectories_SomeView_ContactId' references object 'SomeView' which is not a user table.
Could not create constraint or index. See previous errors.
I'am kind of lost with how I can create a navigational property to the view?
Related
I am designing a database i have three models(user, movie, review).The idea is each user can submit a movie each user can leave a review for a movie.The user model has ICollection and ICollecyion, the movie model has foreign key UserId and ICollection, and the review model has foreign keys for UserId and MovieId. When i try update-database i get this error:
Introducing FOREIGN KEY constraint 'FK_Review_User_UserId' on table 'Review' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Here are my models :
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public int FavouriteGenre { get; set; }
public ICollection<Movie> SubmittedMovies { get; set; }
public ICollection<Review> SubmittedReviews { get; set; }
}
public class Movie
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string PosterUrl { get; set; }
public int Year { get; set; }
public int Genre { get; set; }
public DateTime Posted { get; set; }
public int UserId { get; set; }
public User User { get; set; }
public ICollection<Review> Reviews { get; set; }
}
public class Review
{
public int Id { get; set; }
public int Rating { get; set; }
public string UserReview { get; set; }
public int MovieId { get; set; }
public Movie Movie { get; set; }
public int UserId { get; set; }
public User User { get; set; }
public DateTime Posted { get; set; }
}
And my on creating method in the dbContext :
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Review>()
.HasOne(r => r.User)
.WithMany(u => u.SubmittedReviews)
.HasForeignKey(r => r.UserId);
builder.Entity<Review>()
.HasOne(r => r.Movie)
.WithMany(m => m.Reviews)
.HasForeignKey(r => r.MovieId);
}
I have tried this :
builder.Entity<Review>()
.HasOne(r => r.User)
.WithMany(u => u.SubmittedReviews)
.HasForeignKey(r => r.UserId)
.IsRequired(false)
.OnDelete(DeleteBehavior.Restrict);
builder.Entity<Review>()
.HasOne(r => r.Movie)
.WithMany(m => m.Reviews)
.HasForeignKey(r => r.MovieId)
.IsRequired(false)
.OnDelete(DeleteBehavior.Restrict);
I have also tried making the foreign keys in the Review model nullable(i have tried separately and both at the same time) with DeleteBehaviour.Restrict in the context , but nothing seems to work , when i deleted a movie it worked well , but when i tried to delete a user i got this error in SSMS :
The DELETE statement conflicted with the REFERENCE constraint "FK_Review_User_UserId". The conflict occurred in database "MovieDb", table "dbo.Review", column 'UserId'.
I think i understand the problem but i cant find a solution.
From this article
In SQL Server, a table cannot appear more than one time in a list of all the cascading referential actions that are started by either a DELETE or an UPDATE statement. For example, the tree of cascading referential actions must only have one path to a particular table on the cascading referential actions tree
Your schema contains a circular reference because a user can have a review on their own movie.
Here's what's (potentially) happening when you try to delete a user:
Reviews related to that user are cascade-deleted
Movies related to that user are cascade-deleted
Reviews related to that movie are cascade-deleted
The last two points there are what is causing the issue. Since both User and Movie have (I'm assuming) required relationships, you can have a scenario where the DB tries to delete the same record twice because it has a foreign key reference to a User and that same review is on a movie that the User created.
I would make the UserId field nullable/optional on the Reviews table.
Based on your description, I'm not sure if you set the property to be nullable as well, but your code should look like this:
public class Review
{
// Other fields hidden
public int? UserId { get; set; }
// ...
}
In your FluentAPI model builder definitions, you can set Movie Reviews to cascade, and SubmittedReviews on the users table to restrict on delete. You can also remove the .IsRequired(false) line from the model builder definition for Movie Reviews
Personally, this is why I just use "Soft Delete" columns (like DeletedBy, DeletedOn) in models with lots of dependent relationships. It's a bit more involved in your API code, as you have to manually go in and delete/mark
as deleted the related records, but it prevents headaches like this.
I'm trying to create the POCO entity within my datacontext for the join table (the one created for the m2m relationship).
The reason I'm doing that is that I want to handle keys inserting in the table by my own. (Don't ask me why, several performance issues.)
So the algorithm is like that:
I generate the POCO class using native ADO.NET tools based on the existing table, let's say TableAnotherTable (this table joins two others Table and AnotherTable). Or just creating it manually.
I'm trying to add the migration for it. Either with -IgnoreChanges or not. Without -IgnoreChanges it tries to rename the existing table TableAnotherTable to TableAnotherTable1. Which sounds fair enough but why? It should just map the existing table to newly created POCO class.
I'm cleaning the Up() and Down() methods.
Trying to run the app and do some CUD operations within the context and constantly getting an error: Invalid object name dbo.TableAnotherTable1.
So the main question: How can I map the join table created by entity framework to my own class in order to work with it like with regular entity?
Update:
public class Client
{
public int Id {get;set;}
public ICollection<Group> Groups {get;set;}
}
public class Group
{
public int Id {get;set;}
public ICollection<Client> Clients {get;set;}
}
There is no additional configurations or something.
the name of join table in database is GroupClient
The poco class I'm retrieving with ADO.NET poco generation tools is:
[Table("GroupClient")]
public partial class GroupClient
{
[Key]
[Column(Order = 0)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Group_Id { get; set; }
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Client_Id { get; set; }
}
The initial model with auto link table should have generated GroupClient table with two columns Group_Id and Client_Id forming the PK.
You should be able to map it to a model with the generated explicit GroupClient entity shown, which already defined the properties and the PK, but you need to change the type of the existing entity navigation properties and also specify the FK's:
public class Client
{
public int Id { get; set; }
[ForeignKey("Client_Id")]
public ICollection<GroupClient> Groups { get; set; }
}
public class Group
{
public int Id { get; set; }
[ForeignKey("Group_Id")]
public ICollection<GroupClient> Clients { get; set; }
}
I personally find using Fluent configuration much easier to follow:
Model:
public class Client
{
public int Id { get; set; }
public ICollection<GroupClient> Groups { get; set; }
}
public class Group
{
public int Id { get; set; }
public ICollection<GroupClient> Clients { get; set; }
}
public class GroupClient
{
public int Group_Id { get; set; }
public int Client_Id { get; set; }
}
Configuration:
modelBuilder.Entity<GroupClient>()
.ToTable("GroupClient")
.HasKey(e => new { e.Group_Id, e.Client_Id });
modelBuilder.Entity<Group>()
.HasMany(e => e.Clients)
.WithRequired() // or (e => e.Group) in case you add nav property
.HasForeignKey(e => e.Group_Id);
modelBuilder.Entity<Client>()
.HasMany(e => e.Groups)
.WithRequired() // or (e => e.Client) in case you add nav property
.HasForeignKey(e => e.Client_Id);
I am currently making a web app for MVC 3 using Entity Framework but I'm a beginner and I can't make the Code First mapping work properly and for some reason my Visual Studio doesn't have Database First and Model First functionality.
I have three model classes: Contacto, Proveedor and Usuario, each one of them corresponding to a table on an database existing database and I want to make those classes with Code First to map to this database. Contacto has a one-to-one relationship with Proveedor and Usuario. I declared the model classes like this:
public class Contacto
{
public int ID { get; set; }
public string Nombre { get; set; }
public string Telefono { get; set; }
public Proveedor ProveedorID { get; set; }
public Usuario UsuarioID { get; set; }
}
public class Proveedor
{
public short ID { get; set; }
public string NombreProveedor {get;set;}
}
public class Usuario
{
public short ID { get; set; }
public string NombreUsuario {get;set;}
}
The context class is the following:
public class Contexto : DbContext
{
public DbSet<Contacto> Contactos { get; set; }
public DbSet<Proveedor> Proveedores { get; set; }
public DbSet<Usuarios> Usuarios { get; set; }
}
I made a controller and view with CRUD methods but when the code gets to the EditorFor for the Proveedor attribute in Contacto I get a Invalid column name 'ProveedorID' and the same for UsuarioID. Changing the table names on the existing database doesn't fix this but sometimes makes the Invalid column name error refer to Contacto_ID instead of ProveedorID.
How do I make the reference to the Proveedor and Usuario model in Contacto work? And how override the automatic table mapping and specify the mapping manually? The name conventions don't work well in Spanish and don't detect properly that there is a relationship in a database.
When you don't tell EF anything about how entities and properties map to table and column names, it will make its own assumptions by following a number of conventions. One of them is to assume that a foreign key column is the name of the referred entity with _ID appended to it.
If you don't want EF to follow these default conventions, you have to add your own mappings. I'll show an example of how you can do this.
First, I'd remove the "ID" part from the reference properties, and add primitive foreign key properties to the Contacto class:
public class Contacto
{
public int ID { get; set; }
public string Nombre { get; set; }
public string Telefono { get; set; }
public short ProveedorID { get; set; }
public Proveedor Proveedor { get; set; }
public short UsuarioID { get; set; }
public Usuario Usuario { get; set; }
}
One way to tell EF about database names of entities and properties is fluent mapping. You can either do this in the OnModelCreating override of your context class, or in an EntityTypeConfiguration override:
public class ContactoMap : EntityTypeConfiguration<Contacto>
{
public ContactoMap()
{
ToTable("tblContacto");
Property(c => c.ProveedorID).HasColumnName("ProveedorId");
HasRequired(x => x.Proveedor).WithMany().HasForeignKey(c => c.ProveedorID);
Property(c => c.UsuarioID).HasColumnName("UsuarioId");
HasRequired(x => x.Usuario).WithMany().HasForeignKey(c => c.UsuarioID);
}
}
I just make up some names, since you don't mention which database names you want to have, but you get the idea.
The final step is to add this mapping to the model by overriding OnModelCreating in the context:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ContactoMap());
}
By the way, if you have these properties like ProveedorID, EF will also infer these as foreign keys to the matching entity. Only when you don't include these primitive foreign key properties, EF will expect the column names with underscores in the database.
I have som mapping problem with EF.
This is my classes
public class User{
public Guid Id { get; set; }
// Fullname of the user account owner
public string Name { get; set; }
public string Email { get; set; }
public string Username { get; set; }
public Player Player { get; set; }
}
public class Player
{
public Guid Id { get; set; }
public string Name { get; set; }
public virtual User User { get; set; }
}
It works fine, but now I want to create the navigation property Player and User in this classes. I have this Fluent code:
modelBuilder.Entity<User>()
.HasOptional(x => x.Player)
.WithOptionalDependent(x => x.User)
.Map(x => x.MapKey("Username"));
But I only get this error message, and I have no ide what's wrong.
Each property name in a type must be unique. Property name 'Username'
was already defined.
My DB setup looks like the classes, in the player table the Name is unique. It's not unique in the User table. A user can exist without a player and vice versa. (Actully I don't want any User property inside the Player class but I think it's a requierment?!)
I think it's complaining about the fact that UserName is already a property in the object model. See the docs for the Map() method:
From http://msdn.microsoft.com/en-us/library/system.data.entity.modelconfiguration.configuration.foreignkeynavigationpropertyconfiguration.map%28v=vs.103%29:
Configures the relationship to use foreign key property(s) that are
not exposed in the object model. The column(s) and table can be
customized by specifying a configuration action. If an empty
configuration action is specified then column name(s) will be
generated by convention. If foreign key properties are exposed in the
object model then use the HasForeignKey method. Not all relationships
support exposing foreign key properties in the object model.
Remove the modelBuilder code and mark the PrimaryKey as a ForeignKey on the dependent table. For example if Players don't exist without a User:
public class User
{
public Guid Id { get; set; }
// Fullname of the user account owner
public string Name { get; set; }
public string Email { get; set; }
public string Username { get; set; }
public Player Player { get; set; }
}
public class Player
{
[ForeignKey("User")]
public Guid Id { get; set; }
public string Name { get; set; }
public virtual User User { get; set; }
}
The ForeignKey attribute tells EF which side of the one-to-one is dependent, allowing it to map it properly.
If your columns in the database has the same name as the properties of your model you don't need to map the property ".Map(x => x.MapKey("Username"));" EF already mapped the property "Username" using the convention and is because of that the EF is complaining
With your entities
...I just like to do it the other way around:
modelBuilder.Entity<Player>()
.HasRequired(i => i.User)
.WithRequiredDependent(i => i.Player);
or this (optional):
modelBuilder.Entity<Player>()
.HasRequired(i => i.User)
.WithOptional(x => x.Player);
Hi I am having a problem with a simple EF 4.1 code first model.
I have a class person and a class survey that are bidirectionally linked. The database model is correct but I always get this error:
Unable to determine the principal end of an association between the types 'DAL.Models.Survey' and 'DAL.Models.Person'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
Class Person
[Key]
public Guid Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string Email { get; set; }
public virtual Survey Survey { get; set; }
Class Survey
[Key]
public Guid Id { get; set; }
public bool IsFinished { get; set; }
public virtual Person Person { get; set; }
Datacontext:
modelBuilder.Entity<Survey>().HasRequired(s => s.Person).WithOptional().WillCascadeOnDelete(true);
Can anyone help please
You should define the other navigation property in your mapping since you have it in the model. Otherwise EF will create a second (one-to-many) association:
modelBuilder.Entity<Survey>()
.HasRequired(s => s.Person)
.WithOptional(p => p.Survey)
.WillCascadeOnDelete(true);
I think you have to specify either a foreign key property through HasForeignKey or foreign key column name using Map. Something like this:
modelBuilder.Entity<Survey>()
.HasRequired(s => s.Person)
.WithOptional()
.WillCascadeOnDelete(true)
.Map(m => m.MapKey("fk_column"));