Update one to many table Entity Framework - c#

I have a create User ASP.NET MVC 3 page - on the page there is a multiselect box of cars which is prepoulated from a Cars table in my DB. On save User I return a list of ints of CarIds that the user is associated with. I am using WCF services and Entity Framework 5.0 to get my data back to SQL Server and save to SQL server.
I then have a Table in my DB CarUser - it just contain a user Id and a client id - two FK's to the UserId in User Table and the CarId in the Cars table.
When I create a User it is updating the CarUser table is getting updated correctly (so for e.g - it might look like this)
CarId UserId
2 4
3 4
4 4
5 4
So it is looking correct in that my CarUser table is showing User 4 associated with the 4 CarIds that were seleted from the Multi Select box. However the problem I am having is that it is re-creating the Cars in the cars table - so it is creating Car Id 9,10,11,12 for example but the details of them are exactly the same as 2,3,4,5
The code I have wrote for this is below:
public User User_Create(User user, List<int> carIds)
{
DAL.CarUserWCFFServiceImpl carUser = new DAL.CarUserWCFFServiceImpl();
// cal the DAL layer to do the create
User newUser = carUser.User_Create(user);
//call the DAL layer to create the cars linked to a user
carUser.CarUserMapping_Create(newUser.UserID, carIds);
so my first DAL method (which is a layer below the one this code is listed from) call creates the User - then once I have the user so I have the ID, etc I call another method at the DAL layer passing in the UserID of the newly created User and then the list of carIds associated with the User.
The code from the 2nd method is below:
public void CarUserMapping_Create(int userId, List<int> carIds)
{
using (CUEntities entities = new CUEntities())
{
User user = User_GetById(userId);
entities.Users.Attach(userId);
foreach (int carId in carIds)
{
Car car = Car_GetById(carId);
user.Cars.Add(car);
entities.ObjectStateManager.ChangeObjectState(user, System.Data.EntityState.Modified);
entities.SaveChanges();
}
}
}
Can anyone see something I am doing wrong? My other option I am thinking is take this away from Entity Framework and just call a stored procedure in CarUserMapping_Create

Maybe you need to add do the following:
public void CarUserMapping_Create(int userId, List<int> carIds)
{
using (CUEntities entities = new CUEntities())
{
User user = User_GetById(userId);
entities.Users.Attach(userId);
foreach (int carId in carIds)
{
Car car = Car_GetById(carId);
car.UserID = user.UserID;
entities.Entry(car).State = EntityState.Modified;
entities.SaveChanges();
}
}
}

You shuold not set the state like that. thats like raping the framework.
Instedad send the model instance (entities) down as a paramater to the methods that reads the reads the car and user. (and ofc. use the entitiesas the ObjectContext in the methods)
like this:
using (CUEntities entities = new CUEntities())
{
User user = User_GetById(entities , userId);
foreach (int carId in carIds)
{
Car car = Car_GetById(entities, carId);
car.UserID = user.UserID;
entities.SaveChanges();
}
}
that way, the entity framework will keep tract of the entity state

Related

Entity Framework insert sublist into parent sublist

I try to insert a list of Answers into Questions which in return it is inserted into Exams, all my code works just fine except one part, which is inserting new Answers.
Data is inserted just fine except Answers' data, whose data is not stored in the database, plus I try to get QuestionId so I can store it with Answer as a foreign key and I failed in that too.
API Controller
public IActionResult addExam([FromBody] Exams exam)
{
try
{
if (exam == null)
{
return StatusCode(401, "data is null");
}
var userId = this.help.GetCurrentUser(HttpContext);
Exams exams = new Exams
{
Name = exam.Name,
Number = exam.Number,
FullMarck = exam.FullMarck,
CreatedBy = userId,
CreatedOn = DateTime.Now,
Status = exam.Status
};
db.Exams.Add(exams);
var questionsList = new List<Questions>();
foreach (Questions item in exam.Questions)
{
var question = new Questions
{
ExamId = exam.Id,
Points = item.Points,
CreatedBy = userId,
CreatedOn = DateTime.Now,
Status = item.Status,
};
questionsList.Add(question);
}
exams.Questions = questionsList;
db.SaveChanges();
foreach (Questions item in exam.Questions)
{
var answersList = new List<Answers>();
foreach (Answers answers in item.Answers)
answersList.Add(new Answers
{
QuestionId = item.Id,
ExamAnswers = answers.ExamAnswers,
CreatedBy = userId,
CreatedOn = DateTime.Now
});
item.Answers = answersList;
}
db.SaveChanges();
return Ok("successfully created ");
}
catch (Exception e)
{
return StatusCode(500, e.InnerException.Message);
}
}
The way you build your graph is a bit unusual. I would have expected it more like a single set of nested loops that takes your supplied model and populates entity collections without forced ids. EF will track the IDs; you don't need to worry about them; when you add a new Answer to a particular question.Answers collection, you don't need to tell the Answer what it's QuestionID is; EF knows based on which question it was added to. If the ID for a question is not yet set, because it is generated by the db and no save has occurred, then saving the question will generate an ID and EF will ripple the change out to all the owned Answers in the question.Answers; you don't need to micro manage it
Here's a pseudo code of how I would expect it to go:
//model is an ExamModel
Exam e = new Exam(); //exam is a db entity
e.Title = model.ExamTitle; //model is not a db entity
foreach(QuestionModel mq in model.Questions){ //enumerate all the questionmodel we got from the front end and build a db entity graph
Question q = new Question(); //make new db entity
q.Subject = mq.QuestionHeader; //set entity property from model
q.Body = mq.BodyText; //set property from model
if(e.Questions == null) //not sure how your entities are declared, if this is already done elsewhere, remove it
e.Questions = new List<Question>();
e.Questions.Add(q); //add the question db entity to the exam db entity
//notice I didn't set the question id. EF will do that- it knows what exam this question belongs to
foreach(AnswerModel ma in mq.Answers){ //while our question db entity called q is still in scope let us add the related answers to it
Answer a = new Answer(); //create EF entity
a.Text = ma.AnswerText; //set db entity property from model property
if(q.Answers == null)
q.Answers = new List<Answer>();
q.Answers.Add(a); //add the answer to the question db entity
}
}
//exam e now has a collection of questions that have each a collection of answers, save it to the db
db.Exams.Add(e);
db.SaveChanges(); //only need one call to save changes on the whole graph
I think the way you've split your operations up hasn't created a connected graph of entities and/or the way you've forced the questionids of answers means EF hasn't kept the relationship to date upon saving.
You should also have a separation between the data objects arriving in your controller (I've called these ModelExam, ModelQuestion, ModelAnswer) and the entities in your EF (I've called these Exam, Question, Answer - yours are plural). This separation is achieved by having different classes for your front end controllers etc to use than your back end db context uses. At first it looks like things are being repeated for no good reason but eventually the system will become complex enough that not every db property can or should be exposed all the way to the front end and back, and the front end might need calculated or other non db based data. At this point you really need your front end data models to be completely separate things from your back end data entities
I think you need to call db.SaveChanges() after adding new exam. In your case exam id is not auto generated and it is always 0 , so you cant save question with examid 0;
item.Answers = answersList; is false
exam.Questions.Answers = answersList is true
Remove the first db.SaveChanges(); witch is before last foreach loop. This will insert all your data at once and should do the job.

Is It Possible To Dispose a LinQtoSQL Object from the Dataclass to Counter Data Concurrency?

this is When a new record is inserted From Client A into server :
var Invoice = new Invoice();
foreach (DataGridViewRow item in DataGridView1.Rows)
{
Invoice.InvoiceDetails.Add(new InvoiceDetail
{
Description = txt1.Text,
TotalPrice = txt2.Text
});
}
dc.Invoices.InsertOnSubmit(Invoice);
dc.SubmitChanges();
ok now imagine client 2 is going to edit this record.
var Invoice = dc.Invoices.First(p => p.ID == _InvoiceID);
dc.InvoiceDetails.DeleteAllOnSubmit(Invoice.InvoiceDetails);
foreach (DataGridViewRow item in DataGridView1.Rows)
{
Invoice.InvoiceDetails.Add(new InvoiceDetail
{
Description = "Description",
TotalPrice = "10000000"
});
}
dc.SubmitChanges();
now here is the thing.Client one has the first 2 invoices that IT inserted itself still residing in its DataClass.
meaning the two clients have two different DataClasses Localy on RAM.
problem starts when client 1 Goes For another edit AGAIN :
When Selecting the Invoice again using :
var Invoice = dc.Invoices.First(p => p.ID == _InvoiceID);
currently this invoice on client 1 has the 2 it added itself to the db,still on its dc.so with this select it will also add the 2 from the DB that the client 2 added from the server.
so the problem is client 1 will show you 4 Details instead of 2,containing 2 from the Server system,and 2 from its local DataClass.
i just got informed that its called Data Concurrency.
Please note that the data context is static and public and is kept alive through the whole applications lifetime. I know this is wrong and it has to be kept short but the data is massive and i cant just make a new data context on every form.
Looking for solutions as im already switching to entity framework for the fix but i need to apply a solution to the current state of the application untill im done switching to the entity framework.
Also one more thing, the app creates its database so sprocs are not an option as t linq cant create em with createdatabase().
You can stop your DataContext getting stale if you instantiate it as required:
using (Entities dc = new Entities())
{
var Invoice = dc.Invoices.First(p => p.ID == _InvoiceID);
dc.InvoiceDetails.DeleteAllOnSubmit(Invoice.InvoiceDetails);
foreach (DataGridViewRow item in DataGridView1.Rows)
{
Invoice.InvoiceDetails.Add(new InvoiceDetail
{
Description = "Description",
TotalPrice = "10000000"
});
}
dc.SubmitChanges();
}
For some background, see, for example:
Instantiating a context in LINQ to Entities
Working with DbContext
Editted after explanation about static DbContext
The alternative is to put all SQL in stored procedures, for both clients; then you are not relying on the state of the DbContext.

Why is the Entity Framework System.Data.Entity.Core.Objects.RelationshipEntry Wrong? ( For Change Tracking)

All:
Our company is working on a ASP.NET application that uses Entity Framework.
The Entity Framework Model First approach was taken to create the database tables and POCOs
Moreover, we are using Effort Testing Tool for Entity Framework and NUnit Framework in order to conduct Unit Testing.
At present, we are trying some CRUD operations on Customer and Account Business Entities ASP.NET application.
Every Customer has the option of having at most one account.
Here is how the relationship between the 2 tables looks like in a Database Diagram:
Our application also keeps track of changed tracking of entities and relationships between entities.
We have the following code example with populated Customer and Account business entries:
Customer custFour = new Customer { CustomerID = "4 ", Orders = new HashSet<Order>(), CustomerDemographics = new HashSet<CustomerDemographic>(), CompanyName = "FC Barcelona", ContactName = "Lionel Messi", ContactTitle = "current top basket ball player", Address = "344 testing street", City = "Buenos Aires", Region = "Oklahoma", PostalCode = "4498", Country = "Argentina", Phone = "2344949494", Fax = "33325" };
Account acctFour = new Account { UserName = "lmessi", Password = "myteamlostworldcupmatch", Customer = custFour, AccountRoleMaps = new HashSet<AccountRoleMap>() };
To keep it brief, we also invoke a line of code like the following so that the Entity Framework DbContext can save the Customer and Account entities mentioned above, and also the relationship between the Customer and Account entities:
_context.Set<Account>().Add(acctFour);
_context.SaveChanges();
As the point of execution goes on, we ultimately reach the following excerpt of code:
List<System.Data.Entity.Core.Objects.ObjectStateEntry> allAddModDelItems = ((IObjectContextAdapter)_context).ObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified | EntityState.Deleted).ToList();
foreach (var osEnt in allAddModDelItems)
{
}
The above excerpt of code retrieves all the Objects and Relationships between Objects that have been Added, Modified and Deleted.
I already checked the System.Data.Entity.Core.Objects.EntityEntry for the Customer and the System.Data.Entity.Core.Objects.EntityEntry for the Account, and they have Valid Business Data.
Sadly, the System.Data.Entity.Core.Objects.RelationshipEntry has Entity as null and the EntityKey as null which seems to be wrong because the System.Data.Entity.Core.Objects.RelationshipEntry should reflect the relationship between the Account and Customer objects that I created.
Why is the System.Data.Entity.Core.Objects.RelationshipEntry Wrong? In other words, Why are the System.Data.Entity.Core.Objects.RelationshipEntry's Entity and the EntityKey properties null?
http://msdn.microsoft.com/en-us/library/system.data.objects.objectstateentry.entity%28v=vs.100%29.aspx
The Entity property returns null when the ObjectStateEntry is associated with a RelationshipSet entry instead of an entity type instance.
Therefore, it makes sense for Entity and the EntityKey properties to be null

