Add Detached object to EF 6 - c#

I have a detached object with many different properties. Some of these properties need to be "Attached" at some point, so that EF does not try to insert them into the database.
public partial class Load
{
public virtual int Id {get;set;}
[Required]
public virtual int CustomerId { get; set; }
[ForeignKey("CustomerId")]
public virtual Customer Customer { get; set; }
[Required]
public virtual long CreatedByApplicationUserId { get; set; }
[ForeignKey("CreatedByApplicationUserId")]
public virtual ApplicationUser CreatedByApplicationUser { get; set; }
public virtual long? ModifiedByApplicationUserId { get; set; }
[ForeignKey("ModifiedByApplicationUserId")]
public virtual ApplicationUser ModifiedByApplicationUser { get; set; }
public virtual long? CoveredByApplicationUserId { get; set; }
[ForeignKey("CoveredByApplicationUserId")]
public virtual ApplicationUser CoveredByApplicationUser { get; set; }
public virtual bool IsNetworkLoad { get; set; }
public virtual bool IsExport { get; set; }
public virtual bool CanTrackLoad { get; set; }
[Required]
public virtual DateTime CreatedDateTime { get; set; }
public virtual DateTime? ModifiedDateTime { get; set; }
public virtual string BillingReferenceNumber { get; set; }
[Required]
public virtual int LoadStatusId { get; set; }
[ForeignKey("LoadStatusId")]
public virtual LoadStatus LoadStatus { get; set; }
public virtual Freight Freight { get; set; }
public virtual ICollection<LoadOrigin> LoadOrigins { get; set; }
public virtual ICollection<LoadDestination> LoadDestinations { get; set; }
public virtual ICollection<LoadNote> LoadNotes { get; set; }
public virtual ICollection<LoadCarrier> LoadCarriers { get; set; }
}
Take my property LoadCarries for example. LoadCarries has a property Carrier that is populated from the db with no tracking.
public partial class LoadCarrier
{
public virtual int Id {get;set;}
public virtual bool IsDispatched { get; set; }
public virtual bool IsPrimary { get; set; }
[MaxLength(25)]
public virtual string CarrierProNumber { get; set; }
[MaxLength(1000)]
public virtual string RCNotes { get; set; }
[Required]
public virtual int CarrierId { get; set; }
[ForeignKey("CarrierId")]
public virtual Carrier Carrier { get; set; }
[MaxLength(50)]
[Required]
public virtual string Dispatcher { get; set; }
public virtual long LoadId { get; set; }
[ForeignKey("LoadId")]
public virtual Load Load { get; set; }
public virtual LoadDriver LoadDriver { get; set; }
}
Once I have my object populated properly, I try to save to the db. Since these object are all detached the save fails because I have a unique constraint on members of the Carriers object because EF incorrectly tries to insert an object that already exists in the db.
public virtual int Create(T entity, long userId)
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
dbSet.Add(entity);
return context.SaveChanges(userId);
}
I have tried several times to change the state of the Carriers object to "Unchanged", but the I get a duplicate primary key exception. I've also tried to add the Load object to the context and then set the Carrier object to an object I explicitly pull from the db. Is there a way by which I can attach this detached object properly?

Don't set the Carrier property of LoadCarrier, but instead only set the CarrierId.
This is enough for EF to know that it has to set the foreign key to an existing record.
There are other ways to do this, but since the Carrier is detached, it is most likely better to keep it attached and not to pull it in in the first place. I imagine you are preloading the carriers untracked because they are kind of fixed.

Related

Why is my entity type association being severed?

