Exclude underlying objects when storing data using EF6 - asp.net

I have a class Ticket which has some properties. Three of these (View, Task and Key) properties are navigation properties. Those properties already exist in database even before a ticket has been stored. In my application I load those properties from the database first and then create a Ticket object. I need to save only the ticket (not the underlying objects ) to the database with the id to Key, View and Task (these are primery keys in the Ticket table)
[Table("Tickets")]
public class Ticket
{
[Key]
public int Id { get; set; }
public DateTime? Created { get; set; }
[Required]
public View View{ get; set; }
[Required]
public Key Key { get; set; }
public Task Task { get; set; }
}
I try to save the Ticket object like this:
db.Tickets.Add(ticket);
db.Entry(ticket.Key).State = System.Data.Entity.EntityState.Unchanged;
db.Entry(ticket.View).State = System.Data.Entity.EntityState.Unchanged;
db.Entry(ticket.Task).State = System.Data.Entity.EntityState.Unchanged;
db.SaveChanges();
When I try this approach I get the error:
{"Message":"An error has occurred.","ExceptionMessage":"Saving or accepting changes failed because more than one entity of type 'Key' 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. Use the Entity Designer for Database First/Model First configuration. Use the 'HasDatabaseGeneratedOption\" fluent API or 'DatabaseGeneratedAttribute' for Code First configuration.","
Is it even possible to work with Entity Framework this way? Having pre defined data which is loaded to it's objects (Key, View, Task) first and later assign these objects to an object having these properties but then in the entity framework context only adding the parent object, in this case the ticket?
I have also tried to set the underlying objects to null but then I will loose the data for those underlying objects, data I need later on in the application.
This is how the underlying objects look like:
[Table("Views")]
public class View
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Version { get; set; }
[Required]
public DateTime? Created { get; set; }
}
[Table("Keys")]
public class Key
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Version { get; set; }
[Required]
public DateTime? Created { get; set; }
}
[Table("Tasks")]
public class Task
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Version { get; set; }
[Required]
public DateTime? Created { get; set; }
}

Try adding Foreign Keys to your object and making those required instead of making the navigation property required. Like so:
[Table("Tickets")]
public class Ticket
{
[Key]
public int Id { get; set; }
public DateTime? Created { get; set; }
[Required]
public int ViewId {get; set; }
[ForeignKey("ViewId")]
public View View{ get; set; }
[Required]
public int KeyId {get; set; }
[ForeignKey("KeyId")]
public Key Key { get; set; }
public Task Task { get; set; }
}

Related

When creating a one-to-one relationship in an entity it throws an error The navigation property 'StudentModel' was not found on the dependent type

I want to create one to one relation between tables. My table is
public class StudentModel
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime DateOfBirth { get; set; }
[Required, Display(Name="Department Name")]
public int DeptId { get; set; }
//navigration proprty
[ForeignKey("DeptId")]
public virtual DepartmentModels Department { get; set; }
public virtual StudentRegistrationModels StudentRegistration { get; set; }
}
and my other table is
public class StudentRegistrationModels
{
[Key]
public int StudentId { get; set; }
[Required]
public int CourseId { get; set; }
[Required]
public DateTime EnrollDate { get; set; }
public bool IsPaymentComplete { get; set; }
//navigration proprty
[ForeignKey("StudentId")]
public virtual StudentModel Student { get; set; }
[ForeignKey("CourseId")]
public virtual CourseModels Course { get; set; }
//oneToOneStudentRegistration
}
But when I make migration it throws an error:
Unable to determine the principal end of an association between the types 'StudentManagementSystem.Models.StudentModel' and 'StudentManagementSystem.Models.StudentRegistrationModels'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
Why is this occurring?
I believe the issue is that you have a single StudentRegistrationModel instance in your StudentModel, where the StudentRegistrationModel looks to be more of a Many-to-Many structure. Can a single student only be registered to a single course? If that were the case it would make more sense for StudentModel to just have a CourseModel reference. Since a Student probably has multiple courses, it would probably make more sense for StudentModel to have:
public virtual ICollection<StudentRegistrationModels> StudentRegistration { get; set; } = new List<StudentRegistrationModels>();
Then ensuring that your model configuration maps out the relationship. This can be done with an attribute, as part of the DbContext OnModelCreating, or using an EntityTypeConfiguration. With Attributes:
[InverseProperty("Student")] // Tells EF this collection corresponds to the Student on the StudentRegistrationModel.
public virtual ICollection<StudentRegistrationModels> StudentRegistration { get; set; } = new List<StudentRegistrationModels>();
Maybe try to add [Key] annotation to Id field in StudentModel.
using System.ComponentModel.DataAnnotations;
public class StudentModel
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime DateOfBirth { get; set; }
[Required, Display(Name="Department Name")]
public int DeptId { get; set; }
//navigration proprty
[ForeignKey("DeptId")]
public virtual DepartmentModels Department { get; set; }
public virtual StudentRegistrationModels StudentRegistration { get; set; }
}
or if it won't work try map relationship in OnModelCreating in your data context.

