I'm using EF6.2.0.
I need to create a many-many relationship between two POCO classes, but I need to add a custom property against the link.
So, I created this class to serve as my many-many link POCO:
class SampleGroupSpecies
{
[Key, Column(Order = 0)]
public int GroupId { get; set; }
[Key, Column(Order = 1)]
public int SpeciesId { get; set; }
public bool IsContaminated { get; set; }
[Required]
public virtual Group Group { get; set; }
[Required]
public virtual Species Species { get; set; }
}
however, when EF generates the table, I get this:
dbo.SampleGroupSpecies
Columns
GroupId (PK, FK, int, not null)
SpeciesId (PK, int, not null)
IsContaminated (bit, not null)
Species_Id (FK, int, not null)
As you can see, it's creating its own "Species_Id" column and using that as the foreign key, but that's not what I want.
I can obviously add a GroupSpeciesId column, set that as the only [key] and it'll all work as if by magic, but that's not good design.
I'm trying to avoid using fluent and just use annotations if possible.
Someone tell me where I've gone wrong :)
This was caused by me setting the navigation property incorrectly on the Species POCO - It was not set as a collection of SampleGroupSpecies, but as a single - meaning EF was correctly trying to create a one-many relationship, but I was trying to force it to create a many-to-many when it couldn't :)
Solution was to check both related POCOs to make sure their navigation properties are set as a collection, rather than a single instance.
Related
I have a solution which uses Entity Framework to insert invoices to a database table. These invoices reference an order, which in turn also references an order item collection.
In this instance I am trying to add an order to the database, however the code is inside a new DbContext and so I need to attach the order and order items to the context, as these already exist in the database and shouldn't be re-added.
I've cut down the model properties for the sake of demonstration:
public class Invoice {
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int InvoiceId { get; set; }
public string OrderNumber { get; set; }
...
public virtual List<InvoiceLineItem> LineItems { get; set; }
}
public class InvoiceLineItem {
[Key]
public int Id { get; set; }
...
public ShopifyOrderItem { get; set; }
}
public class ShopifyOrder {
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public long Id { get; set; }
public int OrderNumber { get; set; }
...
public OrderInvoiceStatus InvoiceStatus { get; set; }
public virtual List<ShopifyOrderItem> OrderItems { get; set; }
}
public class ShopifyOrderItem {
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public long Id { get; set; }
...
[Required]
public virtual ShopifyOrder ShopifyOrder { get; set; }
}
In the invoice engine, I'm running the following code for each invoice to add it to the database:
ShopifyOrder order = await db.ShopifyOrders.SingleOrDefaultAsync(x => x.OrderNumber.ToString() == inv.OrderNumber);
if (order != null) {
// Attach marketplace entity to the invoice to avoid duplicate primary key exceptions
db.Marketplaces.Attach(inv.Marketplace);
db.Invoices.Add(inv);
order.InvoiceStatus = OrderInvoiceStatus.InProgress;
}
I've tried a number of methods to try and attach the states, however they all throw errors.
inv.LineItems.ForEach(li => {
db.Entry(li).State = EntityState.Unchanged;
db.Entry(li.ShopifyOrderItem).State = EntityState.Unchanged;
db.Entry(li.ShopifyOrderItem.ShopifyOrder).State = EntityState.Modified;
});
The above code returns the following error on save:
EntityFramework: Saving or accepting changes failed because more than one entity of type 'TorroModels.ShopifyOrder' have the same primary key value. Ensure that explicitly set primary key values are unique. Ensure that database-generated primary keys are configured correctly in the database and in the Entity Framework model.
What is the best way to attach the LineItems/ShopifyOrderItems without trying to attach the ShopifyOrder connected property multiple times?
Sorry to say but it seems that you need to follow the best practice first when constructing a relationship. You may follow this link :
http://www.entityframeworktutorial.net/entity-relationships.aspx
In short :
Avoid using only "Id" in every entity, or you can use attributes to map between the physical name and the property name
It seems that you have circular references here, so maybe you could simplify it first
Next, you can read this link :
http://www.entityframeworktutorial.net/EntityFramework5/attach-disconnected-entity-graph.aspx
if you need to know more about what's the best practice of attaching entities, but in my opinion, just don't abuse this feature, because using normal CRUD should be sufficient most of the time.
I'm sorry I cannot help you more than this, because of lack of information I may need, and with my reputation I still cannot comment directly in your post to ask for it.
So I have an entity EmployeeRegion
EmployeeRegion is a weak entity with a composite key from the tables Region and Employee.
I have an employee profile where they can add themselves to a region. They get a drop down with the regions
I'm doing a pass where I get everything in the model compliant with the data validation stuff that's built in to Asp.net MVC. This is great because the validation (using annotations) shows really great error messages to the end user.
How, using annotations, can I validate that a composite key is unique, and if not, show an error message?
Basically, you just need:
public class EmployeeRegion
{
[Key, Column(Order = 1)]
public int EmployeeId { get; set; }
public virtual Employee Employee { get; set; }
[Key, Column(Order = 2)]
public int RegionId { get; set; }
public virtual Region Region { get; set; }
}
In other words, the part you're missing is [Key, Column(Order = N)]. This makes the ids an actual composite key, which out of the box won't allow duplicates.
However, all this does is make the API more difficult for working with this M2M relationship. If the only data in the table is the keys and there's no payload of additional data needed for the relationship, then you should get rid of this entity and simply let EF handle the relationship:
public class Employee
{
...
public virtual ICollection<Region> Regions { get; set; }
}
public class Region
{
...
public virtual ICollection<Employee> Employees { get; set; }
}
Behind the scenes, EF will create a table similar to what you have with EmployeeRegion, but you won't be responsible for managing that, and things like ensuring unique relationships will be baked in. This also buys you a much easier API to work with. For example. To get all the employees for a region, currently, you'd have to do something like:
dbo.EmployeeRegions.Where(m => m.Region.Id == regionId).Select(m => m.Employee)
Whereas, by allowing EF to handle it, you can just do:
region.Employees
I wonder, if there is any way ,
to use Database-first approach with manually generated classes (models) in advance(just like Code-first approach),
but without using auto-generated code which Entity Framework creates using Database-first approach?
I have 3 Classes(first two of them Student and Courses have many to many relationship), which represents models:
First one is Student:
public class Student
{
public int StudentID { get; set;}
public string Name { get; set;}
public DateTime BirthDate { get; set;}
public ICollection<StudentToCourse> StudentToCourses { get; set; }
public Student()
{
StudentToCourses = new List<StudentToCourse>();
}
}
Then Course:
public class Course
{
public int CourseID { get; set; }
public string CourseName { get; set; }
public ICollection<StudentToCourse> StudentToCourses { get; set; }
public Course()
{
StudentToCourses = new List<StudentToCourse>();
}
}
And Relation/Intermediate Class with additional properties StudentToCourse:
public class StudentToCourse
{
[Key, Column(Order = 0)]
public int StudentID { get; set; }
[Key, Column(Order = 1)]
public int CourseID { get; set; }
[Key, Column(Order = 2)]
public DateTime Date { get; set; }
public virtual Student Student { get; set; }
public virtual Course Course { get; set; }
//public ICollection<Student> Students { get; set; }
//public ICollection<Course> Courses { get; set; }
public int Grade { get; set; }
}
Also, i created Database, using LocalDb feature in VS 2013
I have 3 Tables:
Courses:
CREATE TABLE [dbo].[Courses]
(
[CourseID] INT NOT NULL PRIMARY KEY IDENTITY,
[CourseName] NVARCHAR(100) NOT NULL,
)
Students:
CREATE TABLE [dbo].[Students]
(
[StudentID] INT NOT NULL PRIMARY KEY IDENTITY,
[Name] NVARCHAR(50) NOT NULL,
[BirthDate] DATETIME NOT NULL,
)
Relation Table StudentsToCourses:
CREATE TABLE [dbo].[StudentsToCourses]
(
[StudentID] INT REFERENCES Students(StudentID) NOT NULL,
[CourseID] INT REFERENCES Courses(CourseID) NOT NULL,
[Date] DATETIME NOT NULL,
[Grade] INT NOT NULL,
PRIMARY KEY (StudentID, CourseID, Date)
)
Unfortunately, i have no luck with this approach, i do get students' data but i don't receive data from relational table and i can't receive all related grades per student.
I searched for related topics in google and in stackoverflow , but all those topics weren't helpful for me, although the example above i found in this topic.
As I suspected, the problem is not whether or not you can have a database and a class model independently. Of course you can! All these generation tools and migration stuff only serve one goal: making life easier, help you keeping both models in sync. But you can do that job yourself just as well. The end result is always: two models that – at runtime – don't interact with each other whatsoever. (Don't interact? No, not as such. There must be a middleman, an ORM, to connect both worlds.)
The reason why you don't get data is because lazy loading does not occur. Your statement is
var listOfGrades = _context.Students.Where(s => s.Name.StartsWith("J"))
.FirstOrDefault().StudentToCourses;
This requires lazy loading, because the FirstOrDefault() statement executes the first part of the query. It renders a Student of which subsequently the StudentToCourses are accessed. But these don't load because the collection is not virtual. It should be
public virtual ICollection<StudentToCourse> StudentToCourses { get; set; }
This enables EF to override the collection in a dynamic proxy object that is capable of lazy loading.
But of course is is more efficient to get the collection in one statement, for example:
var listOfGrades = _context.Students.Include(s => s.StudentToCourses)
.Where(s => s.Name.StartsWith("J"))
.FirstOrDefault().StudentToCourses;
Yes, you can. You just need a context with no initialization strategy (so it doesn't try to create or migrate your existing database):
public class ExistingDatabaseContext : DbContext
{
public ExistingDatabaseContext()
: base("ExistingDatabaseConnectionStringName")
{
Database.SetInitializer<ExistingDatabaseContext>(null);
}
// DbSets here for your "code-first" classes that represent existing database tables
}
Just bear in mind that this context will not be capable of doing migrations or any other form of initialization, so if you have actual true code-first tables in there as well, you'll need a separate context to manage those.
I want to add an Entity Framework layer to an existing application with an existing database schema (no way to change this).
I want to define a simple one-to-many relationship with the difference, that the foreign key and type is different in table a from b.
Class/Table A (Event)
[Table("Events")]
public class Event
{
public Event()
{
Participants= new List<MemberCollection>();
}
[Key]
public int EventNumber { get; set; }
public Guid? MemberCollectionId{ get; set; }
[ForeignKey("CollectionId")]
public virtual ICollection<MemberCollection> Participants{ get; set; }
}
Class/Table B (MemberCollection)
[Table("MemberCollections")]
public class MemberCollection
{
[Key]
public Guid CollectionId { get; set; }
public int MemberNumber { get; set; }
[StringLength(30)]
public string RoleId { get; set; }
}
The code above leads to the following exception.
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 'CollectionId' on entity 'MemberCollections' does not match the type of property 'EventNumber' on entity 'Event' in the referential constraint 'Event_MemberCollections'.
How can I tell Entity Framework that the mapping is done through MemberCollectionId and not through EventNumber?
Thanks!
If the database already exists, is there any reason you're not using the Database First Mechanism? It will automatically create all entities and relationships for you.
All your classes are created as partials so you can still extend the classes etc in your EF layer.
I always tend to create my tables like this as I've always preferred setting up my database structure outside of EF as I was more experienced with SQL and I can concentrate on optimizing my DB first.
Is there a way in Entity Framework (and I assume it will be with fluent syntax as data annotations are somewhat limited) to model a many-to-many relationship in which both sides are optional (a 0..M to 0..N relationship)? The use case is this: I would like to allow users to add tags to entities. Tags to entities is a M:N relationship, but neither should be required. That is, a tag can exist that is not applied to any entities and an entity can be untagged. This seems fairly reasonable to me. I can't simply model this using:
public virtual ICollection<Tag> Tags { get; set; }
and
public virtual ICollection<Entity> Entities { get; set; }
because each class has other relationships, and I get a "foreign key constraint may cause cycles or multiple cascade paths." I was hoping maybe I could do something like:
modelBuilder.Entity<Tag>().HasOptional(t => t.Entities);
modelBuilder.Entity<Entity>().HasOptional(t => t.Tags);
but I am warned that EF is "Unable to determine the principal end of the association." From reading, it seems that such relationships HAVE to have a principal end, but in my case, that's undesirable.
I could add a class to represent the bridge table and handle the mapping manually, but I'd prefer not to clutter the code. I was wondering if there is another way to model this in EF.
To fill in a bit more detail, there is also an Author class (which amounts to Users). Authors and tags are 1:M and Authors to Entities are also 1:M. So of course, the problem is that the Entities class occurs twice in the cascade tree. Making the Tag/Entity relationship optional would fix this. I could also fix it if there was a way to get to Tags through Entities, but since Tags can exist without being connected to an entity, I figured that would be impossible.
Here's a summary of the related code:
public class Author
{
public Guid Id { get; set; }
public virtual List<Entity> Entities { get; set; }
public virtual List<Tag> Tags { get; set; }
}
public class Tag
{
public Guid Id { get; set; }
public Guid AuthorId { get; set; }
public virtual Author Author { get; set; }
public virtual ICollection<Entity> Entities { get; set; }
}
public class Entity
{
public Guid Id { get; set; }
public Guid AuthorId { get; set; }
public virtual Author Author { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
EDIT:
Using .HasMany().WithMany() as suggested below gives me this:
CREATE TABLE [dbo].[TagEntities] (
[Tag_Id] [uniqueidentifier] NOT NULL,
[Entity_Id] [uniqueidentifier] NOT NULL,
CONSTRAINT [PK_dbo.TagEntities] PRIMARY KEY ([Tag_Id], [Entity_Id])
)
but what I WANT is for Tag_Id and Entity_Id to be nullable on this table. Maybe this model doesn't make as much sense as I thought?? Can you have a bridge table where both sides are nullable?
Use
modelBuilder.Entity<Tag>().HasMany(t => t.Entities)
.WithMany(t => t.Tags);
Instead of
modelBuilder.Entity<Tag>().HasOptional(t => t.Entities);
modelBuilder.Entity<Entity>().HasOptional(t => t.Tags);
I don't know if this is the RIGHT answer, but I solved this by creating a base class called DbEntity that other classes inherited from. So now Author has just:
// Both entities and tags are part of this collection
public virtual List<DbEntity> Entities { get; set; }
Both "Entities" (which has special meaning in my code) and "Tags" subclass DbEntity. This eliminated the multiple cascade paths while preserving the navigation properties, although I do need to do this:
author.Entities.OfType<Tag>();
or
author.Entities.OfType<Entity>();
to get specific sets of entities.