So I am currently attempting to seed the dev database with test info for our developers, but I am running into this issue - The association between entity types 'UserProfile' and 'ProjectProgress' has been severed but the relationship is either marked as 'Required' or is implicitly required because the foreign key is not nullable. If the dependent/child entity should be deleted when a required relationship is severed, then setup the relationship to use cascade deletes. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the key values.
User Profile Model -
public int UserProfileId { get; set; }
public UserProfileStatus UserProfileStatusId { get; set; } = UserProfileStatus.Active;
public SiteRole SiteRoleId { get; set; }
[MaxLength(100)]
public string Username { get; set; }
[MaxLength(50)]
public string Password { get; set; }
[MaxLength(100)]
public string FirstName { get; set; }
[MaxLength(100)]
public string LastName { get; set; }
[MaxLength(20)]
public string Phone { get; set; }
[MaxLength(150)]
public string Email { get; set; }
public DateTime PasswordChangeDt { get; set; } = DateTime.UtcNow;
public DateTime? LastLoginDt { get; set; }
public int InvalidLogins { get; set; }
public bool Locked { get; set; }
public DateTime? LockoutEnd { get; set; } //local copy of auth db value
public bool Analyst { get; set; }
public bool Reviewer { get; set; }
public bool Broker { get; set; }
public bool EmailGlobalStatusAlerts { get; set; }
public bool EmailReviewerStatusAlerts { get; set; }
public bool EmailAssignedStatusAlerts { get; set; }
public bool Active { get; set; }
public int? BankId { get; set; }
public int AddBy { get; set; }
public DateTime AddDt { get; set; } = DateTime.UtcNow;
public int ModBy { get; set; }
public DateTime ModDt { get; set; } = DateTime.UtcNow;
public virtual Bank Bank { get; set; }
[InverseProperty(nameof(ReviewerAnalyst.Analyst))]
public virtual ICollection<ReviewerAnalyst> AnalystReviewers { get; set; } = new
List<ReviewerAnalyst>();
[InverseProperty(nameof(DocumentAccessLog.AccessUser))]
public virtual ICollection<DocumentAccessLog> DocumentAccessLogs { get; set; } = new
List<DocumentAccessLog>();
public virtual ICollection<EmailLog> EmailLogs { get; set; } = new List<EmailLog>();
public virtual ICollection<Note> Notes { get; set; } = new List<Note>();
[InverseProperty(nameof(Project.Analyst))]
public virtual ICollection<Project> ProjectAnalyst { get; set; } = new List<Project>();
[InverseProperty(nameof(Project.Reviewer))]
public virtual ICollection<Project> ProjectReviewer { get; set; } = new List<Project>();
[InverseProperty(nameof(ReviewerAnalyst.Reviewer))]
public virtual ICollection<ReviewerAnalyst> ReviewerAnalysts { get; set; } = new
List<ReviewerAnalyst>();
public virtual ICollection<TimeOff> TimeOffs { get; set; } = new List<TimeOff>();
[InverseProperty(nameof(TimeOff.Approver))]
public virtual ICollection<TimeOff> TimeOffApprovals { get; set; } = new List<TimeOff>();
public virtual ICollection<UserEmailType> UserEmailTypes { get; set; } = new
List<UserEmailType>();
public virtual ICollection<UserRole> UserRoles { get; set; } = new List<UserRole>();
ProjectProgress Model -
public int ProjectProgressId { get; set; }
public int ProjectId { get; set; }
public int ProgressId { get; set; }
[MaxLength(250)]
public string Notes { get; set; }
public bool Complete { get; set; }
public int AddBy { get; set; }
public System.DateTime AddDt { get; set; }
public virtual Project Project { get; set; }
public virtual Progress Progress { get; set; }
[ForeignKey("AddBy")]
public virtual UserProfile UserProfile { get; set; }
I don't know exactly is needed to help troubleshoot this issue, but let me know and I will update the question.
Check the documentation on cascade delete
For the second action above, setting a foreign key value to null is not valid if foreign key is not nullable. (A non-nullable foreign key is equivalent to a required relationship.) In these cases, EF Core tracks that the foreign key property has been marked as null until SaveChanges is called, at which time an exception is thrown because the change cannot be persisted to the database. This is similar to getting a constraint violation from the database.
Try changing the ProjectProgress as follows, by making the AddBy nullable like this:
public calss ProjectProgress {
public int ProjectProgressId { get; set; }
public int ProjectId { get; set; }
public int ProgressId { get; set; }
[MaxLength(250)]
public string Notes { get; set; }
public bool Complete { get; set; }
public int? AddBy { get; set; }
public System.DateTime AddDt { get; set; }
public virtual Project Project { get; set; }
public virtual Progress Progress { get; set; }
[ForeignKey("AddBy")]
public virtual UserProfile UserProfile { get; set; }
}

Entity Framework - Multiple navigation property

I started working with Entity Framework and I have one BIG problem. I want a many-to-many relation between entities User and Role.
So I created 3 tables User, Role, UserRoles
Next I created 3 entities:
public class User
{
public virtual int UserId { get; protected set; }
public virtual string UserName { get; set; }
public virtual string Password { get; set; }
public virtual DateTime CreateDate { get; set; }
public virtual ICollection<UserRole> Roles { get; set; }
public virtual ICollection<UserRole> CreatedRoles { get; set; }
}
public class Role
{
public virtual int RoleId { get; protected set; }
public virtual string Name { get; set; }
}
public class UserRole
{
public virtual int UserRoleId { get; protected set; }
public virtual int UserId { get; set; }
public virtual User User { get; set; }
public virtual int RoleId { get; set; }
public virtual Role Role { get; set; }
public virtual DateTime CreateDate { get; set; }
public virtual int CreateUserId { get; set; }
public virtual User CreateUser { get; set; }
}
I want to separate mapped user from created user. And In this step Entity Framework start to throw a bugs.
Next I want navigate from User object to his mapped roles and to his created mappings.
Can I configure Entity Framework to do these things?
You should apply InverseProperty attribute:
public class User
{
public virtual int UserId { get; protected set; }
public virtual string UserName { get; set; }
public virtual string Password { get; set; }
public virtual DateTime CreateDate { get; set; }
[InverseProperty("User")]
public virtual ICollection<UserRole> Roles { get; set; }
[InverseProperty("CreateUser")]
public virtual ICollection<UserRole> CreatedRoles { get; set; }
}