Xamarin Form Create Table Problem in SqLite based complex class

I develop cross platform mobile app on Xamarin Forms.
I try to create tables in sqlite. My class name is 'News' and News class containts 'Country' class.
Create table code and class is like below.
[Serializable]
[DataContract]
public class News
{
[PrimaryKey]
[DataMember]
public int Id { get; set; }
[DataMember]
public Guid ActivityId { get; set; }
[DataMember]
public int Type { get; set; }
[DataMember]
public string Title { get; set; }
[DataMember]
public CountryBase Country { get; set; }
}
and code is something like
var db = new SQLiteConnection (dbPath);
db.CreateTable<News> ();
This isn't working. When i remove the country class it works.
I try to create country table separately but still i couldn't create News table. How can i fix the problem?
The SQLite doesn't allow relationships, and public CountryBase Country { get; set; } is interpreted as one.
So you need:
either to serialize your Country as a string before inserting, and you will have this:
[DataMember]
public string SerializedCountry { get; set; }
[Ignore]
public CountryBase Country { get; set; }
And you need to handle serialization with Json.net manually of the Country.
Or create another Country table, and specify its id in your entity definition:
[Serializable]
[DataContract]
public class News
{
[PrimaryKey]
[DataMember]
public int Id { get; set; }
[DataMember]
public Guid ActivityId { get; set; }
[DataMember]
public int Type { get; set; }
[DataMember]
public string Title { get; set; }
[DataMember]
public int CountryId { get; set; }
}
[Serializable]
[DataContract]
public class Country
{
[PrimaryKey]
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
...
}

ASP.NET Core Conflict with Foreign Key

