StrataFrame Forum

Compound Primary Keys

http://forum.strataframe.net/Topic1064.aspx

By Larry Caylor - 5/3/2006

The addition of support for compound primary keys is greatSmile. It would be nice if the framework added a ReadOnly CompoundPrimaryKey Property of type PrimaryKeyValue when generating the designer file for an object. This would save having to code a custom CompoundPrimaryKey property that could be used as a Tag value for listviews, etc. It would also be nice if SeekToPrimayKey included an overload that accepted type PrimayKeyValue instead of having to pass each PrimaryKey individually.

 

-Larry

By StrataFrame Team - 5/3/2006

Very valid points, Larry... I'll check on the SeekToPrimaryKey, but I think there is already an overload (or there should be). And there is already a GetPrimaryKeyValue() method on the business object that will return a PrimaryKeyValue object for the CurrentRow and an overload that will allow you to create one for a given row.



But, I'll check on the SeekToPrimaryKey and make sure it's in the next version Smile
By Larry Caylor - 5/3/2006

Ben,

Thanks for the info, I missed the updates to GetPrimaryKeyValue(). However I’m not clear on how I would use this to set a tag value for say a listview? If I create a custom CompundPrimaryKey Property for my BO, it appears in the drop down list of available tag fields. An Example would help.

-Larry

 

 

By StrataFrame Team - 5/3/2006

Oh, I gotcha... you need a property that is created that is bindable, not just the ability to get the property. Yes, I'll look into that Smile
By Charles R Hankey - 2/2/2012

I have a PK that is
Symol ( char(6) ) + Cntry_code ( char(2)) + from_date ( datetime ) + thru_date ( datetime ))
( I did NOT design it but I wish I knew where the kids of the guy who did go to school ... )


that I would love to use in a tag for a Listview for autonavigate. 

Suggestions? 
By Trent L. Taylor - 2/2/2012

Regardless of the list you are using, you can always use the tag of the row.  However, if you use the new StrataListView, there is a CustomData collection on each row that allows you store ANY type of data you want with a key.  I store all kinds of things in this as it is a collection and can store an data type of anything since it accepts an Object data type as the value.  The key is a string.  But by doing this, you can keep all of the logical data associated to a single row that you need.  It is a real life saver and something that I use all of the time...maybe that is why I put it in there Smile
By Charles R Hankey - 2/2/2012

Thanks Trent.  I've avoided Stratalistview as I could never find documentation after it first appeared three or so years ago.  And I don't believe at that time there were any builders.

Since I have about 25 listviews so far in this app using the original SF listview, I'd prefer for right now ( as in I have a code drop due at 6pm ) to sync movement in my current listview with the underlying BO.  Short of writing my own SelectedIndexChanged event code is there any way to leverage what the old SF Listview does to use a compound key like this?  I tried passing 4 tags, separated by commas - obviously that doesn't work .

Seems earlier posts in this thread hinted at some other approach?  I'm just not seeing it.

( I promise I will look at stratalistview but the pressure today would not make that an option )
By Edhy Rijo - 2/2/2012

Hi Charles,

Nice seeing you back in the forum, even thought not for happy reasons Hehe

If I understand your issue correctly, you have a regular SF ListView which you want it to navigate to the correct BO record automatically but the PK value cannot be stored properly in the ListView.Tag, am I right?  What type is your PK?

Could you create a View with a calculate field of your PK, create a BO using the view then use that BO in the ListView?
By Charles R Hankey - 2/2/2012

