Entity Framework making incorrect PK-FK mapping on Code First migration - ef-code-first

I have the following 3 classes set up to be created in a SQL Server database using Entity Framework Code First migrations. The Survey object is the main table.
public class Survey
{
public int SurveyId {get; set;} //Primary Key
public string Description {get; set;}
public bool HasDevice {get; set;}
public bool HasProcess {get; set;}
public virtual Process Process {get; set;}
public virtual ICollection<Device> Devices {get; set;}
}
Each Survey can have multiple Devices (1-to-many)
public class Device
{
public int DeviceId {get; set;} //Primary Key
public string DeviceType {get; set;}
public int SurveyId {get; set;} //Foreign Key
public virtual Survey Survey {get; set;}
}
Each Survey should have only one Process (1-to-0..1)
public class Process
{
public int ProcessId {get; set;} //Primary Key
public string ProcessInfo {get; set;}
public int SurveyId {get; set;} //Foreign Key
public virtual Survey Survey {get; set;}
}
The Fluent API mapping for these classes looks like this.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasDefaultSchema("Survey");
modelBuilder.Entity<Survey>().HasOptional(x => x.Process).WithRequired(x => x.Survey);
modelBuilder.Entity<Survey>().HasMany(x => x.Devices).WithRequired(x => x.Survey);
}
The problem is that when I apply the code first migration, the ForeignKey property in the Process table (1-to-0..1) keeps getting set to the ProcessId field rather than the SurveyId. This means that every time I try to add a new Process record, I get the following error:
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_Survey.Processes_Survey.Surveys_ProcessId". The conflict occurred in database "Backflow", table "Survey.Surveys", column 'SurveyId'.
The 1-to-many mapping for Device works just fine.
I thought initially that this was due to having all my PK fields just say Id, but even after adding in the additional label part, it still makes the incorrect PK-FK link. I have also tried avoiding the Fluent API by adding the DataAnnotation [Key, ForeignKey("xyz")] instead but it has the same result. Recompiling the project, restarting Visual Studio, and even creating a new project and a new database do not help.
Is there something in the Fluent API or DataAnnotations that I am missing to get this to join correctly? Also, manually fixing the FK in the database does make it work, but that kind of defeats the purpose of doing everything in Code First with migrations.

The fluent mapping of the 1-0..1 relationship is correct:
modelBuilder.Entity<Survey>()
.HasOptional(s => s.Process)
.WithRequired(p => p.Survey);
But Process shouldn't have a SurveyID property (and column). In EF6, the dependent part of a 1-0..1 relationship (here: Process) is supposed to have a primary key that also refers to its principal (here: Survey) as foreign key. So Process.ProcessID is both primary key and foreign key. Thus, one Process is uniquely tied to one Survey.
By the way, in the other mapping, I would also mention the foreign key: if configuration is chosen over convention, it better be complete.
modelBuilder.Entity<Survey>()
.HasMany(s => s.Devices)
.WithRequired(d => d.Survey)
.HasForeignKey(d => d.SurveyId);

Related

Add/insert a new entry containing complex type in Entity Framework 7 Code first

