I am trying to create a DbQuery of ExtendedStudent, from SQL View which was constructed form 2 diffrent tables (see code SQL below).
I have looked at the following posts:
Entity Framework Core Query Types And EF Core 2.1 Query Types
Both have used a model with a navigation propery in it, and then succeeded to fetch it from the Fluent Fluent API.
But When i tried to do so too, i got exception such as "Invalid column name 'PrefixId1'
The models I use are:
public class ExtendedStudent {
public int IdNumber {get; set;}
public string FirstName {get; set;}
public string LastName {get; set;}
public virtual Prefix Prefix {get; set;}
public int Score {get; set;}
}
public class Prefix {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Id {get ;set;}
[required]
public string Name {get; set;}
}
The applicationDbContext.cs file is:
public class ApplciationDbContext : DbContext{
DbSet<Prefix> Prefixes {get; set;}
DbQuery<ExtendedStudent> ExtendedStudents {get ;set;}
...
protected override void OnModelCreating(ModelBuilder builder) {
builder.Query<ExtendedStudent>.ToView("ExtendedStudent");
builder.Query<ExtendedStudent>.HasOne<Prefix>().WithMany();
}
}
At last, I tried to fetch the data like this.
var students = applciationDbContext.ExtendedStudents.Include(v => v.Prefix).ToList();
I have created the ExtendedStudents view in SQL like this:
CREATE VIEW [Organization].[ExtendedStudent] AS
SELECT [TableA].[Student].[FirstName]
,[TableA].[Student].[LastName]
,[TableA].[Student].[PrefixId]
,[TableA].[Student].[IdNumber]
,[Evaluation].[Student].[Score]
FROM [TableA].[Student] AS [Students]
INNER JOIN [Evaluation].[Student] ON [Evaluation].[Student].StudentId = [TableA].[Student].[IdNumber]
I have tried to add a PrefixId property to ExtendedStudent, or add foreign key, But nothing have worked.
I got an error saying
"An exception of type 'System.Data.SqlClient.SqlException' occured in Microsoft.EntityFrameworkCore.dll but was not handled in user code: 'Invalid column name 'PrefixId1'.'
Here
builder.Query<ExtendedStudent>.HasOne<Prefix>().WithMany();
with .HasOne<Prefix>() you are telling EF Core to create many to one relationship without navigation property at each end.
But the navigation property ExtendedStudent.Prefix already implies relationship, hence EF Core assumes a second relationship with default FK property and column name PrefixId1 (because PrefixId is already used by the "other" relationship implied from the navigation property).
To fix that, pass the navigation property to the relationship configuration:
builder.Query<ExtendedStudent>.HasOne(e => e.Prefix).WithMany();
Related
Trying to do something that I believe should be simple.
Customer
------
CustId
BillingId
ShippingId
...other customerInfo
Address
------
Id
...other addressinfo
I have the corresponding POCOs
public class Customer {
[Key]
public int CustId {get;set;}
[Column("BillingId")]
[ForeignKey("BillingAddress")
public int? BillingId {get;set;}
public virtual Address BillingAddress{get;set;}
[Column("ShippingId")]
[ForeignKey("ShippingAddress")
public int? ShippingId {get;set;}
public virtual Address ShippingAddress{get;set;}
...others...
}
public class Address {
[Key]
public int AddressId{get;set}
... others...
}
The BillingId and ShippingId are nullable because the customer may or may not have set an address yet. I'd assume with EF that if the values are null, the ShippingAddress and BillingAddress values should also be null. When I take a look at the object I'm getting back when running the application, all of the data on my Customer object is set but on the ShippingAddress/BillingAddress fields, in debug mode when I inspect the object, I get the below error:
BillingAddress = '((System.Data.Entity.DynamicProxies.CustomerD_E865AA67CAAA399D4F193A2439775301DFD2F05115619BC048699B20BF6F7B11)details).BillingAddress'
threw an exception of type 'System.InvalidOperationException'
A same error appears for the ShippingAddress field. The application actually continues to run, the exception only gets thrown when inspecting in debug more. For the particular inspected object, the ShippingId and BillingId are populated correctly.
Not sure why this is happening given my setup.
one possible reason is: your repository is registered as singleton in DI configure.
another possible reason: add ForeignKey and InverseProperty to navigation properties
public class Customer {
[Key]
public int CustId {get;set;}
//[Column("BillingId")]//not necessary if real column is same as property
public int? BillingId {get;set;}
[ForeignKey("BillingId")]
[InverseProperty("Customer")]
public Address BillingAddress{get;set;} //no need to be virtual
//[Column("ShippingId")]
public int? ShippingId {get;set;}
[ForeignKey("ShippingId")]
[InverseProperty("Customer")]//InverseProperty: Specifies the inverse of a navigation property that represents the other end of the same relationship.
public Address ShippingAddress{get;set;} //no need to be virtual
...others...
}
try Scaffold-DbContext in a new project to validate your pocos:
https://learn.microsoft.com/en-us/ef/core/miscellaneous/cli/powershell#scaffold-dbcontext
Scaffold-DbContext "your connection string" -DataAnnotations -OutputDir "Models" -Force
I have an existing entity User. Now I am trying to create a new entity Contact with 0-1 relation with User.
class Contact
{
public int Id {get; set;}
public string Name {get; set;}
public int? UserId {get; set;}
public virtual User TheUser{get; set;}
}
All suggestion involve about something like this:
modelBuilder.Entity<User>()
.HasOptional(t => t.TheUser)
.WithOptionalDependent(u => u.TheConatct);
But this means we have to add TheConatct property to the existed User entity. Actually I do not want to make any modification to the existed entity. All what I need to define a foreign key form Contact to User entity and can access the User entity from Contact via TheUser property.
Update:
If I use ForeignKey attributes to annotate the property:
class Contact
{
public int Id {get; set;}
public string Name {get; set;}
public int? UserId {get; set;}
[ForeignKey("UserId")]
public virtual User TheUser{get; set;}
}
Then, the result of ObjectContext.CreateDatabase() will also include create statements for already existed tables (depending on the entities that have relations with User).
Of course we are talking about "Entity Framework 6 Code First", Also, I have the same problem with 1-1 relation.
The idea, I cannot alter the existing entity User to add additional property for the new entity Contact
I wonder if there is a way to overcome this issue
Just use another overload:
modelBuilder.Entity<User>()
.HasOptional(t => t.TheUser)
.WithOptionalDependent();
I don't know which version of Entity Framework you are using and I assume you are using Code First, but you may have to consider using a one to many relationship instead of a 0-1.
I don't believe there is support for 0-1 in the way you want it, but you can simulate by having a one to many (even though your "many" will only ever by 1)
I have a database and I want to know how to map the relationships via code. I'm not sure if I understand exactly how this works.
Suppose I have two classes:
public class Address
{
[Key]
public int AddressID {get;set;}
public String Street {get;set;}
}
public class Shipment
{
[Key]
public int ShipmentID {get;set;}
public int ShipToAddressID {get;set;}
public virtual Address ShipToAddress {get;set;}
}
I have a few questions:
Does the navigation property merely give me access to the dbset of Address?
It seems that is not the case. However, if not, how do I specify which property is the foreign key on which the relationship exists? eg: How do I tell this navigation property that it should match the Address entities based on the AddressID property ?
Again, I'm doing this all via code. So I'm mapping the properties in the OnModelCreating call in the context. So please make suggestions/provide answers with that in mind.
You are in need of the HasRequired, WithMany, and HasForeignKey configuration methods.
EntityTypeConfiguration<Shipment> config = modelBuilder.Entity<Shipment>();
config
.HasRequired(s=>s.ShipToAddress)
.WithMany()
.HasForeignKey(s=>s.ShipToAddressID)
.WillCascadeOnDelete(false);
I have a class called Person
public class Contact
{
public int ID{get;set;}
public string Name{get; set;}
public Contact Secretary{get; set;}
public Contact Assistant{get; set;}
}
When I try to do data migrations I get the following error.
Unable to determine the principal end of an association between the types
'MyApp.Contact' and 'MyApp.Contact'. The principal end of this association must be
explicitly configured using either the relationship fluent API or data annotations.
If I remove one of the two "Contact" types the error goes away. How do I have two contact types in the class? I would like to work thru this error using Data Annotations if possible. But am not sure where to start with this one.
Here's an example of some configuration that would work for you, I think. You might want to explicitly specify the foreign key names as they default to using the type of the entity rather than the name of the navigation property.
public class MyContext : DbContext
{
public DbSet<Contact> Contacts { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var contactConfiguration = new EntityTypeConfiguration<Contact>();
contactConfiguration.HasOptional(c => c.Secretary).WithOptionalPrincipal();
contactConfiguration.HasOptional(c => c.Assistant).WithOptionalPrincipal();
modelBuilder.Configurations.Add(contactConfiguration);
}
}
My model looks like this
public Class Address
{
public int Id {get;set;}
/*Props here*/
}
public Class Person
{
public int Id {get;set;}
public String Name {get;set;}
[Required]
public Address Address{get;set;}
/*More props*/
}
Now suppose i have created a person with proper address, in future when i try to update person like this
var person= db.Persons.FirstOrDefault(p=>p.Id=1234);
person.Name="Foo";
db.SaveChanges();
It gives error saying Address is required.
So to avoid this iam including Address property too while loading Person Entity
var person= db.Persons.Include(p=>p.Address).FirstOrDefault(p=>p.Id=1234);
person.Name="Foo";
db.SaveChanges();
Is there any way i can update person Without including Address.
It's the model validation of DbContext which complains apparently. So, one solution would be to switch off this validation:
dbContext.Configuration.ValidateOnSaveEnabled = false;
The other option is to introduce a foreign key property:
public class Person
{
public int Id {get;set;}
public String Name {get;set;}
public int AddressId {get;set;}
public Address Address {get;set;}
/*More props*/
}
You can omit the [Required] attribute here because EF will detect the relationship as required by convention (due to the non-nullable FK property). This works also with enabled validation.
The behaviour is a bit confusing since EF doesn't send a change of the FK column to the database, so there is not really a constraint violation and the Update command executes fine. I guess that the validation just checks the state of the model in memory (invalid, because Address is null) and not the state the model would have in the database when SaveChanges did execute (valid, because FK is correctly set).
If you want the address to be automatically loaded by EF 4.1 you have to make the Address-porperty virtual:
public virtual Address Address{get;set;}
EF will then lazy-load the address when needed.