Entity Framework Navigation Property Error - entity-framework

I am getting this error in my .Net MVC 4 web application:
The property 'Username' cannot be configured as a navigation property. The
property must be a valid entity type and the property should have a non-abstract
getter and setter. For collection properties the type must implement
ICollection<T> where T is a valid entity type.
I am very new to Entity Framework and I can't seem to get around this issue. Here is some code:
//DB Context
public class EFDbContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Role> Roles { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasMany(u => u.Roles).WithMany(r => r.Users).Map(x => x.MapLeftKey("Username").MapRightKey("RoleName").ToTable("Users_Roles"));
}
}
//Entity Classes
public class User
{
[Key]
public string Username { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public string Comment { get; set; }
public int Level { get; set; }
public string PasswordQuestion { get; set; }
public string PasswordAnswer { get; set; }
public bool IsApproved { get; set; }
public DateTime LastActivityDate { get; set; }
public DateTime LastLoginDate { get; set; }
public DateTime LastPasswordChangedDate { get; set; }
public DateTime CreationDate { get; set; }
public bool IsOnLine { get; set; }
public bool IsLockedOut { get; set; }
public DateTime LastLockedOutDate { get; set; }
public int FailedPasswordAttemptCount { get; set; }
public DateTime FailedPasswordAttemptWindowStart { get; set; }
public int FailedPasswordAnswerAttemptCount { get; set; }
public DateTime FailedPasswordAnswerAttemptWindowStart { get; set; }
[InverseProperty("RoleName")]
public virtual ICollection<Role> Roles { get; set; }
public override string ToString()
{
return this.Username;
}
}
public class Role
{
[Key]
public string RoleName { get; set; }
public int Level { get; set; }
[InverseProperty("Username")]
public virtual ICollection<User> Users { get; set; }
public override string ToString()
{
return this.RoleName;
}
}
//Repository
public class EFUsersRepository : IUsersRepository
{
private EFDbContext context = new EFDbContext();
public IQueryable<User> Users
{
get { return context.Users; }
}
public User GetUser(string username)
{
return context.Users.Find(username); //THIS IS WHERE THE CRASH OCCURS
}
}
//DB Setup
Table Users, Role and Users_Role. Users_Role is a simple linking table with [username, role] columns both of type varchar.
The database tables columns & types match the two classes above (User,Role).
I inherited this project which was unfinished but I can't get it to run successfully. Any help understanding what the issue is would be helpful. Thanks!

It might be that Entity Framework is updated. Easiest way will be to recreate the DataModel.
Even if the previous programmer did not use Entity Data Mode, you can at least copy the auto generated code such as EFDbContext, Users and Roles classes.

It turns out, after commenting out enough items all day long, the the following lines are what caused this error for me:
[InverseProperty("RoleName")] //In file User.cs (as shown above)
[InverseProperty("UserName")] //in file Role.cs (as shown above)
I am still learning Entity Framework and I don't know why this was the solution, but it stopped the error which I reported above.
I hope that this helps someone else and if anyone wants to help me understand what the issue was in detail, please feel free. I am eager to learn.

Related

LINQ query throw exception on FirstOrDefault method

I'm using EF core, and I have a many-to-many relationship between two entity
IotaProject <--> User
Here's entities & dto related to the question
public class IotaProject
{
[Key]
public int Id { get; set; }
[Required]
public string ProjectName { get; set; }
[Required]
public DateTime Create { get; set; }
public ICollection<ProjectOwnerJoint> Owners { get; set; } = new List<ProjectOwnerJoint>();
}
public class ProjectOwnerJoint
{
public int IotaProjectId { get; set; }
public IotaProject IotaProject { get; set; }
public int UserId { get; set; }
public User User { get; set; }
}
public class User
{
[Key]
public int Id { get; set; }
[Required]
public string FullName { get; set; }
[Required]
public string ShortName { get; set; }
[Required]
public string Email { get; set; }
public ICollection<ProjectOwnerJoint> OwnedProjects { get; set; } = new List<ProjectOwnerJoint>();
}
public class ApplicationDbContext : DbContext
{
public DbSet<IotaProject> IotaProjects { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<ProjectOwnerJoint> ProjectOwnerJoint { get; set; }
}
public class IotaProjectDisplayDto
{
public int Id { get; set; }
public string ProjectName { get; set; }
public DateTime Create { get; set; }
public UserMinDto Owner { get; set; }
public int Count { get; set; }
public IEnumerable<UserMinDto> Reviewers { get; set; }
}
public class UserMinDto
{
public int Id { get; set; }
public string FullName { get; set; }
public string ShortName { get; set; }
}
Following LINQ is the problem, the LINQ purpose is to convert IotaProject to IotaProjectDisplayDto, and key part is that Owners property of IotaProject is ICollection and Owner property in IotaProjectDisplayDto is just one single element UserMinDto, so I only need to get the first element of IotaProject's Owners and that's FirstOrDefault() comes.
IEnumerable<IotaProjectDisplayDto> results = _db.IotaProjects.Select(x => new IotaProjectDisplayDto
{
Id = x.Id,
ProjectName = x.ProjectName,
Create = x.Create,
Owner = x.Owners.Select(y => y.User).Select(z => new UserMinDto { Id = z.Id, FullName = z.FullName, ShortName = z.ShortName }).FirstOrDefault()
});
return results;
it throws run-time exception
Expression of type 'System.Collections.Generic.List`1[ToolHub.Shared.iota.UserMinDto]' cannot be used for parameter
of type 'System.Linq.IQueryable`1[ToolHub.Shared.iota.UserMinDto]'
of method 'ToolHub.Shared.iota.UserMinDto FirstOrDefault[UserMinDto](System.Linq.IQueryable`1[ToolHub.Shared.iota.UserMinDto])' (Parameter 'arg0')
I'm guessing it's probably related to deferred execution, but after read some posts, I still can't resolve it.
Any tips would be appreciated.
Right now, the only way I can get this work is I change type of Owner property in IotaProjectDisplayDto into IEnumrable, which will no longer need FirstOrDefault() to immediate execution. And later on, I manually get the first element in the client to display.
This issue happened in Microsoft.EntityFrameworkCore.SqlServer 3.0.0-preview7.19362.6
I end up downgrade to EF core stable 2.2.6 as Ivan suggested in comment, and everything works fine.

The entity type 'Program' requires a primary key to be defined

I am trying to make a simple website that tracks students, programs, and classes. I've created the entities and I'm getting an error when trying to add the migration.
"The entity type 'Program' requires a primary key to be defined."
I have tried using the [Key] attribute and there is an Id field. The other table was created just fine. What else should I try?
Here is the problem class:
public class Program
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool UseRanks { get; set; }
}
Here is another table that I had no problems creating a migration for:
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string CellPhone { get; set; }
public string HomePhone { get; set; }
public string WorkPhone { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
public DateTime BirthDate { get; set; }
}
Here is what is in my ApplicationDbContext class:
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
//public DbSet<Attendance> Attendances { get; set; }
public DbSet<Person> People { get; set; }
public DbSet<Bill> Bills { get; set; }
//public DbSet<Session> Sessions { get; set; }
public DbSet<Program> Programs { get; set; }
}
I've commented out the other entities because I was trying to add them one at a time. Trying to add a migration with all the entities resulted in the same error with the same specific class.
Complete shot in the dark, but based on the name of this class, I'm guessing you're referencing the wrong Program. Make sure that your DbSet<Program> is actually using your Program entity and not something like the Program class used at the console app level. You'll likely need to explicitly use the namespace, i.e. DbSet<MyApp.Models.Program>.
You might also consider changing the name of the class to remove any chance of ambiguity. There's some class names that are just going to wreck havoc trying to use them because they'll conflict with framework stuff constantly. It's usually more hassle than it's worth just to have that particular name. Program is one of those.
You can try to use this way:
public class Program
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool UseRanks { get; set; }
}
Adding [Key] attribute to the Id property.
In the file ApplicationDbContext.cs, you can override OnModelCreating method:
public DbSet<Program> Programs { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Program>().ToTable("Programs").HasKey(x => x.Id);
}