I am trying to add a new entry for my object model which has few complex type, using EF 7 code first. But it's not working and throwing exception.
Model:
public class Student
{
public int id {get; set;}
public string Name {get; set;}
public Address Address {get; set;} -> complex type
}
public class Address
{
public int Id {get;set;}
public string Location {get;set;}
}
Code first code:
class SchoolContext {
DbSet<Student> Students {get; set;}
DbSet<Address> Addresses {get; set;}
}
var context = new SchoolContext();
context.Students.Add(new Student { Id = 1, Name = "XYZ", Address = new Address { Id = 2, Location = "US" } } -> The add method has second parameter which says include dependent objects, and by default it is true.
context.SaveChanges();
This throws exception:
{"The INSERT statement conflicted with the FOREIGN KEY constraint \"FK_Student_Address_AddressId\". The conflict occurred in database \"School\", table \"dbo.Address\", column 'Id'.\r\nThe statement has been terminated."}
This error I think means, the Address object does not exists in database, and if I add the address first and then Student, it works. But this is just too much code, as in my real application objects has many complex types.
Ideally this should work, but it does not.
Based on this bug report, you should add item before.
It is considered as-designed. At least until RC2.

Code first from database doesn't create the primary key in the builder of the dbContext

I have a Sqlite database and I have installed Sqlite v1.0.99.0. To create my connection, I am using code first from database, but in the dbContext, in the method when it is used fluent to set the relationships between the entities, the primary key is not set, so I get an error when I try to run my application.
Is there any way that the primary key is set using code first from database to avoid to have to set the primary key in the entities?
Thanks.
Alas you forgot to show the class that describes your entity nor the DbContext that contains this class.
A good beginner's description can be found in "A beginner's guide to entity framework code first"
Here you can see that if you follow certain conventions you don't have to explicitly define the primary key. Entity framework does this for you.
Quite often you see the example of a database with blogs, where each blog has zero or more posts. The blog and the post will have an Id which is the primary key and the post has a foreign key to the Id of the blog.
If you don't want to specify the properties that contains the primary key, nor the ones that contain the foreign keys, define your classes as follows:
public class Blog
{
public int Id {get; set;}
public virtual ICollection<Post> Posts {get; set;}
...
}
public class Post
{
public int Id {get; set;}
public int BlogId {get; set;}
public virtual Blog Blog {get; set;}
...
}
public class MyDbContext : DbContext
{
public virtual DbSet<Blog> Blogs {get; set;}
public virtual DbSet<Post> Posts {get; set;}
}
In the code above, property Id will automatically be the primary key of Blog and Post entities. If desired you can decide to use property names Blog.BlogId and Post.PostId. Personally I don't prefer this because this would mean that all my primary keys have different identifiers.
Property Post.BlogId will automatically be the foreign key of the blog the post belongs to.

How to sort data in an asp.net gridview by properties of sub-objects?

I have a List<Role> (see below) that I am binding to an asp.net gridview. I want to sort this data using SortExpression, such that it is sorted by two properties of sub-objects of the rows. Specifically, I want to sort by the Application's Name, then the ApplicationType's ApplicationTypeName.
How can I do this?
The classes here are:
public class Application
{
public string Name {get; set;}
public int Status {get; set;}
}
public class ApplicationType
{
public string ApplicationTypeName {get; set;}
public int ApplicationTypeStatus {get; set;}
}
public class Role
{
public Application oApplication {get; set;}
public ApplicationType oApplicationType {get; set;}
}
Edit: note that I was responding to the earlier verison of the question, before it related to gridview; still, this might be useful...
Worst case: you can use the approach here to pre-sort the list before binding it to the gridview.
Various options:
implement IComparable[<T>]
implement IComparer[<T>]
use an ad-hoc sort
I'm guessing you just need the last, so perhaps:
list.Sort((x,y) => {
int delta = string.Compare(x.Application.Name, y.Application.Name);
if (delta == 0) delta = string.Compare(
x.ApplicationType.ApplicationTypeName, y.ApplicationType.ApplicationTypeName);
return delta;
});
Alternatively, you can perhaps do it via LINQ in the source data - note however that this is done when creating a new list - it isn't an in-place sort of an existing list:
var list = source.OrderBy(x => x.Application.Name)
.ThenBy(x => x.ApplicationType.ApplicationTypeName)
.ToList();

Two foreign keys to same primary table

I have two classes: Customer and Association.
A customer can have an association with many customers. Each association is of a defined type (Family, Friend, etc) i.e Customer A is a friend of Customer B. Customer A is related to Customer C. The type of association is defined by an enum AssociationType.
In order to create this in EF i've defined the following classes
public class Customer
{
public string FirstName {get; set;}
public string LastName {get; set;}
public virtual ICollection<Association> Associations { get; set; }
}
public class Association
{
public int CustomerId { get; set; }
public virtual Customer Customer { get; set; }
public int AssociatedCustomerId { get; set; }
public virtual Customer AssociatedCustomer { get; set; }
public AssociationType AssociationType { get; set; }
}
I've removed the Data Annotations as I was unable to get this to compile. I get the error:
"Model compatibility cannot be checked because the database does not
contain model metadata".
Does anyone have any ideas?
It happens sometimes when an error occurs during database creation. The database schema is created then - except the __MigrationHistory table. When you run your application again EF wants to check against the __MigrationHistory table if the schema is still up-to-date with the model and if that table doesn't exist it throws the exception you are having.
To fix the problem either delete the database manually or set the initializer to DropCreateDatabaseAlways<MyContext> (with Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>()) - only once. After the DB is created set it back to your original initializer.
BTW: For your model you will have to specify explicitly that Customer.Associations is related to Association.Customer, either with data annotations...
[InverseProperty("Customer")]
public virtual ICollection<Association> Associations { get; set; }
...or with Fluent API:
modelBuilder.Entity<Customer>()
.HasMany(c => c.Associations)
.WithRequired(a => a.Customer)
.HasForeignKey(a => a.CustomerId);
Thank you Slauma,
your answer got us going in the right direction.
We added the following configuration to the Association configuration:
HasRequired(x => x.AssociatedCustomer).WithMany().WillCascadeOnDelete(false);

asp.net MVC3 application flow

I am working on an asp.net application. I need help designing the database related model and linq queries.
I have three tables ( and two lookup tables).
1)Product header (product header id as Primary key)
2) Product Detail(it has product header id as foreign key)
3) product attachment (it has product detail id as foreign key)
Now, I need to insert record in db.
a) for one Product header record there can be multiple Product detail records
b) for multiple product detail records, there can be multiple attachments.
I have created three entities for each tables. Product header also has two keys from user table and history table. but on view I need to show the user name instead of the key. Should I create a view model class which will hold all these entity classes as properties and how can I make sure that there first record is inserted in product header, then product details and then product attachment ?
Please suggest.
Thanks
an easy way to do this is to use the new Code First Feature that comes with EF 4.1
you can create your "entities" like so:
public class ProductHeader
{
public int ID {get; set;}
public virtual ICollection<ProductDetail> ProductDetails {get; set;}
//Other properties
}
public class ProductDetail
{
public int ID {get; set;}
public virtual ICollection<ProductAttachment> ProductAttachments {get; set;}
//Other properties
}
public class ProductAttachment
{
public int ID {get; set;}
// you can have a navigation property here for the user, this allows you to access his name
public virtual User User {get; set;}
//Other properties
}
public class MyContext:DbContext
{
public DbSet<ProductHeader> ProductHeaders {get; set;}
public DbSet<ProductDetail> ProductDetails {get; set;}
public DbSet<ProductAttachment> ProductAttachment {get; set;}
}
for the insertion order, just add your ProductHeader (after adding the ProductDetails and ProductAttachments to it) and EF will take care of it.
EDIT:
here's a sample code for adding a ProductHeader:
var context=new MyContext();
var ph=new ProductHeader();
ph.User=user;
var pd=new ProductDetail();
pd.ProductAttachments.Add(new ProductAttachment());
ph.ProductDetails.Add(pd);
context.ProductHeaders.Add(ph);
context.SaveChanges();
Hope this helps.

Resources