I'm trying to create an Incident record with some Service Activity related ones attached.
For this, I leverage the RelatedEntities property as follows:
// (stripped to the essential)
Entity myRecord = new Entity("incident");
foreach(var e in myData.Select(record => record.ToEntityReference()) // (1) below
{
Entity relatedRecord = new Entity("serviceappointment");
// mandatory fields are set here (omitted)
relatedRecord["new_mylookup"] =
new EntityReference(e.LogicalName, e.Id) { Name = e.Name };
myRecord.RelatedEntities.Add(relatedRecord);
}
service.Execute(new CreateRequest(){ Target = myRecord });
(1): myData is a IQueryable<Entity> which picks some records out from a custom entity. Data is 101% correct (I already inspected it).
The outcome of this code is:
The incident is created
The serviceappointment records are created, *but my new_mylookup attribute is empty*. All other fields are perfectly fine and filled with my data.
I see no errors whatsoever, neither during the execution of the code nor in the server trace. As far as the lookup field goes, it's a common lookup (no scripts, no plugins) set as Recommended.
What's wrong with the code ?
NOTE: removing the Name from new EntityReference(e.LogicalName, e.Id) { Name = e.Name }; doesn't change anything: new EntityReference(e.LogicalName, e.Id); still doesn't fill the lookup.
It turns out a co-worker of mine added an onLoad script which inadvertently was blanking out the same field I was setting. My code worked from the start.
Related
I followed the great advice here (Handling calculated properties with breezejs and web api) to allow Breeze to access my calculated properties which I have set up in a partial class on the server side:
public partial class EventPerson
{
[NotMapped]
public Decimal TotalAmountPaid
{
get
{
return this.EventPersonPayments.Sum(p => p.AmtPaid);
}
}
}
But for each EventPerson I retrieve, this value shows up as 0 unless I use .expand("EventPersonPayments") clientside or .Include("EventPersonPayments") serverside.
I don't want all the data in EventPersonPayments to be serialized and sent to the client; all I want is the summed value. Is this possible?
EDIT: If my calculated property is derived from other properties already in the entity, it works fine. For example:
public partial class EventPerson
{
[NotMapped]
public String DisplayName
{
get
{
return this.FirstName + " " + this.LastName;
}
}
}
returns the DisplayName in the JSON payload. The former type of calculated property always returns 0 or null unless I specifically load all the extra information.
I considered converting these into User Defined Functions in SQL Server, but I shouldn't have to throw out my C# code just to make it work the way it should.
One approach is to use a projection that incorporates both the entities being queried and some calculated properties as well. i.e. your server query might look like this:
[HttpGet]
public IQueryable<Object> CustomersAndFreightTotals(companyName) {
var stuff = ContextProvider.Context.Customers
.Where(c => c.CompanyName.StartsWith(companyName))
.Select(c => new { Customer = c, FreightTotal = c.Orders.Sum(o => o.Freight)) });
return stuff;
}
This query will load all of your customers that start with a specified company name but will also give you the "total freight" for all of the orders on each customer.
You would call this with code something like this:
var query = EntityQuery.from("CustomersAndFreightTotals")
.withParameters({ companyName: "C" });
myEntityManager.executeQuery(query).then(function(data) {
var results = data.results;
results.forEach(function (r) {
// note that each customer WILL also be added to the local entityManager
// because it is an entity, whereas the freightTotal is only available here.
var customer = r.Customer;
var freightTotal = r.FreightTotal;
// and if you wanted to hack the customer entity
// you could do this.
customer.freightTotal = freightTotal;
});
}
I came across this problem also, and there are a couple of other questions/answers that seem to point to what's going on:
My unmapped properties in breeze does not seems to work whith a projection
UnMapped property on the Angular/Breeze SPA template
From my understanding, to put it shortly, [NotMapped] prevents Breeze/Entity Framework from correctly wiring up to the field. Yet Json.NET will serialize the field and send it to Breeze, which will populate the field if you've manually set it up via the class's constructor, and the data has been retrieved by using expand for the other property which Entity Framework recognizes. This seems to be almost an accident you can get [NotMapped] fields to work on the client in this given case; the Breeze+Entity Framework does not seem to be designed for this case.
There is a suggestion at Breeze's User Voice that you could vote and comment on. I'm not sure that Breeze could solve this problem themselves without some work from the Entity Framework team, but at least it could put the issue on their radar.
Sorry for the nebulous title, it's hard to describe this in a single line:
I have 2 entities User and UserAddress, where User has 2 foreign keys DefaultInvoiceAddressId and DefaultDeliveryAddressId and UserAddress has a UserId foreign key.
The user object has navigation properties for the default addresses (DefaultInvoiceAddress and DefaultDeliveryAddress) as well as one for all of his addresses: AllAddresses.
The mapping etc. works, creating and updating users and addresses works too.
What does not work though is setting an existing Address of a User as e.g. DefaultInvoiceAddress. In SQL terms, what I want to happen is UPDATE USER SET DefaultInvoiceAddressId = 5 WHERE Id = 3.
I've tried this the following way:
private void MarkAs(User user, UserAddress address, User.AddressType type) {
if (context.Entry(user).State == EntityState.Detached)
context.Users.Attach(user);
// guess I don't really need this:
if (context.Entry(address).State == EntityState.Detached)
context.UserAddresses.Attach(address);
if (type.HasFlag(User.AddressType.DefaultInvoice)) {
user.DefaultInvoiceAddressId = address.Id;
user.DefaultInvoiceAddress = null;
context.Entry(user).Property(u => u.DefaultInvoiceAddressId).IsModified = true;
}
if (type.HasFlag(User.AddressType.DefaultDelivery)) {
user.DefaultDeliveryAddressId = address.Id;
user.DefaultDeliveryAddress = null;
context.Entry(user).Property(u => u.DefaultDeliveryAddressId).IsModified = true;
}
}
This method is called both when creating new UserAddresses as well as when updating addresses. The create scenario works as expected, however in the update case I receive the following error:
The changes to the database were committed successfully,
but an error occurred while updating the object context.
The ObjectContext might be in an inconsistent state.
Inner exception message: A referential integrity constraint violation occurred:
The property values that define the referential constraints are not consistent between principal and dependent objects in the relationship.
I call the method with a User object I retrive from the database and the DefaultDeliveryAddress it contains, which I load alongside it via eager loading.
var user = mainDb.User.Get(UnitTestData.Users.Martin.Id, User.Include.DefaultAddresses);
var existingAddress = user.DefaultDeliveryAddress;
mainDb.User.Addresses.SetAs(user, existingAddress, User.AddressType.DefaultInvoice))
// the SetAs method verfies input parameters, calls MarkAs and then SaveChanges
In a nutshell, I just want to make the DefaultDeliveryAddress of a user also his DefaultInvoiceAddress, which would be easily accomplished with the above SQL Update command, but I'm missing something with my EF code.
I've already checked that:
Only the Id is set, the navigation property (DefaultInvoiceAddress) is re-set to null
UserAddress.UserId = User.Id (obviously since it is already assigned to the user)
The user object will become Modified (checked with debugger), since one of its properties is being marked as modified
I also tried clearing both default address navigation properties, but that didn't help either
I suspect this problem is due to the User entity having 2 references to UserAddress, and both foreign keys are set to refer to the same address - how can I get EF to work with that?
Update:
Here are the mappings of the User entity:
// from UserMap.cs:
...
Property(t => t.DefaultInvoiceAddressId).HasColumnName("DefaultInvoiceAddressId");
Property(t => t.DefaultDeliveryAddressId).HasColumnName("DefaultDeliveryAddressId");
// Relationships
HasOptional(t => t.DefaultInvoiceAddress)
.WithMany()
.HasForeignKey(t => t.DefaultInvoiceAddressId);
HasOptional(t => t.DefaultDeliveryAddress)
.WithMany()
.HasForeignKey(t => t.DefaultDeliveryAddressId);
HasMany(t => t.AllAddresses)
.WithRequired()
.HasForeignKey(t => t.UserId)
.WillCascadeOnDelete();
UserAddress has no navigation properties back to User; it only contanis HasMaxLength and HasColumnName settings (I exclude them to keep the question somewhat readable).
Update 2
Here's the executed command from Intellitrace:
The command text "update [TestSchema].[User]
set [DefaultInvoiceAddressId] = #0
where ([Id] = #1)
" was executed on connection "Server=(localdb)\..."
Looks fine to me; seems only EF state manager gets confused by the key mappings.
Figured out the problem: apparently it makes quite the difference when to set navigational properties to null, as EF might otherwise interpret that as an intended change / update (at least that is what I suspect).
The following version of the MarkAs method works:
private void MarkAs(User user, UserAddress address, User.AddressType type) {
if (context.Entry(user).State == EntityState.Detached) {
// clear navigation properties before attaching the entity
user.DefaultInvoiceAddress = null;
user.DefaultDeliveryAddress = null;
context.Users.Attach(user);
}
// address doesn't have to be attached
if (type.HasFlag(User.AddressType.DefaultInvoice)) {
// previously I tried to clear the navigation property here
user.DefaultInvoiceAddressId = address.Id;
context.Entry(user).Property(u => u.DefaultInvoiceAddressId).IsModified = true;
}
if (type.HasFlag(User.AddressType.DefaultDelivery)) {
user.DefaultDeliveryAddressId = address.Id;
context.Entry(user).Property(u => u.DefaultDeliveryAddressId).IsModified = true;
}
}
To sum up my findings for future readers:
If you intend to update entities via Foreign Key properties, clear navigation properties. EF doesn't need them to figure out the update statement.
Clear navigation properties before you attach an entity to a context, otherwise EF might interpret that as a change (in my case the foreign key is nullable, if that isn't the case EF might be smart enough to ignore the navigation property change).
I will not accept my own answer right away to give other (more qualified) readers a chance to answer; if no answers are posted in the next 2 days, I'll accept this one.
I have written test code as bellow:
Entities db = new Entities();
var place = new Place
{
Id = Guid.NewGuid(),
Name = "test",
Address = "address"
};
db.Places.Add(place);
var cachedPlace = db.Places.Where(x => x.Id == place.Id).FirstOrDefault(); \\ null
I expected dbset will return the added entity. But it gives me object only after changes were saved to the real DB.
If you want to access the unsaved query, then you use the Local property of the DbSet.
The reason it doesn't work the way you want is that it must also support autonumbered identities, and that will mean the ID is 0. If you insert multiple records, you would have multiple objects with the same 0 ID. EF won't know what the real ID is until after it's been saved.
I have a simple relationship:
I have a "Note" object, which has a related "Company" object.
When creating a "Note", you select an existing "Company", and enter the other neccessary fields.
My issue is that whenever I save, it inserts a new version of the "Company" object, and I can't figure out why.
I have tried all the options in the accepted answer here Insert new object with existing object but nothing works.
Currently, I am doing the following:
var company = db.Company.FirstOrDefault(i => i.ID == note.Company.ID);
note.Company = company;
db.Notes.AddObject(note);
db.ObjectStateManager.ChangeObjectState(company, EntityState.Unchanged);
db.ObjectStateManager.ChangeObjectState(note, EntityState.Added);
db.SaveChanges();
But, it keeps adding a new "Company" object. What is strange, is that it doesn't add all the fields, just the CompanyName" field, (and creates a new Id).
If this is an existing company, set the CompantID field and not the entity
note.CompanyID = company.ID;
I think the problem is with your first line when use use note.Company.Id in the FirstorDefault method. If you could post the entrie method I may be able to help further. If you have companyId as a parameter coming into the method then you could rework the code above as follows:
Company company = db.Company.Find(companyId);
note.Company = company;
db.SaveChanges();
Or as the other answer suggests just set note.CompanyId
Here is how i do it, hope it helps.
using (DataContext dtx = new DataContext()) {
Note note = new Note();
dtx.Notes.Add(note);
Company company = dtx.Companies.FirstOrDefault((System.Object obj) => obj.ServerId == 1);
note.Server = company;
dtx.SaveChanges();
}
The problem is you are creating a new instance of Note and setting the Company property therefore EF which I assume is re-creating the company record. If you are trying to link your Note to an existing Company object them just set the foreign key for the company on the note e.g.
var company = db.Company.FirstOrDefault(i => i.ID == note.Company.ID);
note.CompanyID = company.ID; // will link the note to the specified company
db.Notes.AddObject(note);
db.SaveChanges();
It also doesn't make sense that you are using note.Company.ID to pull the company down, this property should be null if it's a new Note.
Thanks for every ones responses.
My problem in the end was, firstly, when creating my model for the page, I instantiated an initial new "Company", so I set company to null for creating.
Then, on the page, I had Html.TextBoxFor(model => model.Company.CompanyName,.... (it's an auto complete ajax text box that sets a hidden companyID field when chosen) when was meaning a new Company object was getting passed back to the controller.
I did try setting this to null before setting the company ID, but that didn't work.
However, removing the above textboxfor bit, everything started working!
I am trying to create a new user object with a specific Role. The "Role" is an existing entity in EF. I have googled, and stackoverflowed until I am blue in the face, and I have tried all the stuff that seems to be working for everyone else. But when I try to save my new user object, it first tries to create a new "Role", instead of just creating the new user object with a reference to the existing Role.
What am I doing wrong?
Role myRole = new Role { ID = myUser.Role.ID };
myObjectContext.Roles.Attach(myRole);
myUser.Role = myRole;
if (myUser.ID == 0)
{
myObjectContext.Users.AddObject(myUser);
}
else
{
if (myUser.EntityState == System.Data.EntityState.Detached)
{
myObjectContext.Users.Attach(myUser);
}
myObjectContext.ObjectStateManager.ChangeObjectState(myUser, System.Data.EntityState.Modified);
}
myObjectContext.SaveChanges(SaveOptions.None);
EDIT - AFTER MORE TESTING...
Ok.. so I have discovered some portion of the "cause" anyway. I still don't know why it does this and need help.
Basically, there are two sets of data I am attaching to my new User object. One is the "Role" which is a FK to a Role table that contains the Role. This shows up as a navigation property on the User like "User.Role".
The second set of data is a collection of objects called "FIPS", which are a many-to-many relationship between the User and another table called FIPS. There is a relationship table between them, that simply contains two columns, each a foreign key to User and FIPS, respectively. The FIPS for a user are also a navigation property that is referenced like "User.FIPS".
Here is the whole code showing the assignment of the FIPS and Role to the User object prior to saving the context.
List<string> fipsList = new List<string>();
foreach (FIPS fips in myUser.FIPS)
{
fipsList.Add(fips.FIPS_Code);
}
myUser.FIPS.Clear();
foreach (string fipsCode in fipsList)
{
FIPS myFIPS = new FIPS { FIPS_Code = fipsCode };
myObjectContext.FIPSCodes.Attach(myFIPS);
myUser.FIPS.Add(myFIPS);
}
Role myRole = new Role { ID = myUser.Role.ID };
myObjectContext.Roles.Attach(myRole);
myUser.Role = myRole;
if (myUser.ID == 0)
{
myObjectContext.Users.AddObject(myUser);
}
else
{
if (myUser.EntityState == System.Data.EntityState.Detached)
{
myObjectContext.Users.Attach(myUser);
}
myObjectContext.ObjectStateManager.ChangeObjectState(myUser, System.Data.EntityState.Modified);
}
myObjectContext.SaveChanges(SaveOptions.None);
I set up my watch to check the status of "myObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added)" to see when things were being added to this.
As soon as the first Related object is added to the User object, the second Related object that hasn't yet been attached to the context, is added to the context with an EntityState of "Added".
.... Gonna see if there is a way to avoid attaching the related entities to the User entity until after they have all been attached to the context.
--FOLLOWUP--
Ok.. well I changed the order of the code so that the related entities were attached to the context before being assigned to the User entity.. but as soon as the first related entity is assigned, the second related entity is shown as "added" in the ObjectStateEntries.
So, then I changed it to the following order:
Attach all related entities to context.
Remove existing relationships on the user object to related entity
types.
Assign related entities to user entity.
Save user entity.
And.. now.. it works.. omg it works... ! =)
It's been a while since I wrote the code below, but I vaguely recall running into the same problem and it was occurring because the role being added was currently being tracked by the context, so attaching the stub has the effect of adding a new role with the same Id.
In the following code, I check the ChangeTracker first and use an existing entry if the role is being tracked.
// add roles that are in dto.Roles, but not in resource.Roles
// use the change tracker entry, or add a stub role
var rolesToAdd = fromDto.Roles.Where(r => !toResource.Roles.Any(role => role.Id == r)).ToList();
var roleEntries = dbContext.ChangeTracker.Entries<Role>();
foreach (var id in rolesToAdd)
{
var role = roleEntries.Where(e => e.Entity.Id == id).Select(e => e.Entity).FirstOrDefault();
if (role == null)
{
role = new Role { Id = id };
dbContext.Set<Role>().Attach(role);
}
toResource.Roles.Add(role);
}
Why are you creating a new instance of your Role entity if it already exists in the database?
Anyway, if you want to manually attach your new instance to the context, it should work if the ID of the attached instance exists in the database. But in your case the following lines are a bit strange:
Role myRole = new Role { ID = myUser.Role.ID };
myObjectContext.Roles.Attach(myRole);
myUser.Role = myRole;
You first create a new Role that has an ID that comes from an existing Role instance (myUser.Role) then you attach your new instance then finally you affect again your instance to the user it comes from.
There's definitely something wrong here.
If your Role already exists (and it appears to be the case here as you wrote myUser.Role.ID on the first line, so I assume), why are you creating a new instance.
Drop those 3 lines.
Get your Role from the database. Then affect the Role that comes from the database to the myUser.Role property.
This is how I did it in my case.
Its a similar case where Item contains ICollection<Attribute> .Here no update is done , adding already existing attribute to the item is needed.
First I looped through each attribute inside the item.
I had to first detach it from the local
context.Set<Model.Attribute>().Local
.Where(x => x.Id == attr.Id)
.ToList().ForEach(p => context.Entry(p).State = EntityState.Detached);
Then I attached .
context.Set<Model.Attribute>().Attach(attr);
Then I reloaded the datas to it .
context.Entry(attr).Reload();
Try using this instead of the first three lines (which shouldn't be necessary at all, if the user object already knows it's role's ID and is discarded anyway):
int id = myUser.Role.ID; // Role should be NULL, if the user is actually new...
// could it be that you wanted to write myUser.RoleID?
Role myRole = myObjectContext.Roles.FirstOrDefault(x => x.ID == id);
myUser.Role = myRole;