New Posts New Posts RSS Feed: AddToManager problem
  FAQ FAQ  Forum Search   Calendar   Register Register  Login Login

AddToManager problem

 Post Reply Post Reply
Author
cjohnson84 View Drop Down
Groupie
Groupie


Joined: 24-Sep-2009
Location: Akron, Ohio
Posts: 44
Post Options Post Options   Quote cjohnson84 Quote  Post ReplyReply Direct Link To This Post Topic: AddToManager problem
    Posted: 15-Jul-2010 at 12:25pm
I've retrieved entities from my database via my DevForce Silverlight data model.  The actual database table has a PK column and it is defined as a tinyint.
 
The retrieved entities are bound to a datagrid in which I allow new rows to be added.  My event handler is as follows:
 
private static void C1DataGrid_BeginningNewRow(object sender, DataGridBeginningNewRowEventargs e)
{
    try
    {
        BaseEntity baseEntity = e.Item as BaseEntity;
        baseEntity.EntityAspect.AddToManager();
    }
 
    catch
    {
        throw;
    }
}
 
The AddToManager call is failing with the following error "Value was either too large or too small for an unsigned byte".
 
We have other tables that have a PK column as an int that work fine.  What happens is when I say add a new row in the datagrid I am returned a number such as -100.  Eventually when I commit the entities back to the database the PK column value is resolved.
 
How do I handle this in the case where the PK on the table is a datatype such as a tinyint?
Back to Top
ting View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 27-Mar-2009
Location: San Francisco
Posts: 427
Post Options Post Options   Quote ting Quote  Post ReplyReply Direct Link To This Post Posted: 16-Jul-2010 at 6:50pm
Correct, the process is as follows:  Your IIdGenerator will create a temporary ID for the new entity on the client so that the navigation relationships can be set up.  When the entity is saved, it gets assigned a permanent ID by the server and the object graph is fixed up to maintain all the relationships.
 
What you need to do is to modify your IIDGenerator so that it returns temporary IDs that fit into a tinyint.  These IDs must also not conflict with an existing ID.  Your permanent IDs could start from 0, and temporary IDs work down from 255, but this obviously gives you very little room to work with.
 
Back to Top
cjohnson84 View Drop Down
Groupie
Groupie


Joined: 24-Sep-2009
Location: Akron, Ohio
Posts: 44
Post Options Post Options   Quote cjohnson84 Quote  Post ReplyReply Direct Link To This Post Posted: 20-Jul-2010 at 8:00am
Thank you for the response!  I'm stuck with the 0-255 range since that's how the database was designed.
 
So I've written an IdGenerator class and I've added it to one of my assemblies that has a reference to my domain model on the client side (CMISModelSL).  First off, is this where I need to place the IdGenerator class?  I believe it is if I understand the documentation correctly.  Second, is there a way to tell if my generator is being called?  I placed breakpoints in my IdGenerator class but none of them are ever reached.  I make the following call:
 
manager.GenerateId(batchStatus, dataEntityProperty)
 
and it results in the same error from my initial post:
 
"Value was either too large or too small for an unsigned byte"
 
 
Back to Top
cjohnson84 View Drop Down
Groupie
Groupie


Joined: 24-Sep-2009
Location: Akron, Ohio
Posts: 44
Post Options Post Options   Quote cjohnson84 Quote  Post ReplyReply Direct Link To This Post Posted: 20-Jul-2010 at 12:10pm
I've been playing with this some more and reading the documentation again and I'm more confused than I was before.  The developer's guide states on page 203 that "Unless the primary key is an identity column, DevForce doesn't know how to generate an object's primary key identifier(s)".
 
Also the API documentation for the method GenerateId states "If you are using a SQL Server Identity property you do not need to call GenerateId for the property".
 
In my case our "BatchStatus" database table has a PK column named "ID" that is defined as an identity key and the datatype is "tinyint".
 
Since the ID column is an identity column I'm confused why I need to call GenerateId at all?
 
When I run my code the GenerateId call fails with the error mentioned above (Value was either too large...).
 
The stacktrace is as follows:
 
at System.Convert.ToByte(Int64 value)
   at System.Int64.System.IConvertible.ToByte(IFormatProvider provider)
   at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
   at IdeaBlade.EntityModel.StoreGeneratedIdGenerator.GetNextTempId(DataEntityProperty property)
   at IdeaBlade.EntityModel.EntityManager.GenerateId(Object entity, DataEntityProperty entityProperty)
   at IdeaBlade.EntityModel.EntityManager.UpdatePkIfNeeded(EntityWrapper wrapper)
   at IdeaBlade.EntityModel.EntityManager.AddEntityWrapper(EntityWrapper wrapper)
   at IdeaBlade.EntityModel.EntityAspect.AddToManager()
   at OrianaHouse.CMIS.UI.UIFactory.C1DataGrid_BeginningNewRow(Object sender, DataGridBeginningNewRowEventArgs e)
 
In the stacktrace it looks like the "GetNextTempId" method was called on the "StoreGeneratedIdGenerator" object instead of my custom IdGenerator.  The error is clearly occurring when the code tries to convert an Int64 value to a Byte value.  Why was an Int64 value passed in in the first place if the column is defined as a tinyint?
 