Relationships in Entity Framework Code First

yesterday I created database in Management Studio and now I want to create it in program using EF Code First.
Here is link to my database: http://s11.postimg.org/6sv6cucgj/1462037_646961388683482_1557326399_n.jpg
And what I did:
public class GameModel
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreationTime { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public string TotalTime { get; set; }
public DateTime RouteStartTime { get; set; }
public DateTime RouteEndTime { get; set; }
public int MaxPlayersPerTeam { get; set; }
public int CityId { get; set; }
public int CreatorId { get; set; }
[InverseProperty("Id")]
[ForeignKey("CreatorId")]
//public int TeamId { get; set; }
//[ForeignKey("TeamId")]
public virtual UserModel Creator { get; set; }
public virtual CityModel City { get; set; }
//public virtual TeamModel WinnerTeam { get; set; }
}
public class RegionModel
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<CityModel> Cities { get; set; }
}
public class CityModel
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int RegionId { get; set; }
public virtual RegionModel Region { get; set; }
public virtual ICollection<UserModel> Users { get; set; }
public virtual ICollection<GameModel> Games { get; set; }
}
public class UserModel
{
[Key]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Login { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public DateTime RegistrationDate { get; set; }
public string FacebookId { get; set; }
public int CityId { get; set; }
public virtual CityModel City { get; set; }
public virtual IEnumerable<GameModel> Games { get; set; }
}
For now I wanted to create 4 tables but I have some problems... I want to make CreatorId in GameModel, but it doesn't work... When i wrote UserId instead of CreatorId it was working ( without [InverseProperty("Id")] and [ForeignKey("CreatorId")]).
This is what i get:
The view 'The property 'Id' 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 where T is a valid entity type.' or its master was not found or no view engine supports the searched locations.
edit:
I changed it like this:
public int CityId { get; set; }
public int CreatorId { get; set; }
[ForeignKey("CityId")]
public virtual CityModel City { get; set; }
[ForeignKey("CreatorId")]
public virtual UserModel Creator { get; set; }
And there is another problem.
The view 'Introducing FOREIGN KEY constraint 'FK_dbo.UserModels_dbo.CityModels_CityId' on table 'UserModels' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint. See previous errors.' or its master was not found or no view engine supports the searched locations.
And I have no idea how to solve it.
The InversePropertyAttribute specifies, which navigation property should be used for that relation.
A navigation property must be of an entity type (the types declared in your model, GameModel for example) or some type implementing ICollection<T>, where T has to be an entity type. UserModel.Id is an int, which clearly doesn't satisfy that condition.
So, the inverse property of GameModel.Creator could be UserModel.Games if you changed the type to ICollection<GameModel>, or had to be left unspecified. If you don't specify an inverse property, EF will try to work everything out on its own (in this case it would properly recognize GameModel.Creator as a navigation property, but UserModel.Games would most likely throw an exception, as it is neither an entity type, nor does it implement ICollection<T> with T being an entity type, nor is it a primitive type from a database point of view). However, EF's work-everything-out-by-itself-magic doesn't cope too well with multiple relations between the same entity types, which is when the InversePropertyAttribute is needed.
A quick example that demonstrates the problem:
class SomePrettyImportantStuff {
[Key]
public int ID { get; set; }
public int OtherId1 { get; set; }
public int OtherId2 { get; set; }
[ForeignKey("OtherId1")]
public virtual OtherImportantStuff Nav1 { get; set; }
[ForeignKey("OtherId2")]
public virtual OtherImportantStuff Nav2 { get; set; }
}
class OtherImportantStuff {
[Key]
public int ID { get; set; }
public virtual ICollection<SomePrettyImportantStuff> SoldStuff { get; set; }
public virtual ICollection<SomePrettyImportantStuff> BoughtStuff { get; set; }
}
Here, EF knows that it has to generate 2 FKs from SomePrettyImportantStuff to OtherImportantStuff with the names Id1 and Id2, but it has no way to tell which of the IDs refers to the entity where it was sold from and which is the one it was bought from.
Edit: How to fix the cyclic reference problem
To fix that problem, your context class should override OnModelCreating and configure the foreign keys which shouldn't cascade on delete accordingly, like this:
protected override void OnModelCreating(DbModelBuilder builder)
{
builder.Entity<CityModel>().HasMany(c => c.Users).WithRequired(u => u.City)
.HasForeignKey(u => u.CityId).WillCascadeOnDelete(value: false);
// Add other non-cascading FK declarations here
base.OnModelCreating(builder);
}

Entity Framework with ASP.NET MVC is saving value to wrong column

I have the following classes, a challenge has many submissions and it also has a winner.
The problem is that in my database Entity framework has created two columns on the Submissions table [ChallengeId] & [Challenge_ChallengeId] when I save an object it is added to the ChallengeId Column but relationship is being held on the Challenge_ChallengeId column.
Is there an issue with my models that is causing this or is some way I can set to hold the relationship and save to the same column?
Thanks!
public class Submission
{
[Key]
public Int32 SubmissionId { get; set; }
public DateTime Created { get; set; }
public Int32 ChallengeId { get; set; }
[ForeignKey("ChallengeId")]
public virtual Challenge Challenge { get; set; }
public String YouTubeVideoCode { get; set; }
public virtual IList<SubmissionVote> Votes { get; set; }
}
public class Challenge
{
[Key]
public Int32 ChallengeId { get; set; }
[Required]
public String ChallengeName { get; set; }
public virtual IList<Submission> Submissions { get; set; }
public Int32? OverallWinnerId { get; set; }
[ForeignKey("OverallWinnerId")]
public virtual Submission OverallWinner { get; set; }
}
I think you could benefit from just using the conventions and also using the shorthand keywords as data types. I think the error is because you have annotated the proxy with a ForeignKey attribute. Try this
public class Submission
{
public int SubmissionId { get; set; }
public DateTime Created { get; set; }
public int ChallengeId { get; set; }
public string YouTubeVideoCode { get; set; }
public virtual Challenge Challenge { get; set; }
public virtual IList<SubmissionVote> Votes { get; set; }
}
public class Challenge
{
public int ChallengeId { get; set; }
[Required]
public string ChallengeName { get; set; }
public int? OverallWinnerId { get; set; }
public virtual Submission OverallWinner { get; set; }
public virtual IList<Submission> Submissions { get; set; }
}
I'm pretty sure that Entity Framework uses a convention based approach, and the easiest way to fix this is to use the Id name instead of ChallengeId and SubmissionId
public class Submission
{
[Key]
public Int32 Id { get; set; }
public DateTime Created { get; set; }
public Int32 ChallengeId { get; set; }
[ForeignKey("ChallengeId")]
public virtual Challenge Challenge { get; set; }
public String YouTubeVideoCode { get; set; }
public virtual IList<SubmissionVote> Votes { get; set; }
}
public class Challenge
{
[Key]
public Int32 Id { get; set; }
[Required]
public String ChallengeName { get; set; }
public virtual IList<Submission> Submissions { get; set; }
public Int32? OverallWinnerId { get; set; }
[ForeignKey("OverallWinnerId")]
public virtual Submission OverallWinner { get; set; }
}

Many to many mapping not creating correct tables

I have a Template class
public class Template
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Category> Categories { get; set; }
public virtual Template RelatedTemplate { get; set; }
public virtual Field RelatedTemplatePrimaryField { get; set; }
public virtual ICollection<Field> Fields { get; set; }
}
And a Field class
public class Field
{
public int Id { get; set; }
public string Name { get; set; }
public int Min { get; set; }
public int Max { get; set; }
public bool AllowEmpty { get; set; }
public bool IsCollection { get; set; }
public virtual ICollection<Template> Templates { get; set; }
}
The problem is it's not creating a many to many relationship, it just adds a FK on the fields table, I want a many to many relationship on
Fields ICollection<Field> Fields
and
ICollection<Template> Templates
EDIT:
If I remove
public virtual Template RelatedTemplate { get; set; }
public virtual Field RelatedTemplatePrimaryField { get; set; }
It works... Any ideas?
This is a case where you must help EF to correctly recognize your relations. You can do it either by data annotations:
[InverseProperty("Fields")]
public virtual ICollection<Template> Templates { get; set; }
or by fluent-API:
modelBuilder.Entity<Template>()
.HasMany(t => t.Fields)
.WithMany(f => f.Templates);

Categories

Resources