I have a many-to-many relation Patients - PatientDevices - Devices and a basic edmx-model of it (no poco, automatic generation). PatientDevices is generated as an entity, because it has more columns than the foreign keys.
When I create two ObjectContexts and add a new PatientDevice into the first one, the second one has it also. When deleting this relation from the first one, it is still in the second context:
var entities1 = new TherapyDatabaseDevEntities();
var entities2 = new TherapyDatabaseDevEntities();
entities1.PatientDevices.AddObject(new PatientDevice
{
Patient = entities1.Patients.First(),
Device = entities1.Devices.First()
});
entities1.SaveChanges();
var relation1a = entities1.Patients.First().PatientDevices.ToList();
var relation2a = entities2.Patients.First().PatientDevices.ToList();
entities1.PatientDevices.DeleteObject(entities1.PatientDevices.ToList().Last());
entities1.SaveChanges();
var relation1b = entities1.Patients.First().PatientDevices.ToList();
var relation2b = entities2.Patients.First().PatientDevices.ToList();
relation1a and relation2a both have one entry. relation1b has no entry, but relation2b has one entry. Even if working with refreshes before the query:
entities2.Refresh(RefreshMode.StoreWins, entities2.Patients);
entities2.Refresh(RefreshMode.StoreWins, entities2.PatientDevices);
entities2.Refresh(RefreshMode.StoreWins, entities2.Devices);
var relation1b = entities1.Patients.First().PatientDevices.ToList();
// still 1 entry
var relation2b = entities2.Patients.First().PatientDevices.ToList();
Is there a possibility to bring the second context up to date or do I have to create another ObjectContext?
Edit
I found out that if I do this:
entities2.Refresh(RefreshMode.StoreWins, entities2.Patients.First().PatientDevices);
the relation gets updated properly. It's a pity that without the refresh entities2.PatientDevices does not contain the deleted object anymore, but entities2.Patients.First().PatientDevices still has it.
Is this intended behavior?
If you must have multiple contexts and work directly with your entities, have a look at the Attach and Detach methods, which as the names suggest are used to associate/dissociate an object from the context it was retrieved from. Note though that these methods only detatch the object you pass as an argument, not associated objects, so you'd probably have to walk through the connected objects detatching each one, which is messy.
var entities1 = new TherapyDatabaseDevEntities();
var patient1 = entities1.Patients.Single(p => p.Id = 12345);
entities1.Detach(patient1);
//loop through associated entities calling Detach on each
var entities2 = new TherapyDatabaseDevEntities();
entities2.Attach(patient1);
//loop through associated entities calling Attach on each
My preference would be to use viewmodels so that you aren't editing the entities directly, but representations of them. When a user explicitly saves an object, retrieve that object and update the changes from that object only on a fresh context.
var entities1 = new TherapyDatabaseDevEntities();
var patient1 = entities1.Patients.Single(p => p.Id = 12345);
... dispose of your context, it's no longer needed, and make your changes here
var entities2 = new TherapyDatabaseDevEntities();
var patient2 = entities2.Patients.Single(p => p.Id = 12345);
patient2.Property1 = patient1.Property1;
... update with other changes (there's ways to make this code cleaner, just showing simplest example)
entities2.SaveChanges();
entities2.Dispose();
Some reference material on Attach/Detach here - http://msdn.microsoft.com/en-us/library/bb896271.aspx
Do a search on entity framework context lifetimes, there's a lot of discussion on this subject which might help you decide on a route that suits your needs.
Related
I have Ilist to get all Offer from repository using entity framework core. Also I have service model OfferResponseModel which includes
OfferRequestModel as reference. I used mapster to bind entity model to service model. However it only set first child. Now I want to bind it manually. I created "offers" with the size of "Offer". When I try to use foreach loop, I cannot set "offers" child element.Because it has no elements. So, I can I solve this.
var offer = await _unitOfWork.Offers.GetAllOffer();
if (offer == null)
throw ServiceExceptions.OfferNotFound;
var results = new List<OfferResponseModel>(offer.Count);
results.ForEach(c => { c.Offer = new OfferRequestModel(); });
int i = 0;
foreach(var result in results)
{
result.Offer.User = Offer[i].User.Adapt<UserResponseModel>();
result.Offer.Responsible = Offer[i].Responsible.Adapt<EmployeeResponseModel>();
result.CreatedDate = Offer[i].CreatedDate;
result.ModifiedBy = Guid.Parse(Offer[i].UpdatedBy);
result.Active = Offer[i].Status;
result.Offer = Offer[i].Offer;
result.Offer.User.Company = Offer[i].Company.Adapt<CompanyModel>();
i++;
}
I created "offers" with the size of "Offer".
No, you created it with that capacity. It's still an empty list. It's not clear to me why you're trying to take this approach at all - it looks like you want one OfferResponseModel for each entry in offer, directly from that - which you can do with a single LINQ query. (I'm assuming that offer and Offer are equivalent here.)
var results = Offer.Select(o => new OfferResponseModel
{
Offer = new OfferRequestModel
{
User = o.User.Adapt<UserResponseModel>(),
Responsible = o.Responsible.Adapt<EmployeeResponseModel>()
},
CreatedDate = o.CreatedDate,
ModifiedBy = Guid.Parse(o.UpdatedBy),
Active = o.Status
}).ToList();
That doesn't set the Offer.User.Company in each entry, but your original code is odd as it sets the User and Responsible properties in the original Offer property, and then replaces the Offer with Offer[i].Offer. (Aside from anything else, I'd suggest trying to use the term "offer" less frequently - just changing the plural to "offers" would help.)
I suspect that with the approach I've outlined above, you'll be able to work out what you want and express it more clearly anyway. You definitely don't need to take the "multiple loops" approach of your original code.
One thing you have left out is the type of the offer variable that is referenced in the code. But I am thinking you need to do something along these lines:
if (offer == null)
throw ServiceExceptions.OfferNotFound;
var results = offer.Select(o => new OfferResponseModel
{
Offer = new OfferRequestModel
{
User = o.User.Adapt<UserResponseModel>(),
Responsible = o.Responsible.Adapt<EmployeeResponseModel>(),
...
}
}).ToList();
Select basically loops through any items in offer and "converts" them to other objects, in this case OfferResponseModel. So inside select you simply new up an OfferResponseModel and directly sets all the properties you need to set.
You need using System.Linq; for Select to be available.
I have tried multiple solution found in stack and the internet i have lost a full day of work behind this please have a look to my code
public async Task<ActionResult> Edit(JobeezUserInfoViewModel jobeezUserInfo, HttpPostedFileBase UploadedImage)
{
var user = UserManager.FindById(User.Identity.GetUserId());
//list of languages
var lgs = jobeezUserInfo.Languages.Select(l => l.LanguageId);
//Languages to be deleted
var lngTodel = db.Languages.AsNoTracking()
.Where(ut => ut.JobeezUserInfoId == jobeezUserInfo.ApplicationUserId)
.Where(ut => !lgs.Contains(ut.LanguageId));
//language ids as Ilist for better performace
var ids = lgs as IList<int> ?? lgs.ToList();
//Languages to be added
var lngToAdd = ids
.Where(
lid =>
user.JobeezUserInfo.Languages
.Select(ut => ut.LanguageId) //for each userlanguages create a list if languageids _
.Contains(lid) == false //_check if it does not contain the posted languageids and return the language ids if it is the case(tid is the posted languageid)
)
.Select(tid =>
new Language()
{
JobeezUserInfoId = user.Id,
LanguageId = tid,
Name = Enum.GetName(typeof(Enums.LanguageEnum), tid)
});
//languages to be updated
var lngToUpdate = user.JobeezUserInfo.Languages.Where(l=>ids.Contains(l.LanguageId));
Mapper.CreateMap<JobeezUserInfoViewModel, JobeezUserInfo>(MemberList.Destination);
JobeezUserInfo info = Mapper.Map<JobeezUserInfo>(jobeezUserInfo) as JobeezUserInfo;
user.FirstName = jobeezUserInfo.FirstName;
user.LastName = jobeezUserInfo.LastName;
user.PostCode = jobeezUserInfo.PostCode;
user.PhoneNumber = jobeezUserInfo.Telephone;
if (ModelState.IsValid)
{
//mark modified for the userinfo
db.JobeezUserInfo.Attach(info); // Entity is in Unchanged state
db.Entry(info).State = EntityState.Modified;
And the next line is this - My question is why I cant attach the language object 'l'
lngToUpdate.ForEach(l =>
{
db.Languages.Attach(l);
db.Entry(l).State = EntityState.Modified;
});
I have the error
"An entity object cannot be referenced by multiple instances of
IEntityChangeTracker."
I have opened the the quick view After the following line (one part of the image shows also the input parameters of my controler action (viewmodel object):
//mark modified for the userinfo
db.JobeezUserInfo.Attach(info); // Entity is in Unchanged state
Precision : the languageobject in the db.changetracket.Entities() is the same object that is posted to my server (in my viewModel) why the enitity framework cannot understand that the new object has to be tracked or attached in place of the new language object (My code is partial for the sake of clarity i can post full code if needed)
My question is : I really dont know what is the best method to update the child entities correctly. What am I doing wrong here and how to get this work ?
thanks in advance
I solved my parent child problem by using AsNoTracking().
Here BOND is my Parent table and GOVBONDINTERESTSCHEDULE is its Child. Also FI is another child table of BOND. A simple update Threw an error because of EF tracking. So I simply updated GOVBONDINTERESTSCHEDULE using AsNoTracking().
Below is my working code.
GOVBONDINTERESTSCHEDULE editmodel = new GOVBONDINTERESTSCHEDULE();
editmodel = new Entities(Session["Connection"] as EntityConnection).
GOVBONDINTERESTSCHEDULEs.Include("BOND").AsNoTracking().Where(t => t.REFERENCE == Ref).
SingleOrDefault();
editmodel.STATUS = ConstantVariable.STATUS_APPROVED;
editmodel.LASTUPDATED = DateTime.Now;
editmodel.LASTUPDATEDBY = Session["UserId"].ToString();
using (Entities db = new Entities(Session["Connection"] as EntityConnection)) {
db.Entry(editmodel).State = EntityState.Modified;
db.SaveChanges();
}
I understood that I have two different errors actually
1.
I had this error because lngToUpdate was a wrongly populated one - speaking about the following line (my bad! ;) ).
var lngToUpdate = user.JobeezUserInfo.Languages.Where(l=>ids.Contains(l.LanguageId));
It is the same collection as in the database which was obviously already been tracked.
so I created a new collection with the modified property values for each languages who's properties were to be updated.
2.
Now For the other two lists lngToAdd and lngToUpdate , with the ObjectStateManager gotten buy the following code
var manager = ((IObjectContextAdapter)db).ObjectContext.ObjectStateManager;
I used the manager to change the objectState of the peviously tracked languages as EntityState.unchanged, attched the new ones to the context and marked EntityState.Added EntityState.Deleted respectively (as per my lists).
and it worked! hope this helps someone!
I've got Entity types that are in a parent-child relationship.
Since ExecuteTransactionRequest executes multiple message requests in one tranasaction, would the following work as I intend it to?
There are 3 parents with no children to start with:
//Create a 4th parent
cs_parent parent4 = new cs_parent{ cs_name = "p4" };
CreateRequest createParentRequest = new CreateRequest { Target = parent4 };
request.Requests.Add(createParentRequest);
EntityCollection parents
= context.RetrieveMultiple(/*fetchExpression to get all parents (I'm expecting 4 now)*/);
//Create a child for each parent
foreach (var p in parents.Entities)
{
cs_child child = new cs_child
{
cs_parentid = p.ToEntityReference();
}
CreateRequest createChildRequest = new CreateRequest { Target = child };
request.Requests.Add(createChildRequest);
}
response = (ExecuteTransactionResponse)context.Execute(request);
Would I be getting 4 parents with one child each then, or only 3 since when I'm retrieving multiple, the 4th one hasn't been created yet (?)?
If not, how do I revise my code ideally with still one Execute command at the end?
I haven't actually run your code for myself to be 100% sure, but it looks like it's going to error out because the fourth parent record doesn't have the necessary info on it at the time you assign it as an EntityReference on the child entity. You can work around this easily though. CRM allows for this type of situation where inter-dependent records can all be submitted within one batch Create request. Normally when you create a record in CRM, the system assigns it a unique identifier (guid), but you can override this simply by assigning the guid yourself, then you have what you need to set it as a EntityReference on other objects. So when you create the fourth parent, you would have something like this:
cs_parent parent4 = new cs_parent { cs_name = "p4",cs_parentId = Guid.NewGuid());
Just guessing at the Id field on your entity, but you get the idea.
One thing I'm not sure from your code sample, what context is, so I can't say for sure if doing a retrieve on that will return your parent4 object. You might need to have two loops, one for existing cs_parent records to create child records for them, and another loop to create child records for parent records in the request.Requests list that are not yet in the system... Food for thought.
Edit: I realise I misread part of the question, but the following still applies to the new parent record you want to create. Add it to the ExecuteTransactionRequest Requests.
Add the children to the parent Entity's RelatedEntities collection (pseudo-example):
// Create parent object
var invoice = new Entity("invoice");
// Create list of child objects
var invoiceDetailList = new List<Entity>() { new Entity("invoicedetail"), new Entity("invoicedetail") };
// Add child records to parent record's RelatedEntities
invoice.RelatedEntities.Add(new Relationship("invoice_invoicedetails"), new EntityCollection(invoiceDetailList));
// Add to ExecuteTransactionRequest.
transactionRequest.Requests.Add(new CreateRequest { Target = invoice });
This way you don't need to know the parent record's GUID up front.
There's a many-to-many UserFeed table that stands between User and Feed and denotes a twitter-like follow relationship.
It only has two fields, which form a composite key: UserID and FeedID.
I need to write a method that will subscribe or unsubscribe a user from a feed based on a boolean flag.
public void SetSubscriptionFlag (int userId, int storeId, bool subscribe)
{
}
I'm new to Entity Framework so I'm trying to find and follow an "EF-ish" way to accomplish this.
My initial thoughts are:
Instead of working with the middle UserFeed class, I should create a many-to-many Subscriptions property (EDIT: hit limitations here);
After I've done so, I'll need to fetch a User instance by ID, check whether it has given Feed in its Subscriptions and add/delete it depending on the flag and current existence;
Figure out how to avoid racing conflicts when there is a time interval before the check and adding/deleting and user manages to submit two adding or deletion requests;
Optimize my code as to avoid unneccessary SELECTs, if any occur, because all I really want to do is a single SELECT and single INSERT/DELETE.
A relevant code snippet and comment on my points is highly appreciated.
Thanks!
You can use dummy objects - it definitely works for insert and I hope it can be used for delete as well:
Create new relation:
var user = new User() { Id = userId };
context.Users.Attach(user);
var store = new Store() { Id = storeId };
context.Stores.Attach(store);
// Now context trackes both entities as "existing"
// and you can build a new relation
user.Subscriptions.Add(store);
context.SaveChanges();
Remove existing relation:
var user = new User() { Id = userId };
var store = new Store() { Id = storeId };
user.Subscriptions.Add(store);
context.Users.Attach(user);
// Now context trackes both entities as "existing"
// with "existing" relation so you can try to remove it
user.Subscriptions.Remove(store);
context.SaveChanges();
i have following trouble with LINQ to SQL entities:
// Context is DataContext that was auto genereted when i create my .dbml file
var cl = Context.Classes.ToArray();
var rm = Context.Rooms.ToArray();
List<DaySchedule> s = new List<DaySchedule>();
s.Add(new DaySchedule()
{
Class = cl[0],
DayOfWeek = 0,
Pair = 1,
Room = rm[0]
});
Context.SubmitChanges();
so, after "SubmitChanges" new DaySchedules will be saved to db. BUT i didn't call InsertOnSubmit function and i don't want to save this DaySchedule.
BTW,
if i will using following code:
s.Add(new Acceron.University.DBAccess.DaySchedule()
{
Class_id = cl[0].Class_ID,
DayOfWeek = 0,
Pair = 1,
Room_id = rm[0].Room_ID
});
It will not be auto saved to db.
Could you explain is it bug or feature and how i can solve it?
It is by design. Class and Room are context-aware entities, since they were queried against the context. Anytime a context-aware entity adds children, it queues up those changes automatically to the context and marks it as inserted. So you cannot add new entities without the auto-queuing feature. I'd highly recommend not calling save changes later on.