(Since i have a database predefined i can't let EF recreate it).
This the mapping i use now (works but i want to rewrite using fluent api):
public class League
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid LeagueId { get; set; }
....
#region References
public virtual ICollection<News> News { get; set; }
#endregion
}
public class News
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid NewsId { get; set; }
public Guid LeagueId { get; set; }
....
#region References
[ForeignKey("LeagueId")]
public virtual League League { get; set; }
#endregion
}
Now, how can i map this using the fluent API?
Update
Wrote this and it works, but is there a simpler version?
modelBuilder.Entity<League>().HasMany(x => x.News).WithRequired(y => y.League).HasForeignKey(c => c.LeagueId);
Update 2
I added those missing from the classes. But the problem is that if i leave it at that and try it, it fails. I need to specify a key somewhere.I can't let EF create the database and it refuses to just operate with tables.
You should look at this article for example: http://www.codeproject.com/Articles/184133/Using-Entity-Framework-4-1-Code-First-with-an-exis
The base part, is that you should remove default db initializer:
Database.SetInitializer<YourContext>(null);
And this should prevent EF from trying to change your db or throw errors. It will just try to work with what you give it.
Related
A project using Entity Framework Core 2.2 contains a pair of entities with a many-to-many relationship.
The entities are as follows:
class Feature
{
int Id { get; set;}
}
class House
{
int Id { get; set; }
ICollection<int> FeatureIds { get; set; }
}
As EF Core does not support many-to-many relationship natively yet, I have created a join class.
class HouseFeature
{
int FeatureId { get; set; }
int HouseId { get; set; }
}
None of the three classes contains a property for the other entity, only its id. The Feature entity neither contains a list of houses.
The configuration class for HouseFeature looks like:
class HouseFeatureConfiguration : IEntityTypeConfiguration<HouseFeature>
{
void Configure(EntityTypeBuilder<HouseFeature> houseFeatureConfiguration)
{
}
}
How to configure the EF Core relationship?
All examples I found require properties like Feature Feature { get; set; } on top of int FeatureId { get; set; }. I have no use for these Feature Feature { get; set; } so I prefer to not have them in the model. Is it possible to define the relationship including foreign keys while having id-fields only? How?
Use the ModelBuilder. Here's an example where it is manually configured:
https://www.learnentityframeworkcore.com/configuration/many-to-many-relationship-configuration
I don't thinks you can create a mapping without exposing a collection of link class in your model. However, you can make it private. Also, a mapping to the ICollection<int> will not work (and even if it would, it would mean a mapping without the collection of link objects).
Perhaps you could try something like this:
public class House
{
[Key]
public int Id { get; set; }
private IEnumerable<HouseFeature> HouseFeatures { get; set; }
public ICollection<int> FeatureIds
{
get { return HouseFeatures.Select((hf) => hf.FeatureId).ToList(); }
}
}
class HouseFeatureConfiguration : IEntityTypeConfiguration<HouseFeature>
{
public void Configure(EntityTypeBuilder<HouseFeature> builder)
{
builder.HasKey(nameof(HouseFeature.FeatureId), nameof(HouseFeature.HouseId));
builder
.HasOne<House>()
.WithMany("HouseFeatures") // can't use nameof or expression because it's private member of the House
.HasForeignKey(nameof(HouseFeature.HouseId));
builder
.HasOne<Feature>()
.WithMany()
.HasForeignKey(nameof(HouseFeature.FeatureId));
}
}
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.
I recently delete a column ConversationId from my tables. When I start to debug my service and try to save I am getting an error:
Invalid column name 'ConversationId'.
Code:
public class AstootContext : DbContext
{
public AstootContext(DbContextOptions<AstootContext> options)
: base(options)
{ }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
}
public DbSet<ServiceRequest> ServiceRequests { get; set; }
}
And my entity looks like this:
public class ServiceRequest
{
public int Id { get; set; }
public int SenderUserId { get; set; }
public int PriceTypeId { get; set; }
public decimal Price { get; set; }
public bool IsAccepted { get; set; }
public DateTime Created { get; set; }
public int MessageId { get; set; }
}
All references to ConversationId were removed from the code, I've rebuilt, yet I'm still getting this error and I don't understand why.
This is my SQL Server table as you can see there is no ConversationId:
Is there a secret cache that I need to delete or something I have to run to update this?
EF Core is code based ORM, with the most important here being the M - Mapper. It doesn't matter what the actual database structure is, the important is what EF *thinks** it is based on your code model (entity classes and their properties, combined with data annotations, fluent configuration and set of conventions).
So the problem should originate from code. Since you've removed the explicit property, it should be caused by shadow property. And as explained in the documentation link, shadow properties are usually introduced by convention from relationships:
Shadow properties can be created by convention when a relationship is discovered but no foreign key property is found in the dependent entity class. In this case, a shadow foreign key property will be introduced.
The documentation also explains the naming rules applied in different scenarios.
A shadow property called ConversationId can be introduced in a several ways, but according to the provided information, the most likely cause is to have an entity class called Conversation defining one-to-many relationship with ServiceRequest by having a collection type navigation property:
public class Conversation
{
public int Id { get; set; }
// ...
public ICollection<ServiceRequest> ServiceRequests { get; set; }
}
Which according to your comment was indeed the case.
For completeness, here are some other possible scenarios generating such property:
(1) No collection navigation property in Conversation, reference navigation property in ServiceRequest:
public class Conversation
{
public int Id { get; set; }
// ...
}
public class ServiceRequest
{
// ...
public Conversation Conversation { get; set; }
}
(2) No navigation properties in Conversation and ServiceRequest, fluent configuration:
modelBuilder.Entity<Conversation>()
.HasMany<ServiceRequest>();
or
modelBuilder.Entity<ServiceRequest>()
.HasOne<Conversation>();
or variations of the above.
(3) No relationship involved, shadow property created through fluent configuration:
modelBuilder.Entity<ServiceRequest>()
.Property<int>("ConversationId");
First off, I'm new to the Entity Framework and am migrating an existing project from a database framework that I wrote myself so I have a fair amount of flexibility in the solution I choose.
From what I've researched so far everything appears to be set up correctly. However, when my database is constructed, the table for a helper class I wrote has no columns in it (outside of its primary key). The most simplified version of the classes are included below with their relationships defined in the fluent API.
Classes
public class Concept
{
public long ID { get; set; }
[Index(IsUnique = true), MaxLength(255)]
public string Name { get; set; }
}
public class Tag
{
public long ID { get; set; }
public virtual Content Subject { get; set; }
public virtual Concept Concept { get; set; }
}
public class Helper
{
public long ID { get; set; }
public virtual Content Subject { get; set; }
public virtual List<Tag> Instances { get; set; }
// Helper functionality
}
public class Content
{
public long ID { get; set; }
public virtual Helper Helper { get; set; }
public Content() { Helper = new Helper() { Subject = this }; }
}
Context
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Tag>()
.HasRequired(t => t.Concept);
modelBuilder.Entity<Tag>()
.HasRequired(t => t.Subject);
modelBuilder.Entity<Helper>()
.HasRequired(t => t.Subject)
.WithRequiredDependent(c => c.Helper);
modelBuilder.Entity<Helper>()
.HasMany(t => t.Instances);
modelBuilder.Entity<Content>()
.HasRequired(c => c.Helper)
.WithRequiredPrincipal();
base.OnModelCreating(modelBuilder);
}
Program.cs
static void Main(string[] args)
{
Content content = null;
using (var context = new Context())
{
content = context.Content.Find(1);
if (content == null)
{
content = new Content();
context.Content.Add(content);
context.Helper.Add(content.Helper);
context.SaveChanges();
}
}
}
It's also worth mentioning that when the data is saved, the Helper is assigned an ID but on loading the parent class (Content) the second time around, the Helper is not lazy loaded as I would expect from the 'virtual' keyword. I suspect that this is caused by the same issue causing the absence of data in the table.
I have tried both the data annotation and fluent API approaches that EF provides but it seems that there is something fundamental that I am misunderstanding. I would like to retain this helper class as it helps organize the code far better.
As I have spent a fair amount of time researching these relationships / APIs, and scouring Google / SO without found anything to solve this issue in particular any help would be greatly appreciated!
Updated: Solution
Thanks to a question in the comments, I realized that I was expecting to see the keys of a many-to-many relationship in the tables for the entity types themselves (i.e. in the Helpers table). However, in a many-to-many relationship, the keys will always be placed in a separate table (concatenation of type names) which was not being previously created.
By adding '.WithMany();' to the Helper section of the OnModelCreating function as below
modelBuilder.Entity<Helper>()
.HasMany(t => t.Instances)
.WithMany();
the many-to-many relationship became properly defined and the HelperTags table generated as expected. This is due to the fact that the many-to-many relationship is one way (Helpers always refer to Tags, Tags never refer to Helpers). This is also why the 'WithMany' does not have any arguments (since no Helper properties exist in the Tag class). Fixing this simple oversight solved the problem!
You are probably working harder than you need to in the on ModelCreate. You should probably redesign your classes use Identifiers, like this:
public class Tag
{
public long Id { get; set; }
public long SubjectId { get; set; }
public long ConceptId { get; set; }
public virtual Content Subject { get; set; }
public virtual Concept Concept { get; set; }
}
You need to keep the ID names the EXACT same as the object names + Id and EF will magically link everything up. If you don't want them required then make the id nullable (C# 6 == long? SubjectId).
Also, I have changed the ID -> Id; I have no idea if this matters. At one point I remember having to do that to get things working (it was YEARS ago) and I have been doing it that way ever since.
Consider reading:
Entity Framework Code First Conventions
relationship Convention
In addition to navigation properties, we recommend that you include
foreign key properties on the types that represent dependent objects.
Any property with the same data type as the principal primary key
property and with a name that follows one of the following formats
represents a foreign key for the relationship:
<navigation property name><principal primary key property name>
<principal class name><primary key property name>
<principal primary key property name>
If multiple matches are found then precedence is given in the order
listed above.
Foreign key detection is not case sensitive.
Sample Code from MSDN:
In the following example the navigation properties and a foreign key are used to define the relationship between the Department and Course classes.
public class Department
{
// Primary key
public int DepartmentID { get; set; }
public string Name { get; set; }
// Navigation property
public virtual ICollection<Course> Courses { get; set; }
}
public class Course
{
// Primary key
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
// Foreign key
public int DepartmentID { get; set; }
// Navigation properties
public virtual Department Department { get; set; }
}
Using MVC EF4.1, I am trying to link a table (TableMaster) to TableChildOne (relationship one-to-zero-or-one) and also to TableChildTwo (also one-to-zero-or-one).
TableChildOne and TableChildTwo are not directly linked.
TablechildOne and TableChildTwo needs to share the primary key of TableMaster (I read this is not possible, any workarounds?)
I am including an image to make this a bit more clear, not sure if there should be foreign keys added somewhere, this is not an actual model created by the code, but is what i would like. not sure if there should be foreign keys somewhere?
image : http://www.davidsmit.za.net/img/untitled.png
My code below compiles, but when trying to add a controller, I get the error :
"An item with the same key has already been added"
public class TableMaster
{
public int TableMasterID { get; set; }
public DateTime ReportDate { get; set; }
public virtual TableChildOne TableChildOne { get; set; }
public virtual TableChildTwo TableChildTwo { get; set; }
}
public class TableChildOne
{
[Key]
public int TableMasterID { get; set; }
public String Description_1 { get; set; }
public virtual TableMaster TableMaster { get; set; }
}
public class TableChildTwo
{
[Key]
public int TableMasterID { get; set; }
public String Description_2 { get; set; }
public virtual TableMaster TableMaster { get; set; }
}
public class Context : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<TableMaster>()
.HasOptional(p => p.TableChildOne).WithRequired(p => p.TableMaster);
modelBuilder.Entity<TableMaster>()
.HasOptional(p => p.TableChildTwo).WithRequired(p => p.TableMaster);
}
When I remove the second table completely, it works fine.
I used the below link as an example (tables OfficeAssignment and Student), which shows how to link a table one-to-zero-or-one. But I have trouble adding another table with the same linkage:
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/creating-a-more-complex-data-model-for-an-asp-net-mvc-application
Any help will be appreciated.
Thanks
appelmeester
Could you give more background about why you want to do this? If you are sharing the primary key across three tables you are partitioning data. What development scenario are you trying to address. It sounds like you might be wanting to map an object inheritance, is that right?
If you truly only have a couple of Descriptions, then this is really just one table.
EDIT:
Cool. Because the business context of this request is a bit vague, I can't quite understand still, sorry. If you have a TableMaster and then some child tables, then this sounds like an inheritance tree. So with EF, you can choose many different strategies to model this (TPH, TPT etc). For this, I would suggest looking into TPT because this might allow you to get the granularity for how you want to clean up the data. Also, you get the benefit that the tables will be created, by default, largely like you have specified. Check this out for reference.