Basically, I want to have a user that can create their own stories.
I have these classes:
public class ApplicationUser : IdentityUser
{
public string DisplayedName { get; set; }
}
public class Story
{
public int Id { get; set; }
public string Content { get; set; }
}
They are managed on a different context and so as their migration. Something like this.
public class MyDbContext : DbContext
{
public DbSet<Story> Stories { get; set; }
}
public class IdentityContext : IdentityDbContext<ApplicationUser>
{
}
When I try to add a migration then update them individually, it works fine but when I try to add a collection of stories in my application user.
public class ApplicationUser : IdentityUser
{
public string DisplayedName { get; set; }
public virtual ICollection<Story> Stories { get; set; }
}
public class Story
{
public int Id { get; set; }
public string Content { get; set; }
public string WrittenById { get; set; }
public virtual ApplicationUser WrittenBy { get; set; }
}
public class StoryMap : EntityTypeConfiguration<Story>
{
public StoryMap()
{
HasOptional(s => s.WrittenBy)
.WithMany(s => s.Stories)
.HasForeignKey(s => s.WrittenById)
.WillCascadeOnDelete(false);
}
}
Then do a migration on my Story entity using the contenxt of MyDbContext it fails saying.
Data.IdentityUserLogin: : EntityType 'IdentityUserLogin' has no key defined. Define the key for this EntityType.
Data.IdentityUserRole: : EntityType 'IdentityUserRole' has no key defined. Define the key for this EntityType.
IdentityUserLogins: EntityType: EntitySet 'IdentityUserLogins' is based on type 'IdentityUserLogin' that has no keys defined.
IdentityUserRoles: EntityType: EntitySet 'IdentityUserRoles' is based on type 'IdentityUserRole' that has no keys defined.
But when I try the other way around in which I'll do a migration using the IdentityContext it would create a new table of Story
For now, what works is merging my contexts. Something like.
public class MyDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<Story> Stories { get; set; }
}
But there must be a way of managing them separately, right? Or am I doing it all wrong?
You can't reference entities from one context in another, or that context will attempt to manage those entities as well, resulting in errors about tables already existing. You have two options:
If you don't actually need two separate contexts (i.e., they're both Code First and you're fine with everything being in one database), then the best and easiest solution is to just merge them as you've done. There's no benefit to having multiple contexts, and as you've seen, there's plenty of detriment. The only good reason to ever use multiple contexts is if you're dealing with additional existing databases.
Create a simple column to store the related id (not a foreign key). You lose the optimization of having a true foreign key and the ability to lazy load, but you can still at least somewhat relate things this way. Essentially, you just set this property with the id of the related object in the other context. Then, when you need to retrieve that object, you just issue a query with that other context, utilizing that id. In other words, you just manually fetch the objects.
That's your only options, unfortunately.
Related
I am trying to use partial classes to decorate EF's auto-generated entity models for SQL views that I have access to. These views are 1:1 representations of tables, but omit the primary keys and foreign key constraints present in the database. I would like to reproduce these keys/constraints with data annotations, but attempting to use them within EF fails.
Any attempts to use Include or Find within LINQ always fail. The foreign key defined here, for example, does not seem to be recognized:
public class FactTimeEntryMetadata
{
[Key]
public int TimeEntryKey { get; set; }
[ForeignKey("DimEmployee.EmployeeKey")]
public int EmployeeKey { get; set; }
}
[MetadataType(typeof(FactTimeEntryMetadata))]
public partial class FactTimeEntry { }
public class DimEmployeeMetadata
{
[Key]
public int EmployeeKey { get; set; }
}
[MetadataType(typeof(DimEmployeeMetadata))]
public partial class DimEmployee { }
In this example, the FactTimeEntry contains TimeEntryKey as a primary, and a column for EmployeeKey, referring to the foreign key in DimEmployee. Is my syntax off in some way, or is this simply not possible with Entity Framework database-first?
Edit:
I have also tried to add a virtual reference to the object, like so:
public class FactTimeEntryMetadata
{
[Key]
public int TimeEntryKey { get; set; }
[ForeignKey("DimEmployee")]
public int EmployeeKey { get; set; }
public virtual DimEmployee DimEmployee { get; set; }
}
[MetadataType(typeof(FactTimeEntryMetadata))]
public partial class FactTimeEntry { }
public class DimEmployeeMetadata
{
[Key]
public int EmployeeKey { get; set; }
}
[MetadataType(typeof(DimEmployeeMetadata))]
public partial class DimEmployee { }
but was also unsuccessful this way.
Your only three options using ef-database-first:
Put your keys and relationships in your database. Relational Databases were created to be relational. If you don't do that, why use a database?
As Gert Arnold metion in his comment, edit the EDMX and assign your associations.
Edit the T4 templates with custom code to add Keys/relationships to your objects.
I am using an existing database with EF Code First and using the modelBuilder to configure. I have two tables where a SESSION can have a SUBJECT, classes are as such:
public class SessionItem {
[Key]
public int SessionId { get;set; }
// Other Values
public int Subject_ID { get;set; }
public virtual Subject Subject { get;set; }
}
public class SubjectItem {
[Key]
public int Subject_ID { get;set; }
// Other Values
public virtual SessionItem Session { get;set; }
}
And then the modelBuilder code is:
modelBuilder.Entity<SessionItem>().ToTable("tblTblSessions");
modelBuilder.Entity<Subject>().ToTable("tblTblSubjects");
modelBuilder.Entity<SessionItem>()
.HasOptional<Subject>(u => u.Subject)
.WithOptionalDependent(c => c.Session).Map(p => p.MapKey("Subject_ID"));
This failed at first until I removed Subject_ID from the SessionItem class, then I got the error: A relationship multiplicity constraint violation occurred: An EntityReference can have no more than one related object, but the query returned more than one related object. This is a non-recoverable error.
Any idea where I have gone wrong?
Unfortunely one-to-one foreign key associations are not supported with Entity Framework because EF doesn't know what a unique key constraint is (that your Subject_ID column in the Session table apparently has).
You must workaround this by mapping the relationship as one-to-many. Follow the mapping in #flem's answer for the SessionItem entity and for the SubjectItem entity either remove the public virtual SessionItem Session { get;set; } altogether or replace it by
public virtual ICollection<SessionItem> Sessions { get; set; }
You don't need the mapping with Fluent API anymore for this relationship, or if you want, it should be:
modelBuilder.Entity<SessionItem>()
.HasOptional(se => se.Subject)
.WithMany() // or WithMany(su => su.Sessions)
.HasForeignKey(se => se.Subject_ID);
When you add items to that collection you must ensure in your business logic that you don't add more than one item because you can't have more than one row with the same Subject_ID in your database due to the unique key constraint. When you load a subject from the database including the sessions the session collection is either empty or has one single element, but not more.
Try this:
public class SessionItem
{
[Key]
public int SessionId { get;set; }
// Other Values
[ForeignKey("Subject")]
public int? Subject_ID { get;set; }
[ForeignKey("Subject_ID")]
public virtual SubjectItem Subject { get;set; }
}
You need to make Subject_ID foreign key nullable.
[Table("tblTblSessions")]
public class SessionItem {
[Key]
public int SessionId { get; set; }
public virtual SubjectItem Subject { get; set; }
}
[Table("tblTblSubjects")]
public class SubjectItem {
[Key, ForeignKey("Session")]
public int Subject_ID { get; set; }
public virtual SessionItem Session { get; set; }
}
One-to-one relationship between SubjectItem and SessionItem, and you can get rid of all your modelBuilder code - all the table naming and one-to-one mapping you were doing is taken care of with the attributes and properties above.
Edit: Fixed a typo and marked the Dependent side of the one-to-one.
I have a couple of classes (for this example anyway) that use code first with the entity framework to connect to the database.
public class Customer
{
[Key]
public long CustomerId { get; set; }
public string CompanyName { get; set; }
...
public virtual List<Contact> Contacts { get; set; }
}
public class Contact
{
[Key]
public long ContactId { get; set; }
public string Forename { get; set; }
...
public long CustomerId { get; set; }
public virtual Customer Customer { get; set; }
}
When I hook these up in my context class directly to the db the foreign key relationships hook up fine and I can access the collection of contacts from within the customer class.
class RemoteServerContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
public DbSet<Contact> Contacts { get; set; }
...
}
My problem is that these database tables are used by various different systems and are massive. In order to increase efficiency I have overridden the default behaviour to point at a view (and also a stored proc elsewhere) rather than directly at the table.
public IEnumerable<Customer> Customers ()
{
return Database.SqlQuery<Customer>("SELECT * FROM vw_CustomerList");
}
public IEnumerable<Contact> Contacts()
{
return Database.SqlQuery<Contact>("SELECT * FROM vw_ContactsList");
}
I have made sure that in each of the views I have included the foreign key fields: CustomerId and ContactId.
When I do this however the class joins appear to be lost - there's always a null when I drill into either of the objects where it should be pointing to the other one. I have tried to set up what the foreign key field should point to but this doesn't seem to help either.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Contact>().HasRequired(p => p.Customer)
.WithMany()
.HasForeignKey(k => k.CustomerId);
}
Is there a way to establish the connection when overriding the default behaviour?
There is no overriding in this case. If you removed
public DbSet<Customer> Customers { get; set; }
and replaced it with
public IEnumerable<Customer> Customers ()
{
return Database.SqlQuery<Customer>("SELECT * FROM vw_CustomerList");
}
you have completely changed the behavior. The first uses entities and full power of EF. The second is only helper to execute custom SQL. Second without first or without defining entity in OnModelCreating doesn't use Customer as mapped entity at all - it uses it as any normal class (only mapped entities can use features like lazy loading).
Because your Customer is now mapped to view you cannot use your former Customer class used with table. You must define mapping of Customer to a view by cheating EF:
modelBuilder.Entity<Customer>().ToTable("vw_ContactsList"); // EF code fist has no view mapping
Once you have this you can try again using:
public DbSet<Customer> Customers { get; set; }
Unless your view is updatable you will get exception each time you try to add, update or delete any customer in this set. After mapping relation between Customer and Contact mapped to views your navigation properties should hopefully work.
The problem with SqlQuery is the way how it works. It returns detached entities. Detached entities are not connected to the context and they will not lazy load its navigation properties. You must manually attach each Customer instance back to context and to do that you again need DbSet.
I've been trying to create model in EF 4.1 to represent a database schema with a single table and column holding foreign keys from two other tables, but have had little luck with both annotations and the fluent API. A sample model is shown here:
public class User
{
...
public virtual ExtendedAttribute ExtendedAttributes { get; set; }
}
public class Account
{
...
public virtual ExtendedAttribute ExtendedAttributes { get; set; }
}
public class ExtendedAttribute
{
public Guid Id {get; set;}
public Guid ItemId {get; set;} // both Account.Id and User.Id stored here
public string Value { get; set; }
}
Currently the configuration for these entities looks something like this for both User and Account modelBuilders:
this.HasOptional(u => u.ExtendedAttributes).WithRequired();
Any thoughts on how to do achieve? Many thanks.
It is even not possible with the database itself and EF will not put any abstraction for that. You must have separate column and navigation property for each entity.
I am using EF4 code first and want to generate a composite key which is made of a class property and foreign key. I have two classes: Order and Company. The Order class holds a reference but this will not necessarily be unique between companies. So I intend to use a composite key made up of Reference and Company.CompanyId.
I have tried using the following to set it but I get an error message "Key expression is not valid".
modelBuilder.Entity<Order>().HasKey(o => new { o.Reference, o.Company.CompanyId });
I have also tried
modelBuilder.Entity<Order>().HasKey(o => new { o.Reference, o.Company });
and this fails.
these are my classes:
public class Order
{
public string Reference { get; set; }
public Company Company { get; set; }
}
public class Company
{
public int CompanyId { get; set; }
public virtual ICollection Orders { get; set; }
}
Any help would be greatly appreciated.
As Antony Highsky mentioned, you can only use scalar properties in the key.
So, you will need to add a foreign key (scalar property) to the Order class and associate it with the navigation property Company as shown below:
public class Order
{
public string Reference { get; set; }
public int CompanyId { get; set; }
[RelatedTo(ForeignKey = "CompanyId")]
public Company Company { get; set; }
}
And then create the composite key using the model builder:
modelBuilder.Entity<Order>().HasKey(o => new { o.Reference, o.CompanyId });
Note that data annotations (RelatedTo attribute) were introduced with the Entity Framework CTP 3. For another option that only uses data annotations instead of HasKey method, see this post:
http://www.luisrocha.net/2010/11/creating-composite-keys-using-code.html
One thing that doesn't look quite right is your use of the non-generic version of ICollection. Try this:
public virtual ICollection<Order> Orders { get; set; }
Did you try this?
modelBuilder.Entity().HasKey(o =>o.Reference );
modelBuilder.Entity().HasKey(o =>o.CompanyId );
According to this source, only scalar properties are allowed in the key. Navigation properties are not.