- Home
- LLBLGen Pro
- LLBLGen Pro Runtime Framework
multi-database issues
i'm noticing a fair few issues when trying to use llblgen in a scenario of multiple db's
say i grab a collection of entities from dbA, and then want to save it into the identical db (schema) dbB
i tried:
adapter1.FetchEntityCollection(list, ...)
then
adapter2.SaveEntityCollection(list, ...)
of course, it doesn't work because they entities aren't dirty. so i mark them as dirty (a loop over all of them! argh!) and it still doesn't work :|
any ideas here? am i trying something that isn't well supported? or am i just an idiot? appreciate any insight.
for example, i don't understand why the 'save' logic can't check to see if the entity was saved in the database of the adapter provided not just "in general". if it had that sort of insight; life would be much better. i really hope it does and i've missed it, because i love llblgen and this is soul-destroying at the moment
yes; tried with IsNew and without, both result in silent crashes of my service
also when i wrap the fetch/save in a TransactionScope i get an "ORMQueryConstructionException", and earlier i got a deadlock :| this is just ridiculous. i really hope it is all my fault.
Please give us something we can reproduce. Post the relevant information (exception exact message, stack trace, runtime libraries version, real snippets, description of the scenario, etc.). Follow these guidelines: http://www.llblgen.com/TinyForum/Messages.aspx?ThreadID=7722
Thanks
using(TransactionScope scope = new TransactionScope()){
using(DataAccessAdapter adapter = corepool.Get()){
adapter.FetchEntityCollection(entities, bucket, 0, null, null);
}
// S1
foreach(Entity entity in entities){
entity.IsNew = true;
entity.IsDirty = true;
}
using(DataAccessAdapter adapter = mypool.Get()){
int saved = adapter.SaveEntityCollection(entities, true, true);
}
// END S1
foreach(Entity entity in entities){
entity.Fields["Some Field"].CurrentValue = 1;
entity.IsDirty = true;
}
using(DataAccessAdapter adapter = corepool.Get()){
int saved = adapter.SaveEntityCollection(entities);
}
scope.Complete();
}
SD.LLBLGen.Pro.ORMSupportClasses.ORMQueryConstructionException: The insert query doesn't contain any fields.
at SD.LLBLGen.Pro.DQE.SqlServer.DynamicQueryEngine.CreateSingleTargetInsertDQ(IEntityFieldCore[] fields, IFieldPersistenceInfo[] fieldsPersistenceInfo, IDbConnection connectionToUse, Dictionary`2& fieldToParameter) in D:\development\lemmity\trunk\llblgen pro\RuntimeLibraries\Sourcecode\Net2.x\SqlServerDQE\DynamicQueryEngine.cs:line 270
at SD.LLBLGen.Pro.ORMSupportClasses.DynamicQueryEngineBase.CreateInsertDQ(IEntityFieldCore[] fields, IFieldPersistenceInfo[] fieldsPersistenceInfo, IDbConnection connectionToUse) in D:\development\lemmity\trunk\llblgen pro\RuntimeLibraries\Sourcecode\Net2.x\ORMSupportClasses\DynamicQueryEngineBase.cs:line 167
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.CreateInsertDQ(IEntity2 entityToSave, IFieldPersistenceInfo[] persistenceInfoObjects) in D:\development\lemmity\trunk\llblgen pro\RuntimeLibraries\Sourcecode\Net2.x\ORMSupportClasses\DataAccessAdapterBase.cs:line 4293
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.PersistQueue(List`1 queueToPersist, Boolean insertActions) in D:\development\lemmity\trunk\llblgen pro\RuntimeLibraries\Sourcecode\Net2.x\ORMSupportClasses\DataAccessAdapterBase.cs:line 1490
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.SaveEntityCollection(IEntityCollection2 collectionToSave, Boolean refetchSavedEntitiesAfterSave, Boolean recurse) in D:\development\lemmity\trunk\llblgen pro\RuntimeLibraries\Sourcecode\Net2.x\ORMSupportClasses\DataAccessAdapterBase.cs:line 2820
[my code]
you will note that in the section "S1" if i remove the 'isnew' and 'isdirty' loop, the 'saveEntities' method returns 0. otherwise, if i keep them, it crashes.
you will note that i open 3 adapters in the scope. the S1 one refers to a different database.
i did also see a deadlock, when not calling '.isDirty' i believe.
LLBLGen Pro 2.0.0.0 Final
I see now.
Fields get saved if they are dirty (IsChanged = true).
When you set IsDirty = true for an entity, you should set the IsChanged = true for the required fields to be saved. So you have to iterate the fields for each entity and stay them as changed.
There is no "IsChanged" property. iterating over all the items in the list so many times really is ridiculous. at this time i'm thinking of doing all in this a stored procedure; but i don't see why LLBLGen is so adverse to this case :|
I'm sorry, my mistake I misread you.
There is an "IsChanged" on the fields (not the entity)
so now my code is like so:
foreach(Entity entity in entities){
entity.IsNew = true;
entity.IsDirty = true;
foreach(IEntityFieldCore field in entity.Fields ){
field.IsChanged = true;
}
}
and this works.
but seriously; this is just crazy!
imagine i have 1,000 entities here? with just 10 fields in the table! that's 10,000 loops! just to get it saved into a new db? please, is there a better way to do this using llblgen? or do i give up and go to a stored procedure?
silky wrote:
I'm sorry, my mistake I misread you.
There is an "IsChanged" on the fields (not the entity)
so now my code is like so:
foreach(Entity entity in entities){ entity.IsNew = true; entity.IsDirty = true; foreach(IEntityFieldCore field in entity.Fields ){ field.IsChanged = true; } }
and this works.
but seriously; this is just crazy!
imagine i have 1,000 entities here? with just 10 fields in the table! that's 10,000 loops! just to get it saved into a new db? please, is there a better way to do this using llblgen? or do i give up and go to a stored procedure?
replication of data between databases is often done by using special tools for that, like bulkcopy tools or publish/subscribe scenario's. These typically are more efficient because the database engine is optimized for them (as they bypass all sql stacks mostly).
What you want is that you load the data in an entity object, and make it look like you've done all the field setting by hand. The entities don't know from which db they came from, you have to mark the fields as if you set all the data by hand, which is done by looping over the fields indeed.
You can optimize this though, with a trick . You can fetch the data with a datareader, and use a custom DataProjectorToEntityCollection class to project the datareader rows onto new entities
All it takes is a tiny modification in the code of DataProjectorToEntityCollection (see sourcecode of runtime lib, it's very small, just an interface implementation), so what you then do is simply fetching a projection with your custom projector, which results in a collection with new entities, which you then can save in your new db, without precious loops
Thanks Frans.
I think I'll probably move to a SP for this, or maybe try what you talk about. I just think it would be nice if entities/collections could retain information about which DB they came from, thus helping me a bit
In my case replication process isn't really what I want, I'm just into shifting data between multiple db's which the same schema. I'm just a little saddened that LLBLGen doesn't seem to support it that well natively Though I think if I investigate the customising side of it, maybe I can get somewhere ...
If we add this as a method, like 'MarkAsNew' it will likely loop internally over all the fields, so it's as inefficient as your own code . WIth the custom projector and the datareader, you are more efficient, because there's no looping.
Of course mark as new is bad, what I'm suggesting would be to have the entity collection have a field like 'retrievedFromContext' which is db instance it was retrieved from. if it's different when saving (obviously it's some property on the adapter) then things such as 'IsNew' should automatically be disregarded; and the save should be pushed through.
what I'm suggesting would be to have the entity collection have a field like 'retrievedFromContext' which is db instance it was retrieved from.
This would break the abstraction of the design. Entities and entity collection are designed to be un aware of the underlying persistence storage, so persistence info are available at their level.
I think you should try the datareader plus projection suggestion.
But you are making them aware of their storage area; not me, and not only are you making them aware of it, you are making a mistake when you do it.
It seems like an obvious addition to the current (great) design of LLBLGen.
If you go so far to save persistance info as to 'the data was already saved' what you're really saying is 'there data was already saved [HERE]'. The obvious problem being that in my case 'HERE' changes.
That wouldn't help, as it would make things more problematic.
Consider databases A and B. Both contain entity E with ID 1, though you don't know if E with ID 1 is in B, you just know it's in A. You fetch it from A, and then yuo want to insert it in B. But that will give a problem, as it's not a new entity for B, it already exists.
So it has to fetch the entity from B as well, and merge the changes. This is an example of the fact that 'IsNew' only has meaning if you know what the semantic context is. Switching the DB all of a sudden can't trigger any changes in the entity graph, as the code which has to make the changes doesn't know what to change into what (which flags to set to true etc.).
Let that be a configuration.
If the ID's are to be 'matching' then allow them to be merged/updated in that case.
Otherwise, in my case, if the PKs don't matter for determining 'sameness' then enter it as new when the context is different.
So two examples:
Base case:
DB1 [e {pk1, b, c}] code [e {pk1, b, d}] DB2 [e {pk1, b, c}]
Scenario 1 - "SaveEntity" - with "Match up PK's = true"
DB2 [e {pk1, b, d}]
Scenario 2 - "Save Entity" with "Match up PK's = false"
DB2 [e {pk1, b, c}, e {pk2, b, d} ]
what problems do you see with this method?
silky wrote:
Let that be a configuration.
No, if something isn't doable, it's not left to configuration, it's simply not possible.
If the ID's are to be 'matching' then allow them to be merged/updated in that case.
Otherwise, in my case, if the PKs don't matter for determining 'sameness' then enter it as new when the context is different.
So two examples:
Base case:
DB1 [e {pk1, b, c}] code [e {pk1, b, d}] DB2 [e {pk1, b, c}]
Scenario 1 - "SaveEntity" - with "Match up PK's = true"
DB2 [e {pk1, b, d}]
Scenario 2 - "Save Entity" with "Match up PK's = false"
DB2 [e {pk1, b, c}, e {pk2, b, d} ] what problems do you see with this method?
it's a lot of extra overhead (has to go to the DB, as some entities the PK's might match, others might not match), and it likely fits just one project: yours.
Teh engine works with isnew, isdirty and ischanged flags, your idea is requiring that that is going out the window as there's another reason an entity could be saved. At first a single flag might look ok, but it's really on a per-entity level, and I definitely think that the few lines it would take to use the projection route as described above is much simpler, more efficient and more flexible.
shrug a new field per entity is not that bad IMHO, but perhaps there are cases where it's not acceptable, and that's fine.
I'm not suggesting a feature only for my project, what I am suggesting is that LLBLGen is actually wrong as it is. It works for most projects, but it's still wrong in the general case.
I.E. you can equally imagine I would be happy if LLBLGen didn't try and detect saved/inserted at all, and instead let me do that (via .insert/update instead of .save). But no, LLBLGen tries to retain this information; but it doesn't do it completely, and hence in the multi-db case it goes wrong.
I don't think that your projection method is bad, per-se, i haven't tried it. I do think that LLBLGen is broken in specific case though, and a fix is possible (as described). I'm sure there are some intricacies that would need to be worked out, but it certainly isn't in the category of "unsolvable problems"
silky wrote:
shrug a new field per entity is not that bad IMHO, but perhaps there are cases where it's not acceptable, and that's fine.
I'm not suggesting a feature only for my project, what I am suggesting is that LLBLGen is actually wrong as it is. It works for most projects, but it's still wrong in the general case.
No, it's not. Before we end up in a 'it is' ' no it isnt!' kind of game, let me explain
The entity class instance you have in memory is a container. the entity data which is inside the container is an entity instance, but it's a mirrored copy of the ACTUAL instance in the persistent storage (DB). So when you load the entity into memory, you actually make a copy, and in-memory you manipulate the copy. At the end you then tell the system to update the ACTUAL instance with the changes you made. This is done through the save logic.
Now, what you propose is that an entity instance, a mirrored copy, is suddenly used as a mirrored copy from two databases, but that's in theory not possible, you always need a projection for that. Simply flipping a switch isn't going to cut it, because the in-memory system has to be sure that the in-memory copy is in fact a copy of an entity inside the target db. If it can't be sure about that, the whole system fails.
A container with data which represents a new entity is actually a container with just data. An entity exists only when it's safely stored inside a db table, so having an object with data means nothing: it's not something you can rely on, it's just data. So the engine can only insert a new entity if it is indeed a new entity.
The tricks with the isnew, isdirty and ischanged flags are just to shortcut code like: CustomerEntity c = new CustomerEntity(); c.CustomerId = old.CustomerId; c.Companyname = old.Companyname; etc.
But it's still a trick: the data in 'old' in the code above has meaning, the data in 'c' doesn't, until it's saved. the code above copies the values over to the entity container 'c', as what's inserted is a new entity, not something which already exists.
I.E. you can equally imagine I would be happy if LLBLGen didn't try and detect saved/inserted at all, and instead let me do that (via .insert/update instead of .save). But no, LLBLGen tries to retain this information; but it doesn't do it completely, and hence in the multi-db case it goes wrong.
No it doesn't go wrong. It retains the information about the entity instance it mirrored in memory. It can't possibly say ANYTHING at all about another db where it didn't read any entity from! The whole system is based on the fact that it loaded entity E in container C and it manipulated the mirrored copy of E inside C, and it then can update the real instance of E, only because it knows that the mirrored copy came from that db.
I don't think that your projection method is bad, per-se, i haven't tried it. I do think that LLBLGen is broken in specific case though, and a fix is possible (as described). I'm sure there are some intricacies that would need to be worked out, but it certainly isn't in the category of "unsolvable problems"
![]()
Well, I definitely think that by adding your suggestion, the framework actually will be broken. The thing is that what you suggest is solely meant to bypass tedious logic, so to do something more quicker than it otherwise would take. BUt that doesn't mean it's based on solid reasoning, as there's IMHO no logical explanation why what you suggest is valid, logically speaking.
No, it's not. Before we end up in a 'it is' ' no it isnt!' kind of game, let me explain
great more then happy to have a discussion about it
The entity class instance you have in memory is a container. the entity data which is inside the container is an entity instance, but it's a mirrored copy of the ACTUAL instance in the persistent storage (DB).
well this isn't really true. the entity is only a mirror when 'IsNew' is false. i mean i can create a new Entity and set fields. this isn't a mirror of anything because i only created it now.
No it doesn't go wrong.
But it does go wrong. Logically wrong. Logically I am able to create a situation in which your layer doesn't save entities that aren't in a database when I call .Save(...). it just acts as if there is nothing to do. But it's only assuming that there is nothing to do, assuming that you use only ONE db.
A container with data which represents a new entity is actually a container with just data. An entity exists only when it's safely stored inside a db table, so having an object with data means nothing: it's not something you can rely on, it's just data. So the engine can only insert a new entity if it is indeed a new entity.
well it doesn't mean "nothing" to have data in an entity class without a backing entity in the db. it means that you have a new entity! the way you determine "new" is not right though. you determine new/changed based on only "no fields changed". now, as you know, this is true if there is only ever 1 db that an entity is saved against, but is not logically true when there is more then 1 db.
No it doesn't go wrong. It retains the information about the entity instance it mirrored in memory. It can't possibly say ANYTHING at all about another db where it didn't read any entity from! The whole system is based on the fact that it loaded entity E in container C and it manipulated the mirrored copy of E inside C, and it then can update the real instance of E, only because it knows that the mirrored copy came from that db.
you're right it can't say anything about a new db. which is why I present it as a 'match pk' option like you list above.
Let me ask you, if I was to create an entity and give it a pk, (manually) and set .isnew = false then .Save(...) it, would it update in the given db, or would it insert? my guess is update.
hence the situation would be exactly the same for when match pk = true and the db is changed. otherwise, when match pk = false and db is changed it just auto-inserts which the db context is changed (only then).
Well, I definitely think that by adding your suggestion, the framework actually will be broken. The thing is that what you suggest is solely meant to bypass tedious logic, so to do something more quicker than it otherwise would take.
yep it is, but also that's the whole point of your framework, if i may say to make my life eaiser
BUt that doesn't mean it's based on solid reasoning, as there's IMHO no logical explanation why what you suggest is valid, logically speaking.
well i disagree as shown above. for me it follows logically that if you try and claim any "IsNew" status about an entity that "IsNew" can ONLY have meaning against the same DB is was retrieved from. personally i'd be happy if "IsNew" was discarded when the db context changed, but to make the proposal more acceptable for others i think the "match pk" would be nice ...
silky wrote:
No, it's not. Before we end up in a 'it is' ' no it isnt!' kind of game, let me explain
great
more then happy to have a discussion about it
![]()
You've to understand that this is my last post on the matter.
The entity class instance you have in memory is a container. the entity data which is inside the container is an entity instance, but it's a mirrored copy of the ACTUAL instance in the persistent storage (DB).
well this isn't really true. the entity is only a mirror when 'IsNew' is false. i mean i can create a new Entity and set fields. this isn't a mirror of anything because i only created it now.
You didn't understand what I said. If an entity container contains data which isn't obtained from a db, it effectively means nothing at all.
No it doesn't go wrong.
But it does go wrong. Logically wrong. Logically I am able to create a situation in which your layer doesn't save entities that aren't in a database when I call .Save(...). it just acts as if there is nothing to do. But it's only assuming that there is nothing to do, assuming that you use only ONE db.
I'll stop here, as this gets tiresome, sorry. I explained in detail why it works the way it works and what the theoretical base is for how it works. If you still say it's wrong, it's only wrong in the context of your opinion because you look at it differently. You can keep saying that it's wrong, fine by me, but that doesn't make it right, IMHO. The system will keel over and be actually not fundamentally correct if I'd proceed with what you proposed.
Read my article about what I'm talking about: http://weblogs.asp.net/fbouma/archive/2006/08/23/Essay_3A00_-The-Database-Model-is-the-Domain-Model.aspx
You try to fiddle with in-memory objects, but the objects are totally irrelevant, they're simply buckets. What you want is a setting where I can say to any entity object (container) that the data inside it should be seen as something different, so obtained from a different semantical context (created by hand, obtained from a different db), which can only be done if the flags are manipulated and that's something that's inefficient and you can do that yourself.
With the projection code, you actually are doing things semantically correct, because you project the set from db A onto elements for db B. As there's a projection the data isn't the same and can be used immediately. Making changes to the core engine to make it look at the graph passed in as if it has been created differently, obtained from something else, causes a load of complex issues, one being that there's no theoretical base for what it is doing at that moment.
You've to understand that this is my last post on the matter.
Alright.
You didn't understand what I said. If an entity container contains data which isn't obtained from a db, it effectively means nothing at all.
You're right, I don't understand this 'effectively means nothing'. I do this (create an entity container 'xxEntity' which contains data that isn't from a db) all the time, and I call adapter.SaveEntity(foo) and it's saved. This is not 'effectively nothing' to me. But whatever, semantics is boring and useless.
What you want is a setting where I can say to any entity object (container) that the data inside it should be seen as something different, so obtained from a different semantical context (created by hand, obtained from a different db), which can only be done if the flags are manipulated and that's something that's inefficient and you can do that yourself.
Sorry but you are wrong here.
What I want is a field on the Entity which says which DB-context it was retrieved from. Only if this matches the db-context provided by the adapter are the fields 'IsNew' and so on considered relevant. If the db-context is not the same these fields are nonsensical and wrong.
I understand that the model of swapping DB's is one that LLBLGen doesn't seem to handle well, but you don't need to call this proposal wrong just unsupported. It (context-aware saving of 'IsNew') is clearly something that needs to be considered in the multi-db environment.
I'm a little surprised at your ignoring if this honestly. I mean obviously I am not hoping for this feature, or counting on it in any way; it would obviously take several months to get added. So don't look at my argument from that point of view; I'm just commenting on what is an obvious lack of support for the multi-db-world. I also totally disagree that the system would 'keel over' but you obviously have something different in mind then what I have.
Sadly I'll probably have to move away from LLBLGen and back to my own ORM (maybe a mix of both; not that I expect you to care) due to this and other reasons. It's just very frustrating because the whole 'IsNew' problem is one you've contrived inside LLBLGen! It doesn't need to be there [the problem, the feature itself is nice], nor does it need to hinder this development style I have.
In any case; I'll leave it at that. Can't say I'm not disappointed.
silky wrote:
You didn't understand what I said. If an entity container contains data which isn't obtained from a db, it effectively means nothing at all.
You're right, I don't understand this 'effectively means nothing'. I do this (create an entity container 'xxEntity' which contains data that isn't from a db) all the time, and I call adapter.SaveEntity(foo) and it's saved. This is not 'effectively nothing' to me. But whatever, semantics is boring and useless.
Until the entity is saved, it doesn't exist, that's the whole point. That's why it effectively means 'nothing' before it's saved. A 100 threads can cook up the same data in an entity container and not save it, does that mean the entity exists 100 times? No, it means it doesn't exist at all: I can't obtain it from another thread which didn't create it.
What you want is a setting where I can say to any entity object (container) that the data inside it should be seen as something different, so obtained from a different semantical context (created by hand, obtained from a different db), which can only be done if the flags are manipulated and that's something that's inefficient and you can do that yourself.
Sorry but you are wrong here.
Let's set one thing clear: if you're saying I'm wrong, the whole system is fundamentally flawed. And I definitely think that's not the case.
What I want is a field on the Entity which says which DB-context it was retrieved from. Only if this matches the db-context provided by the adapter are the fields 'IsNew' and so on considered relevant. If the db-context is not the same these fields are nonsensical and wrong.
They have a value, so if they're 'wrong', what set of values makes them 'right' again? That's unclear, hence the problem.
I understand that the model of swapping DB's is one that LLBLGen doesn't seem to handle well, but you don't need to call this proposal wrong just unsupported. It (context-aware saving of 'IsNew') is clearly something that needs to be considered in the multi-db environment.
Where does LLBLGen Pro make errors when it comes to swapping db's? You simply want to treat an entity INSTANCE (== data) as if it's obtained from multiple databases, or usable with multiple databases by looking at it from different angles. So I have ONE tuple with data, and if I look at it from the POV of DB A, it's a real entity and if I look at it from the POV of DB B it's a new entity. Sorry but if something is confusing, it's that.
If you want that behavior, create a new entity container for DB B, so there's no confusion.
I'm a little surprised at your ignoring if this honestly. I mean obviously I am not hoping for this feature, or counting on it in any way; it would obviously take several months to get added. So don't look at my argument from that point of view; I'm just commenting on what is an obvious lack of support for the multi-db-world. I also totally disagree that the system would 'keel over' but you obviously have something different in mind then what I have.
Adding this would make things fall apart because the same object with the same data is suddenly having multiple meanings, depending on the PoV you're looking at it from. Sorry, but that's not acceptable to me.
If you then want to call our system flawed I'm sorry, but I don't share that thought nor the reasoning behind it.
Sadly I'll probably have to move away from LLBLGen and back to my own ORM (maybe a mix of both; not that I expect you to care) due to this and other reasons. It's just very frustrating because the whole 'IsNew' problem is one you've contrived inside LLBLGen! It doesn't need to be there [the problem, the feature itself is nice], nor does it need to hinder this development style I have. In any case; I'll leave it at that. Can't say I'm not disappointed.
erm.... I think you're severily overreacting. The thing is solved by a very efficient projection routine which is a few lines of code and does the job what you want, and above all it's very flexible, you've total control over it. It also doesn't make the system's core fundamentals fall apart, as it's based on those fundamentals. Why don't you try it?
Until the entity is saved, it doesn't exist, that's the whole point. That's why it effectively means 'nothing' before it's saved. A 100 threads can cook up the same data in an entity container and not save it, does that mean the entity exists 100 times? No, it means it doesn't exist at all: I can't obtain it from another thread which didn't create it.
Right I agree. What does this have to do with the main discussion?
Let's set one thing clear: if you're saying I'm wrong, the whole system is fundamentally flawed. And I definitely think that's not the case.
I'm certainly not trying to draw that conclusion.
They have a value, so if they're 'wrong', what set of values makes them 'right' again? That's unclear, hence the problem.
It's not unclear. If you change to an unknown DB the values obviously become "unknown". You don't know if the Entity exists or not. So if you decide to "Save" and you have a PK set, and it's on a new db, then you need to ignore the "IsNew (and friends)" flag and check if it exists in the DB. If it does, you update it, if it doesn't, you insert it. What's wrong with that? If there is something wrong, why is it worse then what it currently does? What it currently does it assume that it does exist.
If you want that behavior, create a new entity container for DB B, so there's no confusion.
No confusion but terrible performance.
Adding this would make things fall apart because the same object with the same data is suddenly having multiple meanings, depending on the PoV you're looking at it from. Sorry, but that's not acceptable to me.
But you are currently only giving one window anyway. As if you can only look at the data from 1 db. I am saying, lets be honest about how it's looking when you look from various PoV. In the case where DB is the same, it's your model - "IsNew" is accurate and set to "false".
When looking at it from another adapter, "IsNew" is not known.
Actually, as I type I realise a problem (maybe this is what you speak of).
"IsNew" can be accessed without a database context. So perhaps the model I am thinking of would not be "entity.IsNew" but "adapter.IsEntityNew(entity)". This is probably quite a fundamental change, so I can understand hesitation in that. Still, I think it's more accurate representation of the "IsNew" quality.
Why don't you try it?
I will.