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");
Related
I have the following two classes
public class Tip
{
public string Home { get; set; }
public string Away { get; set; }
public string Prediction { get; set; }
public Tipster Tipster { get; set; }
... other properties
}
public class Tipster
{
public int Id { get; set; }
public string Username { get; set; }
public string Platform { get; set; }
}
Now, I want to make unique index in theTip table. According to the EF Core documentation, there is no Data Annotations syntax, so I am using the fluent one:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Tip>()
.HasIndex(entity => new { entity.Tipster, entity.Home, entity.Away, entity.Prediction })
.HasName("IX_UniqueTip")
.IsUnique();
}
Now, when I update the database I get the following error
C:..>dotnet ef database update System.InvalidOperationException:
Cannot call Property for the property 'Tipster' on entity type 'Tip'
because it is configured as a navigation property. Property can only
be used to configure scalar properties.
It seems that EF didn't liked the fact that I am using referential property in the index. How can I fix that ?
You can't use navigation property in index defining expression. Instead, you should use the corresponding FK property.
The problem in your case is that you don't have explicit FK property in your model Tip. By convention EF Core will create int? TipsterId shadow property. So theoretically you should be able to use EF.Property method to access it:
.HasIndex(e => new { TipsterId = EF.Property<int>(e, "TipsterId"), e.Home, e.Away, e.Prediction })
Unfortunately this doesn't work currently (EF Core 2.0.1). So you have to resort to HasIndex overload with params string[] propertyNames:
.HasIndex("TipsterId", nameof(Tip.Home), nameof(Tip.Away), nameof(Tip.Prediction))
You must define the property TipsterId explicitly cause the Navigation property define it as shadow, so you cannot use it on custom index or alternate key
public class Tip
{
public string Home { get; set; }
public string Away { get; set; }
public string Prediction { get; set; }
public int TipsterId { get; set; }
public Tipster Tipster { get; set; }
... other properties
}
Now you can
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Tip>()
.HasIndex(entity => new { entity.TipsterId, entity.Home, entity.Away, entity.Prediction })
.HasName("IX_UniqueTip")
.IsUnique();
}
They way you defined your entities EF will put the referential column into the tipster table, since it looks like a 1-n relationship. Meaning a tipster can place several tips, but each tip is only placed by a single tipster.
That means on the database level there is nothing to index. No column, no key - nothing.
To fix this you might ask yourself what you really want to achieve with an index in the first place. An index is supposed to make queries using the columns of the index faster and avoid a full table scan.
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; }
}
I have two model
1)
public class Indicator
{
public long ID { get; set; }
public string Name { get; set; }
public int MaxPoint { get; set; }
public string Comment { get; set; }
public DateTime DateChanged { get; set; }
public DateTime DateCreated { get; set; }
public virtual IList<CalculationType> CalculationTypes { get; set; }
public virtual IList<TestEntity> TestEntitys { get; set; }
public virtual IndicatorGroup IndicatorGroup { get; set; }
}
2)
public class CalculationType
{
public long ID { get; set; }
public string UnitName { get; set; }
public int Point { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateChanged { get; set; }
public virtual Indicator Indicator { get; set; }
public virtual IList<Сalculation> Calculations { get; set; }
}
I executing this code
var indicator = DataContext.Indicators.FirstOrDefault(i => i.ID == indicatorID);
var test = DataContext.CalculationTypes.FirstOrDefault();
first line return null on navigation property CalculationTypes
Second line return empty collection. Why?
UPDATE
snapshot database
project link https://github.com/wkololo4ever/Stankin
added Calculation
public class Сalculation
{
public long ID { get; set; }
public virtual CalculationType CalculationType { get; set; }
public virtual ApplicationUser Creator { get; set; }
}
1) Is Lazy Loading enabled? If not, you need to explicitly load your navigation properties with the '.Include' syntax.
2) Are you sure EF should be able to detect that relation? Did you use Code First or Database First?
Edit: 3) Are you sure there is data in your database and that the foreign key from Indicator to IndicatorGroup has a value for that specific record? I am saying this because the value "null" is valid if there is simply no data.
P.S. If you do not see a foreign key on Indicator called "IndicatorGroupId", there might be an "IndicatorId" on the table "IndicatorGroup", in which case - going from the names you provided - your database is misconfigured and you will need to use fluent syntax or data attributes to instruct EF on how to make the foreign keys.
Try this:
DbContext.Configuration.ProxyCreationEnabled = true;
DbContext.Configuration.LazyLoadingEnabled = true;
If DbContext.Configuration.ProxyCreationEnabled is set to false, DbContext will not load child objects for some parent object unless Include method is called on parent object. Setting DbContext.Configuration.LazyLoadingEnabled to true or false will have no impact on its behaviours.
If DbContext.Configuration.ProxyCreationEnabled is set to true, child objects will be loaded automatically, and DbContext.Configuration.LazyLoadingEnabled value will control when child objects are loaded.
I think this is problem:
Edit: 3) Are you sure there is data in your database and that the
foreign key from Indicator to IndicatorGroup has a value for that
specific record? I am saying this because the value "null" is valid if
there is simply no data.
P.S. If you do not see a foreign key on Indicator called
"IndicatorGroupId", there might be an "IndicatorId" on the table
"IndicatorGroup", in which case - going from the names you provided -
your database is misconfigured and you will need to use fluent syntax
or data attributes to instruct EF on how to make the foreign keys.
Try to this and make sure foreign key is corrected.
public class CalculationType
{
public long ID { get; set; }
public string UnitName { get; set; }
public int Point { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateChanged { get; set; }
[ForeignKey("IndicatorID")]
public string IndicatorId { get; set; } //this is the foreign key, i saw in your database is: Indicator_ID, avoid this, rename it to IndicatorID or IndicatorId
public virtual Indicator Indicator { get; set; }
public virtual IList<Сalculation> Calculations { get; set; }
}
Same behavior, but different root cause than selected answer:
Navigation property can also be null if you turned off myContext.Configuration.AutoDetectChangesEnabled
Very obvious, but this got me when I was implementing some performance improvments.
Check this out: Navigation Property With Code First . It mentions about why navigation property is null and the solutions of it.
By default, navigation properties are null, they are not loaded by
default. For loading navigation property, we use “include” method of
IQuearable and this type of loading is called Eager loading.
Eager loading: It is a process by which a query for one type of entity
loads the related entities as a part of query and it is achieved by
“include” method of IQueryable.
I experienced this issue, where navigation properites were not loaded, even when the Include statement was present.
The problem was caused by string-comparison differences between SQL Server and EF6 using .NET. I was using a VARCHAR(50) field as the primary key in my customers table and also, as a foreign key field in my audit_issues table. What I did not realize was that my keys in the customers table had two additional white space characters on the end; these characters were not present in my audit_issues table.
However, SQL Server will automatically pad whitespace for string comparisons. This applies for WHERE and JOIN clauses, as well as for checks on FOREIGN KEY constraints. I.e. the database was telling me string were equivalent and the constraint passed. Therefore I assumed that they actually were exactly equal. But that was false. DATALENGTH of one field = 10, while the DATALENGTH of the other = 8.
EF6 would correctly compose the SQL query to pull the foreign key related fields and I would see them both in the generated Sql query and in the results. However, EF6 would silently fail when loading the Navigation Properties because .NET does not consider those strings equal. Watch out for whitespace in string-type foreign key fields!.
This article helped me.
In sum :
Install-Package Microsoft.EntityFrameworkCore.Proxies
In Startup.cs
using Microsoft.EntityFrameworkCore;
public void ConfigureServices(IServiceCollection services)
{
...
services.AddDbContext<MyDbContext>(builder =>
{
builder.UseLazyLoadingProxies(); // <-- add this
}, ServiceLifetime.Singleton);
This is a variant of Keytrap's answer. Using .NET 6 and EF Core 6, I created a ContextPartials.cs for any custom configurations that I don't want EF's Scaffold command to overwrite:
Required Package:
Install-Package Microsoft.EntityFrameworkCore.Proxies
Code (ContextPartials.cs):
// NOTE: I am not using the new file-scoped namespace on purpose
namespace DataAccess.Models.MyDatabase
{
// NOTE: This is a partial outside of the generated file from Scaffold-DbContext
public partial class MyDatabaseContext
{
// NOTE: This enables foreign key tables to become accessible
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseLazyLoadingProxies();
}
}
I am having some trouble with one to one relationships in EF.
Normally, I would define the key side of a (one to many) relationship, like so:
[ForeignKey("OrderId")]
public virtual Order Order { get; set; }
But when I do this with a one to one relationship, it fails to determine the principal end...
Unable to determine the principal end of an association between the
types 'Retailer.ClientDB.EF.OrderOrigin' and
'Retailer.ClientDB.EF.Order'. The principal end of this
association must be explicitly configured using either the
relationship fluent API or data annotations.
Strangely, I don't get the error if I put the ForeignKeyAttribute on the id column, as I have done here with the relationship between Order and OrderOrigin.
[Table("ORDER_ORIGIN")]
public class OrderOrigin
{
[Key, Column("uidorder"), ForeignKey("Order")]
public int OrderId { get; set; }
[Column("importfilename")]
public string ImportFileName { get; set; }
public virtual Order Order { get; set; }
}
[Table("ORDERS")]
public class Order
{
[Column("uidorder")]
public int Id { get; set; }
[Column("uidstatus")]
public int OrderStatus { get; set; }
public virtual OrderOrigin OrderOrigin { get; set; }
}
But now, when I attempt to include() like this (which would normally work great)...
public List<ClientDB.EF.OrderOrigin> ListOrderOrigin_WithOrder(Common.Data.DBContext context)
{
return context.EFDatabase.OrderOrigin.Include("Order").ToList();
}
...it fails to include Order and errors with the following when I try and access Order (after the context has been disposed of):
The ObjectContext instance has been disposed and can no longer be used
for operations that require a connection.
Can anyone please advise how I can code one to one relationships in order to get this to work?
Thanks
I have two classes defined as such:
public class Questionnaire
{
public int QuestionnaireID { get; set; }
public string Title { get; set; }
public bool Active { get; set; }
public virtual ICollection<Question> Questions { get; set; }
public virtual ICollection<Vendor> Vendors { get; set; }
}
public class Vendor
{
public int VendorID { get; set; }
public string VendorName { get; set; }
public virtual ICollection<Questionnaire> OpenQuestionnaires { get; set; }
public virtual ICollection<Questionnaire> SubmittedQuestionnaires { get; set; }
public virtual ICollection<QuestionnaireUser> QuestionnaireUsers { get; set; }
}
I beleive this is the correct way to establish a many-to-many relationship between these classes, and when the project is built, I would expect three tables to be created.
However, when I attempt to to relate one Questionnaire to two different Vendors, I receive the following error when attempting to save the changes (context.SaveChanges()):
*Multiplicity constraint violated. The role 'Vendor_OpenQuestionnaires_Source' of the relationship 'QuestionnaireApp.Models.Vendor_OpenQuestionnaires' has multiplicity 1 or 0..1.*
If I assign a Questionnaire to only one Vendor, save the changes and then assign it to another and again save changes I no longer get the error; however the Questionaire is then related only to the last Vendor to which it was assigned, indicating that (at best) there is a one-to-many relationship being created.
I'm hoping that there is something wrong with the way I'm declaring the many-to-many relationship between these classes, or perhaps there is something I need to add to the context class to "encourage" the relationsip, but perhaps many-to-many relationships like this are not supported, or cannot be created using "Code First"?
Thank you for your time,
Jason
If you don't have any Fluent API code your expected mapping relies on EF Code First conventions. The convention which you expect to kick in here is the AssociationInverseDiscoveryConvention. Now if you look in Intellisense (and probably also documentation) it says about this convention:
Convention to detect navigation properties to be inverses of each
other when only one pair of navigation properties exists between the
related types.
Now, that's the problem: You don't have only "one pair" of navigation properties between Questionnaire and Vendor. You have two collections in Vendor refering to Questionnaire and one collection in Questionnaire refering to Vendor. The result is that this convention doesn't get applied and EF maps actually three one-to-many relationships with only one end exposed as navigation property in the model.
Moreover the mapping you want to achieve is not possible with your model: You cannot map the one end Questionnaire.Vendors to the two ends Vendor.OpenQuestionnaires and Vendor.SubmittedQuestionnaires.
One workaround is to change your model the following way:
public class Vendor
{
public int VendorID { get; set; }
public string VendorName { get; set; }
[NotMapped]
public IEnumerable<Questionnaire> OpenQuestionnaires
{
get { return Questionnaires.Where(q => q.IsActive); }
}
[NotMapped]
public IEnumerable<Questionnaire> SubmittedQuestionnaires
{
get { return Questionnaires.Where(q => !q.IsActive); }
}
public virtual ICollection<Questionnaire> Questionnaires { get; set; }
public virtual ICollection<QuestionnaireUser> QuestionnaireUsers { get; set; }
}
Now Vendor.Questionnaires is mapped to Questionnaire.Vendors (AssociationInverseDiscoveryConvention should detect this) and the helper properties OpenQuestionnaires and SubmittedQuestionnaires allow you to pull out the selected items. (I'm not sure if IsActive is your distinguishing flag. Otherwise you have to introduce some new flag.)
The [NotMapped] attribute is just here to make it explicite. It is probably not necessary because EF won't map IEnumerable collections and readonly properties with only a getter anyway.
Go figure, after an hour or so of searching, I go and find the exact answer 30 seconds after I post my question.
The solution was to add the following to the context class:
modelBuilder.Entity<Vendor>()
.HasMany<Questionnaire>(x => x.OpenQuestionnaires)
.WithMany(x => x.Vendors)
.Map(x =>
{
x.MapLeftKey("vID");
x.MapRightKey("qID");
x.ToTable("VendorQuestionnaires");
});
I found the answer by reading this Stack Overflow post: EF Code First Many-to-Many not working