creating an object in ternary operator from query result - c#

I need to create objects to return to front end from data I got from database because the response doesnt contain ALL fields of entity and I also add this [NotMapped] propety AmountOfTenants = t.Apartment.Tenants.Count(), to the response.
If I remove ternary operator here t.ApartmentId != null ? and just create new Apartment every time, then, when a Tenant doesnt have any related Apartmetn to him, then my json response contains Tenant with an Apartment object inside of him, where all values are 0/null, thats why i need ternary, to set Apartment to null if Tenant's ApartmentId is null.
Without ternary it works, but i get that apartment with 0/null values and when i add ternary operator i get this error:
System.InvalidCastException: Unable to cast object of type 'System.Linq.Expressions.NewExpression' to type 'System.Linq.Expressions.MethodCallExpression'.
Please help
List<Tenant> tenants = await _context.Tenants.Include(tenant => tenant.Apartment.House)
.Select(t => new Tenant
{
Id = t.Id,
FirstName = t.FirstName,
LastName = t.LastName,
PersonalCode = t.PersonalCode,
Birthday = t.Birthday,
PhoneNumber = t.PhoneNumber,
Email = t.Email,
ApartmentId = t.ApartmentId,
Apartment = t.ApartmentId != null ? new Apartment
{
Id = t.Apartment.Id,
Number = t.Apartment.Number,
Floor = t.Apartment.Floor,
AmountOfTenants = t.Apartment.Tenants.Count(),
AmountOfRooms = t.Apartment.AmountOfRooms,
TotalArea = t.Apartment.TotalArea,
AvailableArea = t.Apartment.AvailableArea,
HouseId = t.Apartment.HouseId,
House = t.Apartment.House
} : null
}).ToListAsync();
----------------------------------------------EDIT
someone asked for Tenant entity:
namespace RestApi.Models
{
public class Tenant
{
public long Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string PersonalCode { get; set; }
public DateTime Birthday { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
public long? ApartmentId { get; set; }
public Apartment? Apartment { get; set; }
}
}
--------------------------EDIT
someone asked for Apartment class:
namespace RestApi.Models
{
public class Apartment
{
public long Id { get; set; }
public int Number { get; set; }
public int Floor { get; set; }
public int AmountOfRooms { get; set; }
[NotMapped]
public int AmountOfTenants { get; set; }
public int TotalArea{ get; set; }
public int AvailableArea { get; set; }
public long? HouseId { get; set; }
public House House { get; set; }
public ICollection<Tenant> Tenants { get; set; }
}
}
------------------------------ EDIT
I got a friend to check something similar on his machine:
var tenants = await context.Tenants
.Include(t => t.Apartment).ThenInclude(a => a.House)
.Include(t => t.Apartment).ThenInclude(a => a.Tenants)
.Select(t => new Tenant
{
Id = t.Id,
//etc...
Apartment = t.ApartmentId != null ? new Apartment
{
Id = t.Apartment.Id,
NumberOfTenants = t.Apartment.Tenants.Count(),
//etc...
} : null
}).ToListAsync();
on his machine this new statement inside another new statement works and gives no error

You could try using this, which should eliminate the need to use the NotMapped annotation and you wouldn't need to use the select at that point either.
public class Apartment
{
public long Id { get; set; }
public int Number { get; set; }
public int Floor { get; set; }
public int AmountOfRooms { get; set; }
public int AmountOfTenants { get { return this.Tenants != null ? this.Tenants.Count : 0; } }
public int TotalArea { get; set; }
public int AvailableArea { get; set; }
public long? HouseId { get; set; }
public House House { get; set; }
public ICollection<Tenant> Tenants { get; set; }
}
New Query
List<Tenant> tenants = await _context.Tenants.Include(tenant => tenant.Apartment).ThenInclude(a => a.House).ToListAsync();
Edit
As for an explanation of the error you are getting. It is basically saying that it can't make a new expression inside of a new expression (i.e. creating a new Apartment inside of a new Tenant)

I'm making an assumption about your Apartments class so correct me if I go astray here.
Assumption : A tenant can only have one apartment and an apartment can have multiple tenants.
With that being said you tenant class just needs to reference an Apartment object. The Apartment ID is contained in Apartment object so you're really storing that data twice. If you're looking to add an new tenant to an apartment you don't need the select statement. You might want to do a bullet point summary to clarify your intentions if there is more to this. If you're looking to include all objects within the Tenant class(data from other tables) you can consider lazy loading if your application permits.
public class Tenant
{
[Key]
public long Id { get; set; } //Primary Key
public string FirstName { get; set; }
public string LastName { get; set; }
public string PersonalCode { get; set; }
public DateTime Birthday { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
//public long? ApartmentId { get; set; }
public virtual Apartment Apartment { get; set; }
}
public class Apartment
{
[Key]
public Guid ApartmentID { get; set; } //Primary Key
public virtual List<Tenant> Tenants { get; set; }
}
public class appStuff
{
void AddStuff()
{
var myContext = new _context();
Apartment apartment = myContext.Find(Predicate4YourApartment());
var ten = new Tenant
{
Birthday = DateAndTime.Now,
Apartment = apartment,
FirstName = "John" //add the rest.
};
myContext.TenantsTable.Add(ten);
//This will add the Tenant to the Tenants table and the apartment to the Apartments table if you create a new one
//instead of finding an existing one like shown above
myContext.SaveChanges();
}
void GetStuff()
{
var myContext = new _context();
var myTenant = myContext.TenantsTable.Where(x => x.Apartment.ApartmentID == "DesiredID");
}
}
public class _context : DbContext
{
public _context() : base()
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseLazyLoadingProxies();
base.OnConfiguring(optionsBuilder);
}
}

Related

EF core won't let me access the same property twice

I'm trying to store older versions of entities in my database. To do that I am copying the existing values before I update them. For some reason EF Core won't let me use the same batch.Values property twice.
public async Task<Batch> UpdateBatch(Batch batch, Batch updatedBatch)
{
foreach (var valueParameter in batch.Values)
{
batch.ValuesHistory.Add(new ParameterValueHistory
{
Parameter = valueParameter.Parameter,
ParameterBatchNumber = valueParameter.ParameterBatchNumber,
Value = valueParameter.Value
});
}
batch.Values = updatedBatch.Values;
batch.Version++;
await this.context.SaveChangesAsync();
return batch;
}
The foreach loop and batch.Values = updatedBatch.Values; work exactly like they should when only one of them exists. But whenever they're both active I get the following error:
The instance of entity type 'ParameterValue' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
These are the relevant models:
ParameterValue:
public class ParameterValue
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
[Required]
public virtual RecipeParameter Parameter { get; set; }
public string Value { get; set; }
public string? ParameterBatchNumber { get; set; }
}
ParameterValueHistory:
public class ParameterValueHistory
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
[Required]
public virtual RecipeParameter Parameter { get; set; }
public string Value { get; set; }
public string? ParameterBatchNumber { get; set; }
}
RecipeParameter for context:
public class RecipeParameter
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Type { get; set; }
public string Unit { get; set; }
public string Value { get; set; }
public bool BatchRequired { get; set; }
}
Batch:
public class Batch
{
[Key]
[MaxLength(12)]
public string BatchNumber { get; set; }
public virtual List<ParameterValue> Values { get; set; }
public virtual List<ParameterValueHistory> ValuesHistory { get; set; }
public int Version { get; set; }
[Required]
public bool IsResearch { get; set; }
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public DateTime CreatedOn { get; set; } = DateTime.UtcNow;
}
This is my DbContext class:
public class ApplicationDataContext : DbContext
{
public ApplicationDataContext(DbContextOptions<ApplicationDataContext> options)
: base(options)
{
}
public DbSet<Product> Product { get; set; }
public DbSet<Batch> Batch { get; set; }
public DbSet<ParameterValue> ParameterValue { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseLazyLoadingProxies();
base.OnConfiguring(optionsBuilder);
}
}
Why does this error keep showing up? Even when I am just accessing the propety as batch.Values more than once, it gives me this error.
UPDATE:
This is the controller method that calls the UpdateBatch method.
[HttpPut("{productId}/batches/{batchNumber}")]
public async Task<ActionResult<Batch>> PutBatch(string batchNumber, Batch updatedBatch)
{
Batch batch = await this.repository.GetBatchByBatchNumber(batchNumber);
if (batch == null)
{
return NotFound()
}
return await this.repository.UpdateBatch(batch, updatedBatch);
}
When you use batch.Values = updatedBatch.Values;, because batch.Values contains the foreign key of Batch, and if the value in updatedBatch.Values also contains the key value,if the equal operation is performed directly, due to the foreign key constraint, the foreign key cannot be modified directly, which will cause your error.
Therefore, you cannot include the key value in the Values in your updateBatch.
Regarding your question. I did a simple test. You can see the following code(updateBatch.Values have no Id).
var batch = _context.Batches.Include(c => c.Values)
.ThenInclude(c => c.Parameter)
.Include(b => b.ValuesHistory)
.ThenInclude(c => c.Parameter)
.Where(c => c.BatchNumber == "1")
.FirstOrDefault();
var updateBatch = new Batch
{
Version = 3,
CreatedOn = new DateTime(),
IsResearch = true,
Values = new List<ParameterValue>
{
new ParameterValue
{
Value = "hello",
Parameter = new RecipeParameter
{
BatchRequired = true,
Name = "h",
Type = "e",
Unit = "l",
Value = "o"
}
},
},
ValuesHistory = new List<ParameterValueHistory>()
};
foreach (var valueParameter in batch.Values)
{
batch.ValuesHistory.Add(new ParameterValueHistory
{
Parameter = valueParameter.Parameter,
ParameterBatchNumber = valueParameter.ParameterBatchNumber,
Value = valueParameter.Value
});
}
batch.Values = updateBatch.Values;
batch.Version++;
_context.SaveChanges();
Test result:
start by making these changes..
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
should not be on
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public DateTime CreatedOn { get; set; } = DateTime.UtcNow;
instead model like
public class Batch
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
//you can add index on this
[MaxLength(12)]
public string BatchNumber { get; set; }
public int Version { get; set; }
[Required]
public bool IsResearch { get; set; }
[Required]
public DateTime CreatedOn { get; set; };// set this in the repo or create do another way
//you add this but don't see the linkage aka ParameterValue does not have a BatchId
public virtual List<ParameterValue> Values { get; set; }
public virtual List<ParameterValueHistory> ValuesHistory { get; set; }
}

