- Home
- LLBLGen Pro
- LLBLGen Pro Runtime Framework
Entity Change : How do I do this?
Joined: 08-Sep-2004
Problem: How do I compare 2 entities?
Background:
Table structure: table A and table B have 1:1 relation, table A and table C have 1:n relation. For B, the primary key and foreign key is the same.
What I want to do is: During updation, if entity A has changed then I want to insert a row in C, and if entity B has changed I add another row in C.
Code:
if (_a.ID == 0)
throw new ORMEntityValidationException("Validation failed: ID not set.",_a);
_a.EntityValidatorToUse = new AValidator();
_a.Validate();
using (DataAccessAdapter adapter = new DataAccessAdapter())
{
adapter.StartTransaction(IsolationLevel.ReadCommitted, "Save Case");
try
{
PrefetchPath2 path = new PrefetchPath2((int)EntityType.AEntity);
path.Add(AEntity.PrefetchPathB);
AEntity _olda = new AEntity(_a.ID);
adapter.FetchEntity(_oolda, path);
_olda.Fields = _a.Fields;
if (_a.B != null)
_olda.B.Fields = _a.B.Fields;
CEntity _c;
if (_olda.IsDirty)
{
_c = new CEntity();
_c.ActionDate = DateTime.Now;
_c.ActionTarget = "Case";
_c.ActionType = "UPD";
_c.ActionUser = (_user.Trim() != String.Empty)?_user:"Unknown";
_a.C.Add(_c);
}
if (_olda.B.IsDirty == true)
{
_c = new CEntity();
_c.ActionDate = DateTime.Now;
_c.ActionTarget = "Tracking";
_c.ActionType = "UPD";
_c.ActionUser = (_user.Trim() != String.Empty)?_user:"Unknown";
_a.C.Add(_c);
}
_a.IsNew = false;
_a.B.IsNew = false;
adapter.SaveEntity(_a, true);
adapter.Commit();
}
catch
{
adapter.Rollback();
throw;
}
}
I would appreciate any criticism and/or suggestions.
Thanks
vikramka wrote:
Problem: How do I compare 2 entities?
Background:
Table structure: table A and table B have 1:1 relation, table A and table C have 1:n relation. For B, the primary key and foreign key is the same.
What I want to do is: During updation, if entity A has changed then I want to insert a row in C, and if entity B has changed I add another row in C.
You can check if an entity is changed by checking its IsDirty flag. This is the most easiest way to check if an entity is changed.
Code:
if (_a.ID == 0) throw new ORMEntityValidationException("Validation failed: ID not set.",_a);
You can also write an IEntityValidator implementing class which does this checking for you and throws this exception on _a.Validate(), i.e. in the AValidator() class below!
_a.EntityValidatorToUse = new AValidator(); _a.Validate(); using (DataAccessAdapter adapter = new DataAccessAdapter()) { adapter.StartTransaction(IsolationLevel.ReadCommitted, "Save Case"); try { PrefetchPath2 path = new PrefetchPath2((int)EntityType.AEntity); path.Add(AEntity.PrefetchPathB); AEntity _olda = new AEntity(_a.ID); adapter.FetchEntity(_oolda, path);
Don't fetch inside a transaction. Fetch first, then start the transaction.
_olda.Fields = _a.Fields; if (_a.B != null) _olda.B.Fields = _a.B.Fields;
I'm not sure what're trying to do here. Copying fields can be done like: _olda.B.Fields = ((EntityFields2)_a.B.Fields).Clone();
CEntity _c; if (_olda.IsDirty) { _c = new CEntity(); _c.ActionDate = DateTime.Now; _c.ActionTarget = "Case"; _c.ActionType = "UPD"; _c.ActionUser = (_user.Trim() != String.Empty)?_user:"Unknown"; _a.C.Add(_c); } if (_olda.B.IsDirty == true) { _c = new CEntity(); _c.ActionDate = DateTime.Now; _c.ActionTarget = "Tracking"; _c.ActionType = "UPD"; _c.ActionUser = (_user.Trim() != String.Empty)?_user:"Unknown"; _a.C.Add(_c); } _a.IsNew = false; _a.B.IsNew = false; adapter.SaveEntity(_a, true);
SaveEntity already starts a transaction, so you don't have to explictly start one. Saves you some code
Joined: 08-Sep-2004
Frans, Thanks for your reply. More questions spring to my mind. Hope you dont mind indulging me a little more.
Otis wrote:
vikramka wrote:
Problem: How do I compare 2 entities?
Background:
Table structure: table A and table B have 1:1 relation, table A and table C have 1:n relation. For B, the primary key and foreign key is the same.
What I want to do is: During updation, if entity A has changed then I want to insert a row in C, and if entity B has changed I add another row in C.
You can check if an entity is changed by checking its IsDirty flag. This is the most easiest way to check if an entity is changed.
The entity doesn't exist in memory in web apps, so I have to build it all over again, in which case every "touched" field "isChanged" and then I have to set the isnew to false to update it. I thought about putting it in session while editing, but what if the user is editing more than 1 entity in the same session. Which is why in the BL Manager class I have a Save method, which retrieves the entity first, compares it with the entity which has been passed in and creates more entities (C) only if there has been a change in A or B.
Otis wrote:
Code:
if (_a.ID == 0) throw new ORMEntityValidationException("Validation failed: ID not set.",_a);
You can also write an IEntityValidator implementing class which does this checking for you and throws this exception on _a.Validate(), i.e. in the AValidator() class below!
![]()
Since AValidator is also used during add, and ID is auto increment, I thought it best to leave it out of AValidator class. But maybe in AValidator I can check if isNew == false to check for ID existence. What do you think?
Otis wrote:
_a.EntityValidatorToUse = new AValidator(); _a.Validate(); using (DataAccessAdapter adapter = new DataAccessAdapter()) { adapter.StartTransaction(IsolationLevel.ReadCommitted, "Save Case"); try { PrefetchPath2 path = new PrefetchPath2((int)EntityType.AEntity); path.Add(AEntity.PrefetchPathB); AEntity _olda = new AEntity(_a.ID); adapter.FetchEntity(_oolda, path);
Don't fetch inside a transaction. Fetch first, then start the transaction.
Shouldn't I be worried that the entity might be changed before I start the transaction later on but after I have read it?
Otis wrote:
_olda.Fields = _a.Fields; if (_a.B != null) _olda.B.Fields = _a.B.Fields;
I'm not sure what're trying to do here. Copying fields can be done like: _olda.B.Fields = ((EntityFields2)_a.B.Fields).Clone();
This worked. Thanks.
vikramka wrote:
Frans, Thanks for your reply. More questions spring to my mind. Hope you dont mind indulging me a little more.
Otis wrote:
vikramka wrote:
Problem: How do I compare 2 entities?
Background:
Table structure: table A and table B have 1:1 relation, table A and table C have 1:n relation. For B, the primary key and foreign key is the same.
What I want to do is: During updation, if entity A has changed then I want to insert a row in C, and if entity B has changed I add another row in C.
You can check if an entity is changed by checking its IsDirty flag. This is the most easiest way to check if an entity is changed.
The entity doesn't exist in memory in web apps, so I have to build it all over again, in which case every "touched" field "isChanged" and then I have to set the isnew to false to update it. I thought about putting it in session while editing, but what if the user is editing more than 1 entity in the same session. Which is why in the BL Manager class I have a Save method, which retrieves the entity first, compares it with the entity which has been passed in and creates more entities (C) only if there has been a change in A or B.
Ok, though I think you can of course use a 'per page' small entity cache in which you store the entities in the session, for example you store the entities in a hashtable and you store their' ObjectID's in the viewstate. You get a postback, read the ObjectID's and with these ObjectID's you get the entities back from the hashtable stored in the session.
Otis wrote:
Code:
if (_a.ID == 0) throw new ORMEntityValidationException("Validation failed: ID not set.",_a);
You can also write an IEntityValidator implementing class which does this checking for you and throws this exception on _a.Validate(), i.e. in the AValidator() class below!
![]()
Since AValidator is also used during add, and ID is auto increment, I thought it best to leave it out of AValidator class. But maybe in AValidator I can check if isNew == false to check for ID existence. What do you think?
If ID is an identity field, then it will fail when saving indeed in other situations where it should ignore it.
Otis wrote:
_a.EntityValidatorToUse = new AValidator(); _a.Validate(); using (DataAccessAdapter adapter = new DataAccessAdapter()) { adapter.StartTransaction(IsolationLevel.ReadCommitted, "Save Case"); try { PrefetchPath2 path = new PrefetchPath2((int)EntityType.AEntity); path.Add(AEntity.PrefetchPathB); AEntity _olda = new AEntity(_a.ID); adapter.FetchEntity(_oolda, path);
Don't fetch inside a transaction. Fetch first, then start the transaction.
Shouldn't I be worried that the entity might be changed before I start the transaction later on but after I have read it?
That's always a possibility. I've to check if readers block writers in a transaction, though I thought the select's in a transaction don't place X locks on a row so another transaction could write data in those records.
Joined: 08-Sep-2004
Otis wrote:
Ok, though I think you can of course use a 'per page' small entity cache in which you store the entities in the session, for example you store the entities in a hashtable and you store their' ObjectID's in the viewstate. You get a postback, read the ObjectID's and with these ObjectID's you get the entities back from the hashtable stored in the session.
![]()
Thats smart, thanks for the idea. This means I can do without reading the database before updation.
Otis wrote:
That's always a possibility. I've to check if readers block writers in a transaction, though I thought the select's in a transaction don't place X locks on a row so another transaction could write data in those records.
You are right, reads probably dont place locks.
Thanks for all your help. I had been racking my brain all last week and trying out so many different things, but hashtable idea seems the most elegant one. So its back to rewriting my code again.
Otis wrote:
That's always a possibility. I've to check if readers block writers in a transaction, though I thought the select's in a transaction don't place X locks on a row so another transaction could write data in those records.
As far I am aware, readers block writers if the read happen from within a transaction and the IsolationLevel is "repeatable read" or "serializable"...
vikramka wrote:
The entity doesn't exist in memory in web apps, so I have to build it all over again
I just notice that you are using "_entityName" which gives me the impression that you are using a module or class level vairable to hold the entity object. This may not apply, but you must ensure that there is only single threaded access to the entity. Entities are not thread safe and you need to be careful about what threads will call methods on your object as the the entity is shared with all methods.
Joined: 08-Sep-2004
Marcus wrote:
I just notice that you are using "_entityName" which gives me the impression that you are using a module or class level vairable to hold the entity object. This may not apply, but you must ensure that there is only single threaded access to the entity. Entities are not thread safe and you need to be careful about what threads will call methods on your object as the the entity is shared with all methods.
I see what you mean, marcus. All underscores have been removed now (from my code that is)