FWIW I did find the sample (thankfully in C# ) you created back in 2009 for Stratalistview.  I get how it works but there appears to be nothing like AutoNavigate so I am still pretty much on my own writing SelectedIndexChanged code. 

Unless you have some compound key magic on the old LV for me, it looks like I'm going through the lv.SelectedItems[0].Subitems[n].Text to create a search on the BO to get navigation. Sad
By Charles R Hankey - 2/2/2012

Hey Eddy

Key is char(6) + char(2) + datetime + datetime

If I understand you correctly, I would use a view that had one char column concatenating the four columns ( with datetimes changed to string ) for my listview.  Ok, that get's me a tag.  But the BO where I'm doing data entry will also need that column as a pk, no?  I think that's what autonavigate looks for is a pk.

Of course, I could make it a customfield property of the BO and then use my own code to navigate to that record,  but I'm not sure that's easier than just building my own bo search in the SelectedIndexChanged out of the four values.
By Edhy Rijo - 2/2/2012

Hi Charles,

I posted a sample in VB of a class I created based on the StrataListView in which I added some of the functionality we need.  Check it here:

http://forum.strataframe.net/FindPost28459.aspx
By Edhy Rijo - 2/2/2012

Charles R Hankey (2/2/2012)
If I understand you correctly, I would use a view that had one char column concatenating the four columns ( with datetimes changed to string ) for my listview.  Ok, that get's me a tag.  But the BO where I'm doing data entry will also need that column as a pk, no?  I think that's what autonavigate looks for is a pk.


Yes you can have a custom field property (CFP) that will have the string value of your PK combination, and you can use that CFP from that point forward anywhere in SF controls.  In your SELECT to grab the data, include the virtual column with the same name as the CFP and it will be there from you in the BO to use:
EX: "SELECT (Your PK Combination) AS cfp_MyCustomPK, * FROM MyTable"

In your BO, create a CFP and name it cfp_MyCustomPK.  Here is a sample (in VB) of one of my CFP which expect the field value from the SELECT statement:

    <Browsable(False),
     BusinessFieldDisplayInEditor(),
     Description("Transaction Reference No."),
     DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)>
     Public Overridable ReadOnly Property [cfp_ReferenceNo]() As Integer
         ' Check to see if datatable contains the custom field in
         ' case object was filled by a method that doesn't contain the field.
         Get
             Dim columnName As String = "cfp_ReferenceNo"
             If Me.CurrentDataTable.Columns.Contains(columnName) Then
                 If Not TypeOf (Me.CurrentRow.Item(columnName)) Is DBNull Then
                     Return CInt(Me.CurrentRow.Item(columnName))
                 Else
                     Return 0
                 End If
             Else
                 Return 0
             End If
         End Get
     End Property


Keep in mind, that you can tell the BO which is the PK column to use, also in the ListView you can select any field to be used for its Tag property.  There are many ways you can go with this.  If you will not be adding new records to this BO I prefer my approach of the CFP since you can use that all over the framework once you have it in the BO.
By Charles R Hankey - 2/2/2012

The whole point here is I want to sync a listview with BO.  Doesn't seem like it should be this hard, and using AutoNavigate it isn't - if there is a single PK.

But with a compound pk I am having to jump through hoops.

How can I get the BD to create the SQL Select and then get a result set that includes my 4 key fields mashed together?

I have created a cfp.  I assume that if I want to  use mykey as the tag in autonavigate in a listview I need to have it in the columns of the listview?  I'm missing something somewhere.  Will this cfp get populated when the bo is filled from the bd ?  If not, how do I get it to have a value before the listview is requeried? 

Obviously confused.

