Loading particular peoperties for entity and assigning it as reference to another entity - entity-framework

Example:
I am entering a new invoice. For this invoice I need to enter a customer. Lets assume that we retrieved a list of customers:
var list = Context.Set<Customer>().ToList();
Here I see two issues:
1) I do not need to bring all information for customer, I only need Id, Code and Name
2) Customer in current DbContext is read-only, so it would be nice if it is possible to tell DbContext not to monitor their states, to improve performance.
Questions:
1) Can we load only partial data for customer, but still be able to assign it to Invoice (see code bellow)?
2) Can we tell DbContext not to monitor Customers for changes, and still be able to do this:
Invoice.Customer = CustomerList[10];

There's not a direct way to do exactly what you want, but you might be able to achieve your goals with some compromise.
I do not need to bring all information for customer, I only need Id,
Code and Name
There isn't a way for EF to create a partially loaded entity, but you could create an anonymous type:
Context.Customers.Select(c => new {Id = c.CustomerId, Code = c.Code, Name = c.Name}).Tolist()
If you could live with the new anonymous type then use that, or you could then iterate through that list, creating actual customer objects.
Customer in current DbContext is read-only, so it would be nice if it
is possible to tell DbContext not to monitor their states, to improve
performance.
EF provides an Extension of AsNoTracking() which will do exactly what you're looking for:
var list = Context.Set<Customer>().AsNoTracking().ToList();
Depending on what you choose from above, the following code may change, but this code does achieve what you're looking for. Partially loads the customer, but still allows you to attach the customer to the invoice.
Note: You'll need to attach the customer to your context before you can use it, and then setting it to a state of Unchanged will prevent it from overwriting exiting data.
m = new Model();
var list = m.Customers.Select(c => new {Id = c.CustomerId, Code = c.Code, Name = c.Name});
List<Customer> customerList = new List<Customer>();
foreach (var item in list)
{
customerList.Add(new Customer()
{
CustomerId = item.Id,
Code = item.Code,
Name = item.Name
});
}
Invoice i = new Invoice();
var customer = customerList.First();
m.Customers.Attach(customer);
m.Entry(customer).State = EntityState.Unchanged;
i.Customer = customer;
m.Invoices.Add(i);
m.SaveChanges();

Related

Entity-framework doesn't save changes to database

I have a problem with saving changes to the database with entity framework. I'm using three tables; AspNetUsers, tblCountry, and tblCountry_AspNetUsers.
tblCountry_AspNetUsers consists of two columns; UserId and CountryId, which creates a one-to-many relationship between tblCountry and AspNetUsers.
Currently, what I want to do is change the country of a specific user, but entity framework doesn't let me access the tblCountry_AspNetUsers database, and instead creates an ICollection of AspNetUsers on tblCountry. I could assign the Id on the AspNetUser directly, but I don't want to start adding/removing columns from identity tables just yet since I'm using database first, and I've heard it can lead to problems.
Anyway, I can remove just fine from the ICollection and save those changes to the database, but when I try to add the same user to a different country, it doesn't save to the database properly, but I can find the user in the context object when debugging.
I've tried attaching and changing the entitystate to both added and modified, but when I try to do this, it breaks out of the method and doesn't update the database. (basically it freezes when I try to attach)
My code for editing a user looks like the following:
(Note that UserManager handles the identity usermodel, ApplicationUser,, which is not the same as AspNetUsers in this aspect)
(Also, tblCountry.AspNetUsers refers to the ICollection of users assigned to a specific country)
...
var aspuser = new AspNetUsers();
using (DbContext dc = new DbContext())
{
aspuser = dc.AspNetUsers.First(x => x.Id == userid);
var user = await UserManager.FindByIdAsync(userid);
user.Email = updatedUser.UserName;
user.UserName = updatedUser.Email;
var result = await UserManager.UpdateAsync(user);
aspuser.tblCountry.AspNetUsers.Remove(aspuser);
await dc.SaveChangesAsync();
}
using (DbContext dc = new DbContext())
{
var c = _country.GetById(newcountryid);
c.AspNetUsers.Add(aspuser);
await dc.SaveChangesAsync();
}
return Users(userid);
}
It would be extremely easy if it was a table I could access directly, but with an ICollection like this I'm confused as to what I should do to make it work, and I appreciate any input!
Cheers
This line aspuser = dc.AspNetUsers.First(x => x.Id == userid); is inside using (DbContext dc = new DbContext()) which means that object aspuser is detached from context when you leave that block. You should do:
var c = _country.GetById(newcountryid);
c.AspNetUsers.Add(aspuser);
await dc.SaveChangesAsync();
in the same using block as the above code, why did you separate it?

Update an entity but not the null value properties

I have a customer which I want to update in the repository.
var customer = new Customer{ Name = "Test" }
The customer has still more properties which are null, because I have not loaded them before to the client. Thus these properties have all their default values like null or 0.
What do I have to do with latest EF 6, that only the property Name is updated and the other properties from the customer are not overwritten?
1.) How would I have to query/update the customer?
2.) What would happen if the customer has a collection of and he has changed some properties of some meetings - but not all properties - how would then be the override behavior?
UPDATE pseudo code
Open context
Get customer
Close context
Open context
Update customer.name
SAveChanges
Close context
The custom.name is not saved, why?
You can't do it natively without getting the whole entiy first, which is obviously 2 db hits.
There is however an extension you can use that does what you want:
https://github.com/loresoft/EntityFramework.Extended/wiki/Batch-Update-and-Delete
Assuming you already have your model generated and it has a name of "MyEntities" and you should have a customer ID if you are updating an existing. Here is a solution using c#.
using(MyEntities db = new MyEntities()) {
//this will retreieve the customer based on ID
Customer cust = db.Customers.FirstOrDefault(c => c.CustomerID == custID);
//you can update each column
cust.Name = "Test";
//save the changes to the entity
db.SaveChanges();
}