How can I ensure that Entity Framework will create an association in a many-to-many relationship instead of a new record?

I have a problem with Entity Framework.... I don't know it well.
I am working on a seemingly simple task of associating an existing contact to a new order in a system I'm working on. Contacts will always exist prior to being associated to the order.
Simplified database tables...
Order
OrderID
Status
Created
Contact
ContactID
FirstName
LastName
ContactToOrderMap
OrderID
ContactID
I've linked these two tables in the DbContext.
modelBuilder.Entity<Order>()
.HasMany<Contact>(a => a.Contacts)
.WithMany()
.Map(a =>
{
a.ToTable("ContactToOrderMap");
a.MapLeftKey("OrderID");
a.MapRightKey("ContactID");
});
I wrote a unit test that simply creates an order, a contact, adds the contact to a list member on the order and attempts to save.
// setup
var order = new Order() { // initialize required fields };
// add a contact that already exists in the database
var orderContact = new Contact() { ID = 2 };
order.Contacts.Add(orderContact);
// call
logic.Save(ref order);
Eventually this order is saved after some unrelated business logic is performed...
// Add to collection
_db.Orders.Add(obj);
// Commit changes
_db.SaveChanges();
>> Here I receive errors related to required database fields on the Contact table. Not what I'm looking for as I really just want to add an association record in the ContactToOrderMap table.
As a next step, I tried to retrieve the existing contacts from the database prior to saving the order. This code's not super clean, but it should explain what I'm attempting.
if (currentOrder.Contacts != null)
{
var matchedContacts = new List<Contact>();
foreach (var con in currentOrder.Contacts)
{
matchedContacts.Add(_contactLogic.Get(con.ID));
}
currentOrder.Contacts.Clear();
foreach (var item in matchedContacts)
{
currentOrder.Contacts.Add(item);
}
}
This created the association, but created a new Contact (new unique ID) as well.
The goal is to create an association and a new order only, not create a new contact.
Any ideas or pointers to get me going in the right direction?
Please let me know if additional information is required.
Edit: Fixed code - renamed obj to currentOrder.
After the line
var orderContact = new Contact() { ID = 2 };
do
_db.Contacts.Attach(orderContact);
Now the contact is part of the context in an Unchanged state, and
order.Contacts.Add(orderContact);
will not change its state to Added anymore.

EF - Navigation property explanation

I use EF 4 and Membership Provider Shipped with ASP.Net 4.0.
I need find all Users in a Specific Role and Populate a DropDownList using List<ListItem>.
DataBase Tables involved are:
aspnet_Roles
aspnet_Users
aspnet_UsersInRoles
Navigation Property from aspnet_Users to aspnet_Roles (using junction table aspnet_UsersInRoles) is:
myUser.aspnet_Roles
Matching RoleID (Guid) "CE44ED48-E9F9-49C6-9E15-E40EEFDC7479")
My Script:
using (CmsConnectionStringEntityDataModel context = new CmsConnectionStringEntityDataModel())
{
IQueryable<aspnet_Users> userQuery = from aspnet_Users in context.aspnet_Users select aspnet_Users;
IQueryable<aspnet_Roles> roleQuery = from aspnet_Roles in context.aspnet_Roles select aspnet_Roles;
List<ListItem> myListUsersInRoles = new List<ListItem>();
foreach (aspnet_Users myUser in userQuery)
{
// PROBLEM HERE
if (myUser.aspnet_Roles.ToString() == "CE44ED48-E9F9-49C6-9E15-E40EEFDC7479")
myListUsersInRoles.Add(new ListItem(myUser.UserName.ToString(), myUser.UserId.ToString()));
uxListUsers.DataSource = myListUsersInRoles;
uxListUsers.DataBind();
}`
Problems:
IF return FALSE always, so I am not able to populate List<>.
I suppose I am doing wrong in if an some Properties.
Do you have any ideas? Thanks for your time.
myUser.aspnet_Roles is an EntitySet<aspnet_Roles> (in other words, a collection of roles, so it's highly unlikely that it's string representation happens to match the string representation of the role you're after's ID.
There are several ways you could fix it. One is:
// ...
if (myUser.aspnet_Roles.Any(r => r.RoleId.ToString() == "CE44ED48-E9F9-49C6-9E15-E40EEFDC7479"))
// ...
Also note that from y in x select y is just the same as x, so you could write
foreach (aspnet_User myUser in context.aspnet_Users)
// ...
And not bother declaring userQuery or roleQuery. (You don't actually seem to be using roleQuery anywhere anyway...)
Also, you could look at using the membership/roles API to fetch users in a role:
using System.Web.Security; // at the top of your file
var usersInRole = Roles.GetUsersInRole("name of role here, NOT the guid");
Note that this returns an array of usernames, not user objects, so you'd have to do a little extra work to get the User ID, but it is something you could think about...

Categories

Resources