A typical situation:
- an "Order" table / class
- a "Customer" table / class
"Order" has a reference to "Customer", of course. It's an integer column named, let's say, CustomerRef.
Now, I have a form for creating a new order. Among others, there is a combo on this form, for selecting a customer. Customers' IDs are the values of items' values. After entering all neccessary data, including selecting a customer, I press "Save". Then, I create a new "Order" object, fill its properties and, finally, I want to set the reference to the selected customer. In database's terms I should just put customer's ID (an integer) to the CustomerRef column (also integer). But CustomerRef property of the Order class is not an integer but Customer class. And this is an issue:
- I can, of course, use Database.OID.ParseObjectId and <objectscope>.GetObjectById methods to get the Customer object
- but will this algorithm ask the database for this object? or will it just create a nearly empty object filled with the ID only - wtihout asking the database?
If it is the first case - is there another option for setting the reference?
10 Answers, 1 is accepted
I too struggled with this at first, and my findiings were that your assumption is correct. Instead of doing:
order.customerID = 5;
You would do :
order.Customer = BLL_MethodToGetCustomer(5); (obviously a fictitious BLL method that returns the customer object by ID)
I tried to create a new customer object and set a reference to that in the order, but that will try to insert a new customer...
I believe your assumption is the way it should be done.
Thanks. From the functional point of view - it works. But what about efficiency? The key is - whether GetObjectById method asks the database for anything or (required behaviour) just creates an empty object filled with the ID only?
I think the authors of OA, i.e. somebody from the Telerik team should answer this question...
The behavior is like: when you request an object by an ID the L1 cache is checked whether there is a materialized object available. If so it is returned. However if the object is not available an empty object is created that has only the ID field initialized. Then after its properties are accessed, it is fetched from the database. Hope that helps.
the Telerik team
That is exactly what I've expected. And it's another thing that should be exactly written in the documentation.
Your answer is that IObjectContext.GetObjectById works as follows:
- look for the object in the cache
- if found - return this object
- if not found - return a new, empty object with its ID initialized
I've done an experiment:
User u = scope.GetObjectById(Database.OID.ParseObjectId(typeof(User), pID.ToString())) as User;
(User is a persistent class, pID is variable containing the user's id)
When I set pID to some nonexistent value (123456) I received the message:
"No row for EX.Org.BLL.User ('User') GenericOID@1e244 User USR_id=123456 NOTRES "
Evidently OA searched the database.
And it is not bad: if I would ask scope for such a user just to work with it I would expect to receive this message at this moment, not later while accessing any property.
I think that you should add to IObjectContext a new method (GetObjectRef(IObjectId id)) that would explicitly return just a reference and would never access the database. If such a method would be added it would become clear:
- GetObjectById: look for a real, existing object (in the cache or in the database)
- GetObjectRef: create just a reference (maybe to a nonexisting object)
The functionality is designed in this way. We want to avoid situations where you get an error too late so that you can not find the real cause. This is why GetObjectById is directly checking the existance of the data.
What do you want to achieve? Why do you think a referenc to non existing data makes sense? You may have a constraint defined on the reference anyway and you will get the exception later in the commit call. And there is really no chance to handle it.
the Telerik team
My goal is described in the first post of this thread: I want to set the reference to an object of which I know its ID - but not the object itself. It is the ID of the existing object - so setting this reference makes sense. I have everything I need to set this reference from the SQL point of view: the ID of the object. But it is not enough from the ORM point of view: I still haven't got the object. And to get this object I must access the database - which is absolutely unneccessary.
This is the problem.
The solution is to be able to create a dummy (ghost, fake - call it as you want) object just with its ID set - and nothing else. And with no database access.
We have observed your scenario as well as some other similar cases and as a result, giving access to the foreign key fields directly is already part of our to-do list. A temporary workaround for our case is to manually write some code for the desired functionality and that is a field and a property accessing the CustomerID field as well as an app.config entry for the field.
The code should have the following form in the .cs files:
the app.config entry part of the Order class section should be as the following:
You should also add the following code in the set method of the Customer reference property. The reason for this is that currently, when you have a direct access to the foreign key, it's value is always the one going in the database.
We are sorry for the delayed answer and once again I notice that this workaround is only temporary, in future the process will be much more automatized and manual code-behind or calling GetObjectById() will not be required in these circumstances.
the Telerik team
Thanks a lot for the detailed discussion of this issue.