How to avoid parent entities beeing reinserted in EF6 - dealing with many to many relationship? - entity-framework

Im new to EF6 and facing the following issue:
If I add my model via db.Modelclass.Add( model )-method and save the changes to the db new entities will be created in the tables Tours and ToursEmployees like it should for the model, but also in my parent table Employees. Last one is my use in the code below.
I already read this article about the issue https://msdn.microsoft.com/en-us/magazine/dn166926.aspx but in my case I dont know what to do, cause Im dealing with a many-to-many realtionship. I used code first and marked the artibutes in the models via virtual as navigation properties, so the framework created me a join table.
Whats the right way to use the framework for this kinda relaltionships, maybe creating a model for the joined table and then using the FK described in the article?
public ActionResult Create(TourViewModel tourVM) {
if (ModelState.IsValid) {
Tour tour = new Tour() {
Tourname = tourVM.Tourname,
Tourdate = tourVM.Tourdate,
VehicleId = tourVM.VehicleId
,Employees = new List<Employee>()
};
for (var i = 0; i < tourVM.Employees.Count; i++)
if (tourVM.Employees[i].Assigned)
tour.Employees.Add(
new Employee() { EmployeeId = tourVM.Employees[i].EmployeeId }
);
db.Tours.Add(tour);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(tourVM);
}

I believe this is happening because you are adding the employees to the list that are not bound to the data context. Doing something like the below should work and correctly bind them to the data context object.
public ActionResult Create(TourViewModel tourVM) {
if (ModelState.IsValid) {
Tour tour = new Tour() {
Tourname = tourVM.Tourname,
Tourdate = tourVM.Tourdate,
VehicleId = tourVM.VehicleId,
Employees = new List<Employee>()
};
var IDs = tourVM.Employees.Where(e => e.Assigned).Select(e => e.EmployeeId);
var Employees = db.Employees.Where(e => IDs.Contains(e.EmployeeId)).ToList();
tour.Employees.AddRange(Employees);
db.Tours.Add(tour);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(tourVM);
}

Related

Alternatives to splitting tables Entity Framework 6 - VARBINARY(MAX)

Solution :
The idea is that the loading problem was from the table files. So in order to get everything in order, I had to turn off lazy load, and load the navigation property from the entity with the blob instead of the other way around.
public FileDto[] RetrieveFilesWithUserId(string id)
{
FileDto[] files;
logger.Info("Looking for files #{0}", id);
using (var db = ContextFactory.GetEntities())
{
db.Configuration.LazyLoadingEnabled = false;
logger.Debug("User retrieved !");
files = db.Files.Include("Users").Where(f => f.Users.Count(user1 => user1.Id == id) > 0).Select(
x =>
new FileDto
{
ContentType = x.ContentType,
Id = x.Id,
Name = x.Name,
Type = db.TypeFiles.FirstOrDefault(b => b.Id == x.Type).Description
}).ToArray();
db.Configuration.LazyLoadingEnabled = true;
}
logger.Info("done looking for files");
return files;
}
Problem was:
I have an issue which seems quite classic.
I have a table with one large blob, and when I load it I have huge performance issues (due to loading large amount of data).
I have users, those users have files and in my file entity I have the datas.
Relations are many to many.
Easy fix: I do some table splitting, and all my problems are solved.
Problem: My boss doesn't want me to do that (I argued that this was the best option).
So, how can I do differently ?
I thought about requiring directly but I don't have access to the intermediate tables (they are navigation properties now.)
Would a procedure make the trick ?
You can use projection to get all fields you want without blob:
var people = context.People.Select(p => new PersonDTO { Id = p.Id, Name = p.Name });
Where PersonDTO is like:
class ProductDTO
{
public int Id { get; set; }
public string Name { get; set; }
}
DTO means Data Transfer Object and it is often used for such tasks.

Delete data sith Breeze.js without loading it to client

I am using Breeze.js with Entity Framework WebAPI backend, and I need to delete a large set of data that is not loaded to client. I would really like to do it on the server and not load it.
Is there a "breeze way"? By that I mean a method in a BreezeController.
EDIT
I have to delete all rows from one table that belong to the user, whose date field is in future, and all their child rows.
public override int SaveChanges()
{
foreach (
var entry in
this.ChangeTracker.Entries()
.Where((e => (e.State == (EntityState) Breeze.WebApi.EntityState.Deleted))))
{
if (entry.Entity.GetType() == typeof(User))
{
var entity = entry.Entity as User;
var childEntitiesInFuture = ChildEntities.Where(c => c.DateField > DateTime.Now);
foreach (var child in childEntitiesInFuture){
var grandchildrenForDeletion = Grandchildren.Where(c => c.ChildId == child.Id);
foreach (var g in grandchildrenForDeletion) Grandchildren.Remove(g);
ChildEntities.Remove(child);
}
}
}
}
Assuming you are deleting User, one User has many ChildEntity saved in ChildEntities and each ChildEntity has many Grandchild saved in Grandchildren. A bit messy names, but that's what you get with no real names :)
This method goes into your Context class. Good luck.

How to update only modified values (EntityFramework 5.0)?

I have this entity, want to update using entityframework
EmployeeModel employee = new EmployeeModel
{
Id = 1000, //This one must
FirstName = modifiedValue,
Email = modifiedValue,
LastName = originalValue,
Phone = originalValue
};
Code to update
_db.ObjectStateManager.ChangeObjectState(employee, EntityState.Modified);
_db.SaveChanges();
This is the SQL statement got once updated
Update Employee set Id=1138,FirstName='modifiedValue',Email='modifiedValue',LastName= 'OriginalValue',phone='originalValue' where Id=1138
But I am expecting this
Update Employee set FirstName='modifiedValue', Email='modifiedValue' where Id=1138.
I dont know what I am missing here. Please let me know.
This problem is common when dealing with DTOs. An employee entity is fetched from the database, mapped to a DTO and sent over the wire. The client then modifies this DTO and sends it back to the server.
When you touch (set) a property on an EF entity, EF will assume that the value has been changed. Even if the old value and the new value are exactly the same.
The same problem occurs when you map the DTO to a new Entity and attach it to EF and updating its status to 'Modified'.
Using AutoMapper:
// This will result in the full update statement
var employee = AutoMapper.Mapper.Map<EmployeeDto, Employee>(dto);
// This will result in a smaller update statement (only actual changes)
var employee = dbContext.Employees.Find(dto.Id);
AutoMapper.Mapper.Map(dto, employee);
Or, manually (I would avoid doing this, but just for the sake of completeness):
// This will result in a smaller update statement (only actual changes)
var employee = dbContext.Employees.Find(dto.Id);
if (employee.Email != dto.Email )
employee.Email = dto.Email;
There are probably some other ways for dealing with this problem... but using AutoMapper together with Entity Framework correctly is definitely one of the easiest ways.
This is the solution I got
var entity = _db.CreateObjectSet<Employee>();
entity.Detach(employee);
entity.Attach(employee);
foreach (string modifiedPro in employeeModel.ModifiedProperties){
_db.ObjectStateManager.GetObjectStateEntry(employee).SetModifiedProperty(modifiedPro);}
_db.SaveChanges();
Only modified values in the sql update statement
Update Employee set FirstName='modifiedValue', Email='modifiedValue' where Id=1138.
If anybody knows better answer than this, Please post your suggestions
You can try this way
public update(Person model)
{
// Here model is model return from form on post
var oldobj = db.Person.where(x=>x.ID = model.ID).SingleOrDefault();
var UpdatedObj = (Person) Entity.CheckUpdateObject(oldobj, model);
db.Entry(oldobj).CurrentValues.SetValues(UpdatedObj);
}
public static object CheckUpdateObject(object originalObj, object updateObj)
{
foreach (var property in updateObj.GetType().GetProperties())
{
if (property.GetValue(updateObj, null) == null)
{
property.SetValue(updateObj,originalObj.GetType().GetProperty(property.Name)
.GetValue(originalObj, null));
}
}
return updateObj;
}

In Entity Framework, take a newly created object and use it to update an existing record

Here's what I'd like to do:
var myCustomer = new Customer();
myCustomer.Name = "Bob";
myCustomer.HasAJob = true;
myCustomer.LikesPonies = false;
Then I'd like to pass it into an update method:
public UpdateCustomer(Customer cust)
{
using(var context = dbcontext())
{
var dbCust = context.Customers.FirstOrDefault(c => c.Name == cust.Name);
if(dbCust != null)
{
// Apply values from cust here so I don't have to do this:
dbCust.HasAJob = cust.HasAJob;
dbCust.LikesPonies = cust.LikesPonies
}
context.SaveChanges();
}
}
The reason for this is I'm working in multiple different parts of my application, and/or across DLLs. Is this possible?
EDIT: Found this question to be immensely useful:
Update Row if it Exists Else Insert Logic with Entity Framework
If you are sure that the entity is in the database and you have key you would just Attach the object you have to the context. Note that attached entities are by default in Unchanged state as the assumption is that all the values of properties are the same as in the database. If this is not the case (i.e. values are different) you need to change the state of the entity to modified. Take a look at this blog post: http://blogs.msdn.com/b/adonet/archive/2011/01/29/using-dbcontext-in-ef-feature-ctp5-part-4-add-attach-and-entity-states.aspx it describes several sceanrios including the one you are asking about.

Update a single property of a record in Entity Framework Code First

How can I update a single property of a record without retrieving it first?
I'm asking in the context of EF Code First 4.1
Says I have a class User, mapping to table Users in Database:
class User
{
public int Id {get;set;}
[Required]
public string Name {get;set;}
public DateTime LastActivity {get;set;}
...
}
Now I want to update LastActivity of a user. I have user id. I can easily do so by querying the user record, set new value to LastActivity, then call SaveChanges(). But this would result in a redundant query.
I work around by using Attach method. But because EF throws a validation exception on Name if it's null, I set Name to a random string (will not be updated back to DB). But this doesn't seem a elegant solution:
using (var entities = new MyEntities())
{
User u = new User {Id = id, Name="this wont be updated" };
entities.Users.Attach(u);
u.LastActivity = DateTime.Now;
entities.SaveChanges();
}
I would be very appriciate if someone can provide me a better solution. And forgive me for any mistake as this is the first time I've asked a question on SO.
This is a problem of validation implementation. The validation is able to validate only a whole entity. It doesn't validate only modified properties as expected. Because of that the validation should be turned off in scenarios where you want to use incomplete dummy objects:
using (var entities = new MyEntities())
{
entities.Configuration.ValidateOnSaveEnabled = false;
User u = new User {Id = id, LastActivity = DateTime.Now };
entities.Users.Attach(u);
entities.Entry(user).Property(u => u.LastActivity).IsModified = true;
entities.SaveChanges();
}
This is obviously a problem if you want to use the same context for update of dummy objects and for update of whole entities where the validation should be used. The validation take place in SaveChanges so you can't say which objects should be validated and which don't.
I'm actually dealing with this right now. What I decided to do was override the ValidateEntity method in the DB context.
protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
var result = base.ValidateEntity(entityEntry, items);
var errors = new List<DbValidationError>();
foreach (var error in result.ValidationErrors)
{
if (entityEntry.Property(error.PropertyName).IsModified)
{
errors.Add(error);
}
}
return new DbEntityValidationResult(entityEntry, errors);
}
I'm sure there's some holes that can be poked in it, but it seemed better than the alternatives.
You can try a sort of hack:
context.Database.ExecuteSqlCommand("update [dbo].[Users] set [LastActivity] = #p1 where [Id] = #p2",
new System.Data.SqlClient.SqlParameter("p1", DateTime.Now),
new System.Data.SqlClient.SqlParameter("p2", id));