Okay, so it's late and I'm tired, but this shouldn't be happening... or so I believe. I have the following code for an event handler for a button click where I create a new customer from a web form.
int customerId = <from_somewhere_doesnt_matter>;
Customer cust;
if (int.TryParse(Request.QueryString["cid"], out customerId)) {
// update existing customer
cus = db.Customers.Single(c => c.CustomerId == customerId);
cus.UpdatedByUser = user;
cus.Updated = DateTime.Now;
}
else {
// create new customer
cus = new Customer();
cus.InsertedByUser = user;
cus.Inserted = DateTime.Now;
}
SetFields(cus);
db.SaveChanges();
The SetFields() method just populates the different properties for the customer from the corresponding web form fields.
I have been running this code in production for quite a while and it's been working fine. However, recently a user told me it doesn't work to add a new user (doesn't happen very often). I checked it, and sure enough, I filled in the form and tried to add the user but was just redirected back to the user list without any error message and without a new user.
I checked the code, and realised I had forgotten to add the db.Users.AddObject(usr) when adding a new user. I added the method call, and the user was added correctly. I then went to the customer code, just to check how and when I call the AddObject-method there, and it turns out I don't!
I might be blind, but I have searched the source code and I do not call the method anywhere and it still works to add a customer! The only thing I can think of is that the customer is added because it refers to another object (the current user), and that somehow triggers an add. The user does not depend on any other fields.
What is happening!?
Thanks in advance!
The reason might be automatic association fixup. The T4 Template POCO Generator of EF 4.0 for instance creates such methods which are called in the property setters. Possibly it happens in this line in your code:
cus.InsertedByUser = user;
If your User entity has a collection of customers (as the inverse navigation property to InsertedByUser) then the POCO Generator would create a fixup method which does something like ...
InsertedByUser.Customers.Add(this);
... where this is cus in your example. This is called in the setter for the InsertedByUser property. This means that your new created customer cus is automatically added to the Customers collection of the user which you assign. EF change detection will recognize this and put the new customer in Added state into the context. When you call SaveChanges a new customer record in the database is created.
Just a hypothesis. You could check in detail what's going on by debugging into the property setter.
When you set
cus.InsertedByUser = user;
Your customer will be added to the user.Customers collection that represents the other end of the many-to-one. This gives EF a handle on the object which will then be added to the context and saved away since it is new.
Related
im new in CRM. I have create two entity : Order and Product. On order entity there is look up field that fire to product entity. I try to get productquantity from product through look up field and paste it to a field within the order entity. Here is the code i tried:
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
{
Entity entity = (Entity)context.InputParameters["Target"];
if (entity.Attributes.Contains("new_productname"))
{
Entity productreference = service.Retrieve("new_callistyproduct", ((EntityReference)entity["new_productname"]).Id, new ColumnSet(true));
if (productreference.Attributes.Contains("new_productquantity"))
{
if (entity.Attributes.Contains("new_numberofproduct"))
entity["new_numberofproduct"] = productreference.GetAttributeValue<Decimal>("new_productquantity");
else
entity.Attributes.Add("new_numberofproduct", productreference.GetAttributeValue<Decimal>("new_productquantity"));
}
}
}
I want this plugin work whenever i create a new record. So i register this plugin as Pre-create event. But, when i try to create a record. This plugin did't retrieve value from productquantity field.
So, i tried to run this plugin as Pre-Update event. On the record i've create before, i change the lookup value from product A to product B. And its work, the plugin retrieve a product quantity value from product B.
The question is, what should i do if i want this plugin also work for pre-Create event.
Thanks
If you want to update a target entity, and have CRM perform the update for you, you'll have to register your plugin on the Pre-Create or Pre-Update. If you want to do the action on a Post event, you'll need to call Update using the IOrganizationService, just updating the Target won't work. You'll also want to be sure you don't create an infinite loop, where an update triggers the plugin, which performs another update that triggers the same plugin, which performs another update... etc. etc.
In my application I instantiate a new Linq-to-SQL object and pass it (with some value assigned) to a WinForms window so the user can fill out the rest.
If the user cancels the form I don't want the data to be inserted.
The problem is that when SubmitChanges is called the next time, the record is still inserted.
Is there an easy way to prevent that? I don't want to create a separate class with the same fields as the Linq-to-SQL class when I already have all the fields I need.
I found this article http://www.codeproject.com/KB/linq/linq-to-sql-detach.aspx, but it seems to solve a different problem and doesn't seem like an easy solution.
Update:
I was able to reproduce the behavior in a console app.
Here is the code I used (with different names like Customer and Country):
MyDatabase db = new MyDataBase(#"Data Source:d:\test.sdf");
Customer customer = new Customer();
customer.Name = "customer 1";
customer.Country = db.Country.FirstOrDefault();
db.SubmitChanges();
The insert happened as soon as I added the "Country" assigment line. Country data in this example is stored in a different table.
The record won't be inserted unless you either call InsertOnSubmit, or it is otherwise attached to your datacontext.
Are you inserting it to your database before you send it to the form? I assume that if your user cancels the form then the object is thrown away?
Perhaps you could post some code, specifically around where you create the object.
edit: it appears that your customer is being attached to the datacontext when you assign it the db.Country.FirstOrDefault(). These EntityRef / EntitySet relationships are two way - when you assign customer.Country = db.Country.FirstOrDefault() you are also effectively saying db.Country.FirstOrDefault().Customers.Add(customer), which adds the customer to the datacontext as a new object. Then when you call SubmitChanges, the customer will be inserted.
The best approach is to either (a) keep the customer detached from the Country until you go to submit then record, or (b) to dispose of the datacontext if the user cancels / fails to submit - and then instantiate a new context. These approaches will definitely work and are a better pattern than you currently have.
However an even simpler way to fix the problem - without any code changes - might be to change the properties of the relationship in the LINQ-to-SQL designer. This is a workaround for your immediate problem, it's not a real fix for the underlying issue of keeping the datacontext around - it's likely you'll run into more problems. However give it a go because it's a very simple fix.
Open the dbml in Visual Studio
Select the relationship between Customer and Country, and then view Properties
You'll see a property for the Child and Parent of the relationship. In the Child section you want to set 'ChildProperty' (or it might be 'IsChildProperty') to false.
Doing this removes the Customers property on the Country object, and will mean that you can set the country in the manner that you are doing without any side-effects; i.e. it won't implicitly associate the Customer with the Country. It means that the Country object no longer 'has' Customers, and so you can't accidentally attach a Customer to a Country. Let me know if this works for you!
when the user cancels the form you can dispose the datacontext object. when the user open the form again you instantiate a new datacontext.
I'm playing around with NHibernate 3.0. So far things are pretty cool. I'm trying to attach an entity that wasn't detached previously:
var post = new Post(){ Id = 2 };
session.Update(post); // Thought this would work but it doesn't.
post.Title = "New Title After Update";
session.Flush();
What I'm trying to write is the following:
var post = new Post(){ Id = 2 };
session.Attach(post);
post.Title = "New Title After Update";
session.Flush(); // Sql should be something like: UPDATE Post SET Title='New Title After Update' WHERE Id=2
Is this possible so that only Title gets updated? This is currently possible in EntityFramework. I'd like to not have to load Post from the database when I just need to update a few properties. Also, I'm trying to avoid a method call that would create the object... since it's moving away from an object oriented approach in my opinion.
EDIT: I know about using transactions, I just used Flush() to make the code simple. Ok so I think we're sort of getting on the right track for what I'm trying to achieve. I'd like to be able to create an entity with a known Id using the constructor, like I have in the 2nd code block above. I don't want to have to make a call to Get<T> or Load<T> since it feels rather wrong constructing objects like this that already exist in the database. For example, in Entity Framework I can write the 2nd code example and it will "just work". It only updates the Title property.
You can session.Save() or session.SaveOrUpdate()
update
Okay, I think I see now what you are trying to do. You are trying to update a single property on a Post that was previously persisted, not a new Post, and to do that you're instantiating a new Post and giving it the Id of one in the database.
I'm not sure what you mean when you say you're trying to avoid a method call that would create the object, but the way to do this with NHibernate is this:
var post = session.Load<Post>(2);
post.Title = "New Title";
session.SaveOrUpdate(post);
In general, you should not be calling Flush() on your sessions.
The important thing to note here is the use of session.Load. Calling Load with an id in and of itself does not load the entity from the database. The entity's property values will only be loaded when/if you access them.
Of course, in this scenario, I believe that NHibernate will load the properties for the Post, (but not collections unless you've specified an eager fetch mode), and that makes sense (frankly, I don't understand why EF would not load the entity). What if the setter for your Title property does something important, like check it against the existing title, validate the title's length, check your credentials, or update another property? Simply sending an UPDATE to the database isn't sufficient.
It's possible to only update changed properties by setting dynamic-update in the mapping. However, as far as I know, it is not possible (without reverting to SQL) to perform an update without retrieving the object from the database at some point.
Use the Merge method. You have to create a new instance variable to accept the attached entity = nhibernate will not do anything else with your detached instance.
var post = new Post(){ Id = 2 };
post.Title = "New Title After Update";
// Must create a new instance to hold final attached entity
var attachedPost = session.Merge(post);
session.Update(attachedPost);
session.Flush();
// Use attachedPost after this if still needed as in session entity
That covers the "attach" functionality you are looking for, but I don't see how you are going to be able to only update the one property. if the object instance has not been populated from the database, the properties will be different. Dynamic mapping will not solve this - NHibernate sees the properties as "updated" to a bunch of nulls, empty strings.
Gotta say, you are creating a new instance but what you are actually doing is updating an existing instance. You are working directly with IDs not objects. And you are setting a single property and now have an instance potentially hanging around and doing more things but it has not enforced any invariants and may in fact bear no resemblence to the real deal other than the id property...
It all feels pretty anti-object oriented to me personally.
I am using ASP.NET DynamicData (based on LINQ to SQL) on my site for basic scaffolding. On one table I have added additional properties, that are not stored in the table, but are retrieved from somewhere else. (Profile information for a user account, in this case).
They are displayed just fine, but when editing these values and pressing "Update", they are not changed.
Here's what the properties look like, the table is the standard aspnet_Users table:
public String Address
{
get
{
UserProfile profile = UserProfile.GetUserProfile(UserName);
return profile.Address;
}
set
{
UserProfile profile = UserProfile.GetUserProfile(UserName);
profile.Address = value;
profile.Save();
}
}
When I fired up the debugger, I've noticed that for each update the set accessor is called three times. Once with the new value, but on a newly created instance of user, then once with the old value, again on an new instance, and finally with the old value on the existing instance.
Wondering a bit, I checked with the properties created by the designer, and they, too, are called three times in (almost) the same fashion. The only difference is, that the last call contains the new value for the property.
I am a bit stumped here. Why three times, and why are my new properties behaving differently? I'd be grateful for any help on that matter! =)
I observed something similar when I let Linq to SQL use stored procedures for inserting/updating. I am not sure if I remember correctly, but I think that Linq to SQL uses these three instances of the entity class to figure out what changed so that the required SQL statement can be built.
I see basically two options (though I am not sure if this really works):
You could probably store the extra field(s) in the "OnValidate" event of the entity.
You could overwrite the partial methods for inserting/updating. In that case you will also need to take care of storing the entity in the database (e.g. with stored procedure).
The property would look then like this:
private string address = null;
public string Address
{
get
{
if (this.address == null)
{
// Load on first use: This might make a problem...
UserProfile profile = UserProfile.GetUserProfile(UserName);
this.address = profile.Address;
}
return this.address;
}
set
{
this.address = value;
}
}
In both cases you have the problem that you might update the extra fields though the update of the rest of the entity fails. This was of course also a problem with your initial approach.
I think the best solution would be to implement your own profile provider and store the profile information in your own tables. If you do that you could let Linq to SQL create entities for your profile information: Everything would be "standard" and you would not have to resort to some kind of "hack"...
I have the following scenario:
Entities are loaded from the database.
One of them is presented to the user in a Form (a WPF UserControl) where the user can edit properties of that entity.
The user can decide to apply the changes to the entity or to cancel the editing.
How would I implement something like this with the EntityFramework?
My problem is that, when I bind the UI directly to the Properties of the Entity, every change is instantanously applied to the entity. I want to delay that to the moment where the user presses OK and the entity is validated successfully.
I thought about loading the Entities with NoTracking and calling ApplyPropertyChanges after the detached entity has been validated, but I'm not entirely sure about the correct way to do that. The docu of the EntityFramework at MSDN is very sparse.
Another way I could think of is to Refresh the entity with StoreWins, but I don't like resetting the changes at Cancel instead of applying changes at Ok.
Has anyone a good tutorial or sample?
One options is what you said do a no-tracking query.
ctx.Customers.MergeOption = MergeOption.NoTracking;
var customer = ctx.Customers.First(c => c.ID == 232);
Then the customer can modify 'customer' as required in memory, and nothing is actually happening in the context.
Now when you want actually make the change you can do this:
// get the value from the database
var original = ctx.Customers.First(c => c.ID == customer.ID);
// copy values from the changed entity onto the original.
ctx.ApplyPropertyChanges(customer); .
ctx.SaveChanges();
Now if you are uncomfortable with the query either for performance or concurrency reasons, you could add a new extension method AttachAsModified(...) to ObjectContext.
that looks something like this:
public static void AttachAsModified<T>(
this ObjectContext ctx,
string entitySet,
T entity)
{
ctx.AttachTo(entitySet, entity);
ObjectStateEntry entry =
ctx.ObjectStateManager.GetObjectStateEntry(entity);
// get all the property names
var propertyNames =
from s in entry.CurrentValues.DataRecordInfo.FieldMetadata
select s.FieldType.Name;
// mark every property as modified
foreach(var propertyName in propertyNames)
{
entry.SetModifiedProperty(propertyName);
}
}
Now you can write code like this:
ctx.Customers.MergeOption = MergeOption.NoTracking;
var customer = ctx.Customers.First();
// make changes to the customer in the form
ctx.AttachAsModified("Customers", customer);
ctx.SaveChanges();
And now you have no concurrency or extranous queries.
The only problem now is dealing with FK properties. You should probably look at my index of tips for help here: http://blogs.msdn.com/alexj/archive/2009/03/26/index-of-tips.aspx
Hope this helps
Alex
I suggest IEditableObject, too, and additionally IDataErrorInfo.
The way i do it is, i basically have a viewmodel for an entity that takes the entity as constructor parameter (basically a wrapper object).
In BeginEdit, i copy the entity properties to my viewmodel, so if i do CancelEdit, the data is only changed in the ViewModel and the original Entity hasn't changed. In EndEdit, i just apply the ViewModel properties to the Entity again, or course only if validation has succeeded.
For validation i use the methods of IDataErrorInfo. I just implement IDataErrorInfo.Error so that it checks each Property name via IDataErrorInfo[string columnName] and concatenates eventual error messages. If it's empty, everything is ok. (not sure if Error is meant to be used that way, but i do it)
If i have other Entities attached to my original Entity, such as Customer.Orders, i create them as nested ViewModels in the original Entity's ViewModel. The original ViewModel calls it's subModels' Begin-,Cancel-,EndEdit / Error methods in it's own implementations of those methods then.
It's a bit more work, but i think it's worth it because between BeginEdit and EndEdit, you can be pretty sure that nothing changes without you noticing it. And having a code snippet for INotifyPropertyChanged-enabled properties helps a lot, too.
The normal way of doing this is binding to something that implements IEditableObject. If and how that fits in with the entity framework, I'm not sure.