Multiplicity constraint violated error while implementing chat functionality

I am implementing Chat functionality excatly like this one in asp.net.This is the model explaination
My classes are: User, Conversaiton and Message:
public class User
{
public Guid Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public string UserName { get; set; }
//keys
public ICollection<Conversation> Conversations { get; set; }
}
public class Conversation
{
public Guid ID { get; set; }
public ICollection<Message> Messages { get; set; }
public User RecipientUser { get; set; }
public Guid SenderUserId { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime ModifiedDate { get; set; }
}
and finally,
public class Message
{
public int ID { get; set; }
public string Text { get; set; }
public bool IsDelivered { get; set; }
public bool IsSeen { get; set; }
public Guid SenderUserId { get; set; }
public Conversation Conversation { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime ModifiedDate { get; set; }
}
I am using EntityTypeConfiguration using fluent Api which are:
public class UserConfig : EntityTypeConfiguration<User>
{
public UserConfig()
{
HasMany(x => x.Conversations).WithRequired(x => x.RecipientUser).HasForeignKey(x => x.SenderUserId);
}
}
public class ConversationConfig : EntityTypeConfiguration<Conversation>
{
public ConversationConfig()
{
HasKey(x => x.ID);
HasRequired(x => x.RecipientUser).WithMany(x => x.Conversations);
HasMany(x => x.Messages).WithRequired(x => x.Conversation).HasForeignKey(x => x.SenderUserId);
}
}
But I'm getting error as follows:
Multiplicity constraint violated. The role 'Conversation_RecipientUser_Target' of the relationship 'DataAcessLayer.Conversation_RecipientUser' has multiplicity 1 or 0..1.
If anyone could help me. Many thanks!!!
I didn't get the exception when I used your model, but that doesn't matter much, because you've got to make some changes anyway.
First, this configuration in UserConfig ...
HasMany(x => x.Conversations).WithRequired(x => x.RecipientUser)
.HasForeignKey(x => x.SenderUserId);
... doesn't make sense to me. This means that the conversations belong to the recipient, not to the sender, which in itself is somewhat unexpected. But the name of the foreign key is SenderUserId. If you save objects into this model, SenderUserId will get the value of the recipient's ID!
Second, assuming the previous point was an error, you make it harder than necessary by defining ...
public User RecipientUser { get; set; }
public Guid SenderUserId { get; set; }
This means that you can only navigate from Conversation to the sender User by an explicit join over SenderUserId. Conversely, you can only set the recipient by setting RecipientUser, not by simply setting a foreign key value.
Third, you shouldn't have this SenderUserId in Message. It should be ConversationId instead. You can find a Message's sender through the Conversation it belongs to.
All in all, I changed your model into this, reducing it to the bare-bone minimum, and using int for ID, just because it's easier reading:
public class User
{
public int ID { get; set; }
public string Name { get; set; }
public ICollection<Conversation> Conversations { get; set; }
}
public class Conversation
{
public int ID { get; set; }
public int SenderUserID { get; set; }
public User SenderUser { get; set;}
public int RecipientUserID { get; set; }
public User RecipientUser { get; set; }
public ICollection<Message> Messages { get; set; }
}
public class Message
{
public int ID { get; set; }
public string Text { get; set; }
public int ConversationID { get; set; }
public Conversation Conversation { get; set; }
}
And the only configuration:
public class ConversationConfig : EntityTypeConfiguration<Conversation>
{
public ConversationConfig()
{
HasKey(c => c.ID);
HasRequired(c => c.SenderUser).WithMany(u => u.Conversations)
.HasForeignKey(c => c.SenderUserID).WillCascadeOnDelete(true);
HasRequired(c => c.RecipientUser).WithMany()
.HasForeignKey(c => c.RecipientUserID).WillCascadeOnDelete(false);
HasMany(c => c.Messages).WithRequired(x => x.Conversation)
.HasForeignKey(x => x.ConversationID);
}
}
(One of the foreign keys doesn't have cascaded delete because that will cause a SQL-Server error: multiple cascade paths).
A simple test:
using (DbContext db = new DbContext(connectionString))
{
var send = new User { Name = "Sender" };
db.Set<User>().Add(send);
var rec = new User { Name = "Recipient" };
var messages = new[] { new Message { Text = "a" }, new Message { Text = "b" } };
var conv = new Conversation { SenderUser = send, RecipientUser = rec, Messages = messages };
db.Set<User>().Add(rec);
db.Set<Conversation>().Add(conv);
db.SaveChanges();
}
Result:
Users:
ID Name
1 Recipient
2 Sender
Converstions:
ID RecipientUserID SenderUserID
11 1 2
Messages:
ID Text ConversationID
21 a 11
22 b 11
Given Conversation has 1 User foreign key-column which called SenderUserId
// Here you set the user key for the old user!
conversation.RecipientUser = user;
// Here you are trying to change Conversation.SenderUserId from other user (currentUser)
currentUser.Conversations.Add(conversation);
As you seen the added user into collection is not the same which you have assigned to the Conversation 1..n can not be working in this way correctly.
EF try to say to you with the exception, that you are trying to add/change more then entity for the releshinship one-to-zero-or-one, the error message is a little bit foggy I would say the error message should be like "One-to-many navigation properties should using the same entity"
solution 1)
// conversation.RecipientUser = user; remove this line why you need it?!
solution 2)
Create a new property in conversation one for CurrentUser and the other one for User
public class User
{
public User()
{
Conversations = new List<Conversation>();
CurrentUserConversations = new List<Conversation>();
}
public Guid Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public string UserName { get; set; }
public ICollection<Conversation> Conversations { get; set; }
public ICollection<Conversation> CurrentUserConversations { get; set; }
}
public class Conversation
{
public Conversation()
{
Messages = new List<Message>();
}
public Guid ID { get; set; }
public User RecipientUser { get; set; }
public Guid SenderUserId { get; set; }
public User CurrentUser { get; set; }
public Guid? CurrentUserId { get; set; }
public ICollection<Message> Messages { get; set; }
}
public class UserConfig : EntityTypeConfiguration<User>
{
public UserConfig()
{
HasMany(x => x.Conversations).WithRequired(x => x.RecipientUser).HasForeignKey(x => x.SenderUserId);
HasMany(x => x.CurrentUserConversations).WithOptional(x => x.CurrentUser).HasForeignKey(x => x.CurrentUserId);
}
}
How to use it:
var user = myDbContext.Users.First();
var currentUser = new User { Active = true, Id = Guid.NewGuid() };
myDbContext.Users.Add(currentUser);
var message = new Message { IsDelivered = true };
var conversation = new Conversation() { ID = Guid.NewGuid() };
conversation.Messages.Add(message);
conversation.CurrentUser = user;
currentUser.Conversations.Add(conversation);
myDbContext.Conversations.Add(conversation);
myDbContext.SaveChanges();

How to load DTO Within DTOs?

PROBLEM:
I am very new to EF and to LINQ, so please bear with me.
I am trying to create a EF6 model using the database first approach. Simply speaking, I have 2 database tables tblUser and tblMilkMan which have a foreign key relationship on the UserID column.
To avoid cyclic references and to shape the entity data I have created DTO classes for both the models.
I made the MilkManDTO class contain a reference to a UserDTO instance.(This is probably stupid, if so, please guide me to the right way).My aim is to be able to load a milkmen and the related User data
Anyway in my API call, when I try to load a MilkMan by ID, I do not know how to load the related UserDTO. I found examples online on how to load related Entities but not related DTOs.
DB Schema:
Models:
MilkMan Model and DTO:
namespace MilkMan.Models
{
using System;
using System.Collections.Generic;
public partial class tblMilkMan
{
public int RecordID { get; set; }
public int UserID { get; set; }
public bool IsMyTurn { get; set; }
public int RoundRobinOrder { get; set; }
public virtual tblUser tblUser { get; set; }
}
public class MilkManDTO
{
public int RecordID { get; set; }
public int UserID { get; set; }
public bool IsMyTurn { get; set; }
public int RoundRobinOrder { get; set; }
public virtual UserDTO User { get; set; }
}
}
User Model and DTO:
public partial class tblUser
{
public tblUser()
{
this.tblMilkMen = new HashSet<tblMilkMan>();
}
public int UserID { get; set; }
public string LogonName { get; set; }
public string Password { get; set; }
public int PasswordExpiresAfter { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
:
// more fields
:
public virtual ICollection<tblMilkMan> tblMilkMen { get; set; }
}
public class UserDTO
{
public int UserID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Web API Controller Method:
// GET api/MilkMan/5
[ResponseType(typeof(MilkManDTO))]
public async Task<IHttpActionResult> GettblMilkMan(int id)
{
//tblMilkMan tblmilkman = await db.tblMilkMen.FindAsync(id);
MilkManDTO milkMan = await db.tblMilkMen.Select(b => new MilkManDTO()
{
RecordID = b.RecordID,
UserID = b.UserID,
IsMyTurn = b.IsMyTurn,
RoundRobinOrder = b.RoundRobinOrder,
User = //???? Error//
}).SingleOrDefaultAsync(b => b.RecordID == id);
if (milkMan == null)
{
return NotFound();
}
return Ok(milkMan);
}
You can nest a new UserDTO and use the same initialization list technique.
MilkManDTO milkMan = await db.tblMilkMen.Select(b => new MilkManDTO()
{
RecordID = b.RecordID,
UserID = b.UserID,
IsMyTurn = b.IsMyTurn,
RoundRobinOrder = b.RoundRobinOrder,
User = new UserDTO {
UserID = b.User.UserID,
FirstName = b.User.FirstName,
LastName = b.User.LastName,
}
}).SingleOrDefaultAsync(b => b.RecordID == id);
This code may throw a null reference exception on b.User.UserID if there is not associated User and thus User could be null. You would need to deal with this with either a ?? coalesce, ternary (b.User == null ? "DefaultFirstName" : b.User.FirstName) or omit the entire reference User = (b.User == null ? (UserDTO)null : new UserDTO { ... }). null's make this kind of thing fun.
With C# 6 we have null reference operator .? that makes this much more succinct.

Collections duplicated when trying to update a detached entity's related collection

I have two API calls. GetExam and SaveExam. GetExam serializes to JSON which means by the time I go to save, the entity is detached. This isnt a problem, I can go retrieve the entity by its primary key and update its properties manually.
However, when I do so the exam questions get its current collection duplicated. For example, if examToSave.ExamQuestions had a few questions deleted, and a new one added all selectedExam.exam_question are duplicated and the new one is added in. Eg. if 3 questions existed, I deleted 1 and added 4 there will now be 7.
Domain models:
public partial class exam
{
public exam()
{
this.exam_question = new HashSet<exam_question>();
}
public int ID { get; set; }
public string ExamName { get; set; }
public string ExamDesc { get; set; }
public Nullable<decimal> TimeToComplete { get; set; }
public bool AllowBackStep { get; set; }
public bool RandomizeAnswerOrder { get; set; }
public int Attempts { get; set; }
public virtual ICollection<exam_question> exam_question { get; set; }
}
public partial class exam_question
{
public exam_question()
{
this.exam_answer = new HashSet<exam_answer>();
}
public int ID { get; set; }
public int ExamID { get; set; }
public string QuestionText { get; set; }
public bool IsFreeForm { get; set; }
public virtual exam exam { get; set; }
public virtual ICollection<exam_answer> exam_answer { get; set; }
}
public partial class exam_answer
{
public int ID { get; set; }
public string AnswerText { get; set; }
public int QuestionID { get; set; }
public bool IsCorrect { get; set; }
public virtual exam_question exam_question { get; set; }
}
Save method:
[Route("SaveExam")]
[HttpPost]
public IHttpActionResult SaveExam(ExamViewModel examToSave)
{
using (var db = new IntranetEntities())
{
// try to locate the desired exam to update
var selectedExam = db.exams.Where(w => w.ID == examToSave.ID).SingleOrDefault();
if (selectedExam == null)
{
return NotFound();
}
// Redacted business logic
// Map the viewmodel to the domain model
Mapper.CreateMap<ExamAnswerViewModel, exam_answer>();
Mapper.CreateMap<ExamQuestionViewModel, exam_question>().ForMember(dest => dest.exam_answer, opt => opt.MapFrom(src => src.QuestionAnswers));
Mapper.CreateMap<ExamViewModel, exam>().ForMember(dest => dest.exam_question, opt => opt.MapFrom(src => src.ExamQuestions));
var viewmodel = Mapper.Map<exam>(examToSave);
// Update exam properties
selectedExam.ExamName = viewmodel.ExamName;
selectedExam.ExamDesc = viewmodel.ExamDesc;
selectedExam.AllowBackStep = viewmodel.AllowBackStep;
selectedExam.Attempts = viewmodel.Attempts;
selectedExam.RandomizeAnswerOrder = viewmodel.RandomizeAnswerOrder;
selectedExam.exam_question = viewmodel.exam_question; // DUPLICATES PROPS
// Save
db.SaveChanges();
return Ok(examToSave);
}
}

EntityFramework 5 - Conflicting changes to the role 'x' of the relationship 'x' have been detected

I have this model (Animal Model):
public int Id { get; set; }
public int AnimalSpecieId { get; set; }
public int AnimalBreedId { get; set; }
public Nullable<int> ProtectorId { get; set; }
public Nullable<int> OwnerId { get; set; }
public string Name { get; set; }
public virtual Owner Owner { get; set; }
public virtual Protector Protector { get; set; }
Protector Model:
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Phone { get; set; }
public string CellPhone { get; set; }
public string Email { get; set; }
public virtual ICollection<Animal> Animals { get; set; }
Owner Model:
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Phone { get; set; }
public string CellPhone { get; set; }
public string Email { get; set; }
public virtual ICollection<Animal> Animals { get; set; }
When I insert this model at the first time, if
ProtectorID = 1
and
OwnerID = null
it's ok, but, and I try to update this model, changing to:
OwnerID = 1
and
ProtectorID = null
I get the error in title, someone can help me with that ?
I don't agree with the above answer. I am not sure whether it solved your problem permanently because the issue is not related with the null value assignment. The actual reason is related with DBContext. When we go for any SaveChanges the context needs to be dispatched properly in order to proceed with the next SaveChanges to insert another record into DB on the same item with a different foreign key. You just need to add the below line after your "context.SaveChanges()"
context.Entry(your object).State = System.Data.Entity.EntityState.Detached;
This will solve the conflicts. Multiple insertion with same context results in conflicts.
Apologize if my comments criticised your answer in any manner.
I found the problem, after read this msdn post, I was thinking and found out what was happening, in my repository when I will update my entity, I was forgeting to set null all the related entities.
Old code:
var oldAnimal = context.Animals.Find(animal.Id);
if (oldAnimal != null)
{
oldAnimal.AnimalBreed = context.AnimalBreeds.Find(animal.AnimalBreed.Id);
oldAnimal.AnimalSpecie = context.AnimalSpecies.Find(animal.AnimalSpecie.Id);
oldAnimal.OwnerId = animal.OwnerId;
oldAnimal.ProtectorId = animal.ProtectorId;
oldAnimal.Castrated = animal.Castrated;
oldAnimal.DateBirth = animal.DateBirth;
oldAnimal.Gender = animal.Gender;
oldAnimal.Name = animal.Name;
oldAnimal.UpdateDate = DateTime.Now;
oldAnimal.Vaccinated = animal.Vaccinated;
oldAnimal.Weight = animal.Weight;
}
context.SaveChanges();
return animal;
new code:
var oldAnimal = context.Animals.Find(animal.Id);
if (oldAnimal != null)
{
oldAnimal.AnimalBreed = context.AnimalBreeds.Find(animal.AnimalBreed.Id);
oldAnimal.AnimalSpecie = context.AnimalSpecies.Find(animal.AnimalSpecie.Id);
oldAnimal.Owner = null;
oldAnimal.Protector = null;
oldAnimal.OwnerId = animal.OwnerId;
oldAnimal.ProtectorId = animal.ProtectorId;
oldAnimal.Castrated = animal.Castrated;
oldAnimal.DateBirth = animal.DateBirth;
oldAnimal.Gender = animal.Gender;
oldAnimal.Name = animal.Name;
oldAnimal.UpdateDate = DateTime.Now;
oldAnimal.Vaccinated = animal.Vaccinated;
oldAnimal.Weight = animal.Weight;
}
context.SaveChanges();
return animal;

Categories

Resources