Entity Framework fails to get child elements

I have SQLite db and these EF models and context.
Models and Context
public class CardHolder
{
public int CardHolderId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string PhoneNumber { get; set; }
public string EmailAddress { get; set; }
public string TenantName { get; set; }
public ICollection<AccessCard> AccessCards { get; set; }
}
public class AccessCard
{
public int AccessCardId { get; protected set; }
public CardHolder CardHolder { get; set; }
public DateTime ActivationDate { get; protected set; }
public bool ActivationProcessed { get; set; }
public DateTime? DeactivationDate { get; protected set; }
public string DeactivationReason { get; set; }
public bool DeactivationProcessed { get; set; }
}
public class MyContext : DbContext
{
public DbSet<CardHolder> CardHolders { get; set; }
public DbSet<AccessCard> AccessCards { get; set; }
}
And the Main program
class Program
{
static void Main(string[] args)
{
using (var db = new MyContext())
{
var cardHolders = db.CardHolders.Include("AccessCard").ToList();
}
}
}
Question1: Why do I get this exception
System.InvalidOperationException: 'A specified Include path is not
valid. The EntityType 'SQLiteDemo.Models.CardHolder' does not declare
a navigation property with the name 'AccessCard'.'
If I replace it with
var cardHolders = db.CardHolders.Include("AccessCards").ToList();
I get another error:
SQL logic error no such column: Extent2.CardHolder_CardHolderId
What is wrong with Entity Framework?
Question2: Why cant I use arrow function in Include statement, it doesnt compile at all?
var cardHolders = db.CardHolders.Include(x => x.AccessCards).ToList();
Question3: Why do I need to use Include at all if my ICollection association property AccessCards is NOT virtual - that means eager loading must work by itself!
Why the hell it is so problematic and buggy? Nothing works as it should :(
1 - You have a typo as you have already determined :)
1B - "SQL logic error no such column: Extent2.CardHolder_CardHolderId"
EF isn't finding your FK. You could add it to your AccessCard model:
public int CardHolderId { get; set; }
2 - You need to pull in the LINQ extensions. Make sure you have both of these using statements at the top:
using System.Data.Entity;
using System.Linq;
3 - You, like many others, are misunderstanding lazy loading. Eager loading still requires an Include() to fetch related data. Lazy loading only fetches the relations when you access them.