I have got several models:
Course.cs
public class Course
{
public Guid Id { get; set; }
public ICollection<ApplicationUser> Teacher { get; set; }
public string Name { get; set; }
public string ShortName { get; set; }
public DateTime CreationDate { get; set; }
public bool IsActive { get; set; }
}
Group.cs
public class Group
{
public Guid Id { get; set; }
public ApplicationUser Mentor { get; set;}
public string DisplayName { get; set; }
public string GroupName { get; set; }
public DateTime StartYear { get; set; }
public string InviteCode { get; set; }
public ICollection<ApplicationUser> Students { get; set; }
public ICollection<Course> Courses { get; set; }
}
ApplicationUser.cs
public class ApplicationUser : IdentityUser
{
[Required]
public string Firstname { get; set; }
[Required]
public string Surname { get; set; }
public bool Gender { get; set; }
public DateTime Birthdate { get; set; }
//[Required]
public string InviteCode { get; set; }
public Guid GroupId { get; set; }
[ForeignKey("GroupId")]
public Group CurrentGroup { get; set; }
public ICollection<Group> PastGroups { get; set; }
}
Now when I try to register (using Identity) a user (not even trying to give the user a group) I receive this error:
SqlException: The INSERT statement conflicted with the FOREIGN KEY
constraint "FK_AspNetUsers_Groups_GroupId". The conflict occurred in
database "aspnet-Project_Dojo-3af15f80-8c62-40a6-9850-ee7a296d0726",
table "dbo.Groups", column 'Id'. The statement has been terminated.
In my modelBuilder I have added some logics for the relations between Group, ApplicationUser (Students) and the Foreign Key:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);\\
builder.Entity<ApplicationUser>()
.HasOne(p => p.CurrentGroup)
.WithMany(b => b.Students)
.HasForeignKey(p => p.GroupId);
}
I don't know what this is exactly doing, but I've been browsing some Stackoverflow threads to come to this code (migrations weren't working without it).
I look forward to a solution for my problem. Once again, I'm not doing ANYTHING with the groups yet when registering.
Thanks in advance!
not even trying to give the user a group
Well there's your problem, it's required.
Either provide a group, or make it optional by making the foreign key nullable (Guid? GroupId).
Because it's currently a non-nullable struct, it'll have a default value of all zeroes (Guid.Empty). This FK is not known in your database, resulting in the error you see.

Unable to add second self Referencing FK to model, causes Unable to determine the principal end error

First off, I know there are a lot of posts about the Unable to determine the principal end of an association between the types error but ever single one I see does not match my issue, if I missed one sorry about that.
I have built an Entity that will end up referencing it's self twice and when I put the code in for the first self reference it works fine, as soon as ad the code for the second it breaks. Doing some testing I have found that if I use either of the self references by them self everything works fine, it is only when I add the second self reference that it breaks. The code I am using for the self references is:
[ForeignKey("ManagerID")]
public User Manager { get; set; }
//Auditing Fields
public DateTime DateCreated { get; set; }
public int? UpdatedByUserID { get; set; }
public DateTime? DateUpdated { get; set; }
public DateTime LastAutoUpdate { get; set; }
[ForeignKey("UpdatedByUserID")]
public User UpdatedByUser { get; set; }
The full entity code block is:
public class User
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public int ADPFileNumber { get; set; }
[Required]
public string ADUserName { get; set; }
public int AirCardCheckInLateCount { get; set; }
[Required]
public string Email { get; set; }
public DateTime? EndDate { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public int ManagerID { get; set; }
public string MobilePhone { get; set; }
[Required]
public string Office { get; set; }
[Required]
public string Phone { get; set; }
public decimal PTO { get; set; }
public DateTime StartDate { get; set; }
public int VehicleCheckInLateCount { get; set; }
public int WexCardDriverID { get; set; }
[ForeignKey("ManagerID")]
public User Manager { get; set; }
//Auditing Fields
public DateTime DateCreated { get; set; }
public int? UpdatedByUserID { get; set; }
public DateTime? DateUpdated { get; set; }
public DateTime LastAutoUpdate { get; set; }
[ForeignKey("UpdatedByUserID")]
public User UpdatedByUser { get; set; }
}
What am I missing that cause the second self reference to break?
You have to indicate the principal end of both associations explicitly. You can do that with the class you had originally, without inverse collection properties:
modelBuilder.Entity<User>()
.HasOptional(u => u.Manager)
.WithMany()
.HasForeignKey(u => u.ManagerID);
modelBuilder.Entity<User>()
.HasOptional(u => u.UpdatedByUser)
.WithMany()
.HasForeignKey(u => u.UpdatedByUserID);
Note that ManagerID should be an int? as well. You can't create any User if it requires another user to preexist. That's a chicken-and-egg problem.
As mentionned in Multiple self-referencing relationships in Entity Framework, you seem to be missing the other part of the relationship.
i.e.
[InverseProperty("Manager")]
public virtual ICollection<User> ManagedUsers {get;set;}
[InverseProperty("UpdatedByUser")]
public virtual ICollection<User> UpdatedUsers {get;set;}
EDIT: based on #Gert Arnold's answer you should indeed add the [InverseProperty] attribute

Repopulate Database with table generated from model

A collegue of mine accidentally deleted the UserProfiles table from my database, which was generated from a model class.
Here's what the model class looks like:
namespace OneMillion.Models
{
[Table("UserProfile")]
public class User
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public string Sex { get; set; }
public string SecretQuestion { get; set; }
public string SecretQuestionAnswer { get; set; }
public int MoneyIn { get; set; }
public int MoneyOut { get; set; }
public int TimesWon { get; set; }
}
}
When I try to Update-Database via the package manager console I get this error:
Cannot find the object "dbo.UserProfile" because it does not exist or you do not have permissions.
Data migrations are enabled.
How can I solve this? Shall I delete the whole database?
Can you connect to the DB as a user with sufficient permissions to create databases? You could log in as that user in SSMS and create the table with the appropriate columns etc. Then the table exists, and migrations can find it.

Resources