Does DevForce assume all identity columns are Int64?
 
So in summing up I am now confused as to whether or not I even need a custom IdGenerator.  If I do need a custom IdGenerator I'm not sure where to put it in my project.  As of now I've placed it everywhere except for the DataModel itself on the server side and everytime I run my code the built-in IdGenerator seems to be called instead of my own.
 
Could someone shed some light on where I'm going wrong here.  I think I'm missing something very basic here.  Thanks in advance!
 
 
Back to Top
kimj View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 1391
Post Options Post Options   Quote kimj Quote  Post ReplyReply Direct Link To This Post Posted: 20-Jul-2010 at 6:25pm
DevForce automatically "handles" Identity primary keys in two respects:  1) you do not need to implement a custom IIdGenerator, and 2) you do not need to call EntityManager.GenerateId for entities with Identity keys, since DevForce will do this for you when the entity is added to the EntityManager.
 
The problem is not that DevForce assumes all identity columns are Int64, but it does assume that the target column can accept temporary values beginning with -100 and decrementing by 1.  The exception here is thrown because we are trying to convert the value -100 into a Byte, which only accepts values from 0 to 255.
 
Unfortunately, we don't have a workaround for you at the moment.  A custom IIdGenerator will not be used by DevForce for Identity columns, so this won't solve the problem.  Ideally you should be able to implement a custom IIdentityIdGenerator, or sub-class the internal DevForce StoreGeneratedIdGenerator, but that is not currently supported.   Please contact support directly to discuss options.
 
 
 
 
 
 
Back to Top
cjohnson84 View Drop Down
Groupie
Groupie


Joined: 24-Sep-2009
Location: Akron, Ohio
Posts: 44
Post Options Post Options   Quote cjohnson84 Quote  Post ReplyReply Direct Link To This Post Posted: 21-Jul-2010 at 7:58am
Thank you for the detailed explanation.  This is starting to all make some sense.
 
So I'm actually making some progress now.  We've removed the Identity declaration off of the column in our database table and updated our DataModel.  Interestingly enough we had to go into the model and change the "StoreGeneratedPattern" property of the column from "Identity" to "None" manually.  The update model operation didn't catch this (another bug?).  We found that this change was required as well otherwise our NumericIdGenerator was never called.  DevForce always assumed an identity column even though in the database it was no longer an identity column.
 
Anyways, now that my IdGenerator is being called I'm running into the following error when I try to commit to the database:
 
"The formatter threw an exception while trying to deserialize the message:
There was an error while trying to deserialize parameter http://ideablade.com/EntityModel:workState.
The InnerException message was 'Element 'http://ideablade.com/EntityModel:NextIdGenerator' contains
data from a type that maps to the name 'http://schemas.datacontract.org/2004/07/OrianaHouse.CMIS.UI:NumericIdGenerator'.
The deserializer has no knowledge of any type that maps to this name.
Consider using a DataContractResolver or add the type corresponding to 'NumericIdGenerator' to the list of known types -
for example, by using the KnownTypeAttribute attribute or by adding it to the list of known types passed to DataContractSerializer.'.
Please see InnerException for more details."
 
I'm guessing here that my NumericIdGenerator is not being found on the server side?  This leads me once again to one of the questions I asked previously in this thread.  Where do I need to place my NumericIdGenerator so that it can be seen on both the Client and Server side?
 
Currently I have the NumericIdGenerator in my main interface assembly on the Client side.  This assembly contains a reference to my domain model assembly named "CMISModelSL".
 
On the server side I went ahead and just placed the NumericIdGenerator in the data model itself called "CMISModel".  I tried the KnownTypeAttribute and that didn't make a difference.
 
Below are screenshots of my project structure:
 
 
 
Back to Top
kimj View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 1391
Post Options Post Options   Quote kimj Quote  Post ReplyReply Direct Link To This Post Posted: 21-Jul-2010 at 10:00am
The problem with having to manually change the conceptual model in the EDM after an "Update from Database" is a limitation in the EDM Designer itself - http://msdn.microsoft.com/en-us/library/cc716697.aspx. 
 
The IdGenerator should be defined on both client and server sides, and an instance holding the temporary Ids will be transmitted from the client to the server when doing a SaveChanges.   The class should be marked with a DataContract attribute, and any members needed on the server, such as temporary ids, should be marked with the DataMember attribute.  These members should also be public in Silverlight applications.  You don't need to mark up the class as a known type. 
 
We have a sample NumericIdGenerator for use in Silverlight applications in the MiniDemos for Business Object Persistence (although this might not be available until version 6.0.4 ships - if you can't find it in your Learning Resources we can send it to you).
Back to Top
cjohnson84 View Drop Down
Groupie
Groupie


Joined: 24-Sep-2009
Location: Akron, Ohio
Posts: 44
Post Options Post Options   Quote cjohnson84 Quote  Post ReplyReply Direct Link To This Post Posted: 21-Jul-2010 at 10:08am
Thank you again for the detailed explanations!  I really appreciate your help on this!  Could you please send me the NumericIdGenerator that you referred to as I don't have it.  My email is chadcjohnson@orianahouse.org.
 
 
Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down