Referenced object is not loaded from database

This the table structure I have:
#region Tables
public class WorkoutProfile
{
public WorkoutProfile()
{
WorkoutExercises = new List<WorkoutExercise>();
}
[Key]
public int ProfileId { get; set; }
public string Name { get; set; }
public int Sets { get; set; }
public int RestAfterSetInSeconds { get; set; }
public virtual User User { get; set; }
public virtual ICollection<WorkoutExercise> WorkoutExercises { get; set; }
}
public class WorkoutExercise
{
[Key]
public int WorkoutId { get; set; }
public virtual Exercise Exercise { get; set; }
public int Order { get; set; }
public int WorkoutTimeInSeconds { get; set; }
public int RestAfterInSeconds { get; set; }
}
public class Exercise
{
[Key]
public long ExerciseId { get; set; }
public string Title { get; set; }
public string Visualisation { get; set; }
public bool IsDefault { get; set; } // Is exersice should be included when user first registers
}
public class User
{
[Key]
public long UserId { get; set; }
public string Email { get; set; }
public DateTime Registered { get; set; }
}
#endregion Tables
In the repository class I run the following linq query:
return context
.WorkoutProfiles.Include(w => w.WorkoutExercises)
.Where(q => q.User.UserId == userId && q.ProfileId == profileId)
.FirstOrDefault();
and I receive the good and old "Object reference not set to an instance of an object". When examining the result, see that Exercises property in WorkoutExercises is null.
This is how the database is created using code first approach:
So, the question is: why Exercises not included in WorkoutExercises object? Do I need to include it somehow? I am using .NET Core 2
The simple answer would be no lazy loading in EFCore. Not Released yet but if you want to dabble with alpha code, its in the repository. Based on your classes there are no collections for exercises in WorkoutExcercise.
Then you need to ThenInclude(w => w.Exercises) following your Include clause since EFCore doesn't do lazy loading.
I found a solution following this post
Altered my code as following:
var top = context
.Set<WorkoutProfile>()
.Where(q => q.ProfileId == profileId && q.User.UserId == userId)
.Include(q => q.WorkoutExercises)
.SingleOrDefault();
context
.Entry(top)
.Collection(e => e.WorkoutExercises)
.Query()
.OfType<WorkoutExercise>()
.Include(e => e.Exercise)
.Load();
And it worked

Entity framework Database First & EF Code First get Relation Object By ID

in EF Database First when change ForeignKey(CommodityGroupID) automatic Get CommodityGroup for Commodity object, But in EF Code First(4.3.1) not doing.
public class Commodity
{
public int CommodityID { get; set; }
public string MadeBy { get; set; }
public decimal ServiceTimePrice { get; set; }
public decimal QCPrice { get; set; }
public int ServicePoint { get; set; }
public string Note { get; set; }
public int CommodityGroupID { get; set; }
[ForeignKey("CommodityGroupID")]
public virtual CommodityGroup CommodityGroup { get; set; }
}
public class CommodityGroup
{
public int CommodityGroupID { get; set; }
public string CommodityGroupName { get; set; }
public virtual ICollection<Commodity> Commodities { get; set; }
}
this Property defined in Edmx file (database first), i Should define this code in ef code first?
[BrowsableAttribute(false)]
[DataMemberAttribute()]
public EntityReference<CommodityGroup> CommodityGroupReference
{
get
{
return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<CommodityGroup>("GaamRepairModel.FK_Commodity_CommodityGroup", "CommodityGroup");
}
set
{
if ((value != null))
{
((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedReference<CommodityGroup>("GaamRepairModel.FK_Commodity_CommodityGroup", "CommodityGroup", value);
}
}
}
It sounds like you're wanting a change tracking proxy. You want the CommodityGroup navigation property to update automatically when the FK is changed correct?
See this post on MSDN for details about the change tracking proxy.
This post on MSDN shows some code on how to test of your proxy object is being created properly.
Is this a new object? If so, you'll need to call the CreateObject function on your DbSet, not use the New Commodity().