Am I creating the CFP correctly ?


        [Description("mykey"), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public string mykey
            {
            get
                {
               string _mykey = this.Symbol + this.Cntry_code + this.from_date.ToString()+this.thru_date.ToString();
                return _mykey;
                }
            set
                {
                value =  this.Symbol + this.Cntry_code + this.from_date.ToString()+this.thru_date.ToString();
                }
            }

        /// <summary>
        /// Provider property descriptors for the custom bindable fields
        /// </summary>
        protected override FieldPropertyDescriptor[] GetCustomBindablePropertyDescriptors()
            {
            //-- Return the array of property descriptors
            return new MicroFour.StrataFrame.Business.FieldPropertyDescriptor[]
            { new ReflectionPropertyDescriptor("mykey", typeof(boiSource100Symbology))};
            }



By Trent L. Taylor - 2/3/2012

OK.  A couple of things.  First, you are right in that this issomething that you can do several different ways.  To begin, there are a couple of things that Ineed to know.

  1. Is your CFP an actual field in the table or are you doing this as acustom field?
  2. If it is something stored in the table (or at least as a field in the datatable within the BO) then you can seek on it using the BO methods.

 So assuming that you have an actual field built within the BO that doeshave your CFP, then you can seek on it. That is the first thing that you need to get working before worryingabout the list.  The list is the easypart.  If you are just using the CFP as acustom field in the BO that DOESN’T have an actual field in the BO behind it,then you need to get that taken care of first. It doesn’t have to exist in the table in the database, but just in theDataTable in the BO.  The easiest way todo this would be to return it as part of a result set from a SPROC.


SELECT
               
MT.*,
                CAST((CAST(MT.mt_pkAS VARCHAR(20) + MT.mt_Code) AS VARCHAR(40) AS MyCFP
FROM MyTable AS MT


 Once you get here, it should be pretty smooth sailing.

By Charles R Hankey - 2/3/2012

Thanks Trent.  ( and please see other question re datetime searchfields being checked by default )

This column does not currently exist, so I see where I need to add it.  I am not pulling any data initailly but letting the bd handle it.  i had thought of creating a fill method on the BO

Select *, space(24) as mykey from Symbology where 1 = 2

and call it in parentformloading just to get the structure.  I did try it at one point but I may have had something else screwed up. 

Listview is working fine.  and I see mykey there.

Currently, since I don't have the column, I'm doing this which isn't working out so well as it does not seem to be refreshing the controls on the form.

I would much prefer seek, so I will try again to add the column to the BO.  I guess my confusion is that the BD is creating the SQL select, so I'm not seeing how to use an SP or other fill method to change the structure of datatable of the BO.  I have done it directly, but I am not veyr good an manipulating the BD.



private void lvSymbology_SelectedIndexChanged(object sender, EventArgs e)

{

ListView lv = (ListView)sender;

if (!(lv.FocusedItem == null) && lv.Items.Count > 0)

{

string _mykey = lv.FocusedItem.SubItems[13].Text.Trim();

string _name = lv.FocusedItem.SubItems[2].Text;

//boiSource100Symbology1.CurrentView.Sort = "mykey";

//boiSource100Symbology1.CurrentView.Find(_mykey);

foreach (boiSource100Symbology bo in boiSource100Symbology1.GetEnumerable())

{

if (bo.mykey == _mykey)

{

string myname = bo.Name;

int idx = boiSource100Symbology1.CurrentRowIndex;

this.Refresh();

return;

}

}

}

By Edhy Rijo - 2/3/2012

Hi Charles,
We have to have the whole picture in order to give you the faster, shorter advice. Whistling

Lets recap:
  1. You have Browser Dialog (BD) which will return one or several records.
  2. After selecting the record you want to work with from the BD, you will show those records in a ListView in the form?
  3. You want the ListView in the form to autonavigate its BO using your Custom Field Property (CFP)
If the above is correct, in which step are you having issues?
By Trent L. Taylor - 2/3/2012

Charles, call the Refresh on the BO to refresh the controls on the page.  That updates the bindings and refreshes the contents.
By Charles R Hankey - 2/3/2012

Thanks, I'll try that.

Regarding adding the column :

I called a fill method on the BO in parentformloading and saw the mykey column was now part of the currentdatatable.  The problem is that after calling the browse dialog, the currentdatatable no longer had the row.  BD creates it's own fillmethod.  How do I make sure the browse dialog either has that column or does not clobber the existing column?

Craate a view with the extra column, let the BD use that but still have the main BO get the column as I am doing with the dummy fillmethod?
By Trent L. Taylor - 2/3/2012

On the BrowseDialog, use a custom view to populate the BO.  This should come back as part of the results.  You can also fill from it as well.  The contents of the browse dialog don't necessarily reflect what comes back if you build custom fields within the dialog.  Another option is to create the field when you come out of the browse in the data table itself.  I have done this before.  Just write a method that checks to see if the data column exists within the BO.  If not, create it.


if(!MyBO.CurrentDataTable.Columns.Contains("MyField"))
{
    DataColumn col = new("MyField", typeof(string));
    col.DefaultValue = string.Empty;
    MyBO.CurrentDataTable.Columns.Add(col);
}
By Trent L. Taylor - 2/3/2012

On another note, you have two options on the checkbox on the date field.  Create an InfoPanel, which gives you access to the browse dialog window in which you could actually set this yourself.  The second option is for me to make a change to allow this as a new feature...which would require a new build and access to new releases.  I would go with #1 if I were you right now. Smile
By Charles R Hankey - 2/3/2012

Tried using a view with the extra column for the BD, but when it does a select for the main BO it still does not create that column.

Am I really being that thick?  I want a calculated column in a BO to persist after a BD gets records.

I am going to try the approach I outline below but I would still appreciate any insight on how to get data from a BD without redefining the structure of the currentdatatable back to the SQL table defined columns

In my biggest SF app to get around this problem I grab the PKs of the BD result, create a datatable that I pass to tablevalued parameter in an SP to get my records ( posted how to do this in user contributed samples a couple years ago. )

Works great for a single key.  The problem here is that I have a compound key ... so as i am writing this, it occurs to me that since I have the mykey in my BD result I can use that single value to pass in as a TVP to an SP that will return my records - and will have the row !

Will report my results.
By Charles R Hankey - 2/6/2012

FWIW I solved the problem like this:

created view for BD bov_Symbology that includes the column mykey which concats the 2 char and 2 datetime columns to a 24 char string mykey

This BD fills that view with its result. 

In browse diaglog closed I grab mykey from the results into List<string> .  That goes to FillbyStringList(List<string> pklist) fill menthod of the bo_Symbology, which also has a customfield property mykey which does the same concat in its accessor

The List<string> is built into a datatable and passed to a stored procedure which has a param that accepts a previously defined Table Valued Parameter ( see my explanation of this technique in User Updates and Samples)

It returns all rows where the 4 columns concatenated make = the char(24) value of the TVP row. ( using a join is easiest)

I have a TVP in the database for int keys as well. 

This is the best way I have found to work around what I see as some major shortcoming of the BD.