Having a hard time with Entity Framework detached POCO objects

I want to use EF DbContext/POCO entities in a detached manner, i.e. retrieve a hierarchy of entities from my business tier, make some changes, then send the entire hierarchy back to the business tier to persist back to the database. Each BLL call uses a different instance of the DbContext. To test this I wrote some code to simulate such an environment.
First I retrieve a Customer plus related Orders and OrderLines:-
Customer customer;
using (var context = new TestContext())
{
customer = context.Customers.Include("Orders.OrderLines").SingleOrDefault(o => o.Id == 1);
}
Next I add a new Order with two OrderLines:-
var newOrder = new Order { OrderDate = DateTime.Now, OrderDescription = "Test" };
newOrder.OrderLines.Add(new OrderLine { ProductName = "foo", Order = newOrder, OrderId = newOrder.Id });
newOrder.OrderLines.Add(new OrderLine { ProductName = "bar", Order = newOrder, OrderId = newOrder.Id });
customer.Orders.Add(newOrder);
newOrder.Customer = customer;
newOrder.CustomerId = customer.Id;
Finally I persist the changes (using a new context):-
using (var context = new TestContext())
{
context.Customers.Attach(customer);
context.SaveChanges();
}
I realise this last part is incomplete, as no doubt I'll need to change the state of the new entities before calling SaveChanges(). Do I Add or Attach the customer? Which entities states will I have to change?
Before I can get to this stage, running the above code throws an Exception:
An object with the same key already exists in the ObjectStateManager.
It seems to stem from not explicitly setting the ID of the two OrderLine entities, so both default to 0. I thought it was fine to do this as EF would handle things automatically. Am I doing something wrong?
Also, working in this "detached" manner, there seems to be an lot of work required to set up the relationships - I have to add the new order entity to the customer.Orders collection, set the new order's Customer property, and its CustomerId property. Is this the correct approach or is there a simpler way?
Would I be better off looking at self-tracking entities? I'd read somewhere that they are being deprecated, or at least being discouraged in favour of POCOs.
You basically have 2 options:
A) Optimistic.
You can proceed pretty close to the way you're proceeding now, and just attach everything as Modified and hope. The code you're looking for instead of .Attach() is:
context.Entry(customer).State = EntityState.Modified;
Definitely not intuitive. This weird looking call attaches the detached (or newly constructed by you) object, as Modified. Source: http://blogs.msdn.com/b/adonet/archive/2011/01/29/using-dbcontext-in-ef-feature-ctp5-part-4-add-attach-and-entity-states.aspx
If you're unsure whether an object has been added or modified you can use the last segment's example:
context.Entry(customer).State = customer.Id == 0 ?
EntityState.Added :
EntityState.Modified;
You need to take these actions on all of the objects being added/modified, so if this object is complex and has other objects that need to be updated in the DB via FK relationships, you need to set their EntityState as well.
Depending on your scenario you can make these kinds of don't-care writes cheaper by using a different Context variation:
public class MyDb : DbContext
{
. . .
public static MyDb CheapWrites()
{
var db = new MyDb();
db.Configuration.AutoDetectChangesEnabled = false;
db.Configuration.ValidateOnSaveEnabled = false;
return db;
}
}
using(var db = MyDb.CheapWrites())
{
db.Entry(customer).State = customer.Id == 0 ?
EntityState.Added :
EntityState.Modified;
db.SaveChanges();
}
You're basically just disabling some extra calls EF makes on your behalf that you're ignoring the results of anyway.
B) Pessimistic. You can actually query the DB to verify the data hasn't changed/been added since you last picked it up, then update it if it's safe.
var existing = db.Customers.Find(customer.Id);
// Some logic here to decide whether updating is a good idea, like
// verifying selected values haven't changed, then
db.Entry(existing).CurrentValues.SetValues(customer);

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.

How do you wrap a circular reference operation in a single transaction using Entity Framework 4?

I have a table of Persons and a table of Things, where each Thing is owned by a Person and each Person has a FavoriteThing.
Persons
PersonID int <PK>
FavoriteThingID int <FK>
Things
ThingID int <PK>
PersonID int <FK>
I would like to be able to add a Person and his/her favorite Thing, as well as setting that Thing's PersonID to the new Person, in a single transaction, without having DTC promote the transaction to distributed. Wrapping the operation in a TransactionScope() and manually managing the Entity connection does not appear to work:
ThingEntities ent = new ThingEntities();
using (TransactionScope scope = new TransactionScope())
{
ent.Connection.Open();
Thing t = ent.CreateObject<Thing>();
ent.Things.AddObject(t);
ent.SaveChanges(false);
Person p = ent.CreateObject<Person>();
t.Person = p;
ent.Persons.AddObject(p);
p.FavoriteThing = t;
ent.SaveChanges(false);
scope.Complete();
ent.AcceptAllChanges();
ent.Connection.Close();
}
This results in a "Unable to determine a valid ordering for dependent operations." exception on the second SaveChanges() call.
Is there a simple way to do this?
Thanks
You don't need TransactionScope for this. You can do it in one call to SaveChanges, which means one transaction.
Person p = ent.CreateObject<Person>();
p.FavoriteThing = ent.CreateObject<Thing>();
ent.Persons.AddObject(p);
ent.SaveChanges();