DataTable to Entity?

Posts   
 
    
iDave avatar
iDave
User
Posts: 67
Joined: 14-Jun-2010
# Posted on: 08-Oct-2010 03:12:19   

Hallo,

I'm fetching data into a XtraGrid using a TypedView as follows:


using (var staff = new StaffViewTypedView())
{
    using (var adapter = new DataAccessAdapter(_newMySqlConnString))
    {
        // Fetch All Active Staff Members
        adapter.FetchTypedView(staff.GetFieldsInfo(), staff, true);
        // Bind to BindingSource
        bindingStaff.DataSource = staff;
        // Bind to DataGridView
        gridControlListStaff.DataSource = bindingStaff;
        // Get Staff Count
        lblStaffCount.Caption = String.Format("Total: {0}", gridViewListStaff.RowCount.ToString());
    }
}

Which shows only: StaffName, UserName, Phone, Mobile, Mail, HireDate, BranchName, Active fields in the XtraGrid

StaffName is a concat of FName and LName fields from Staff table BranchName is the BName field from Branch table

The data displays in the XtraGrid without any problems.

But then when a user clicks on the Edit button, the following of course don't work:

var staffMember = (StaffEntity)gridViewListStaff.GetRow(rowHandle);

because it's an object of type 'System.Data.DataRowView'.

I'm using a workaround to get some values from the selected row and based on that get the StaffEntity, but I think there's a better solution for that.

Then I though I can fetch the data including only the fields I want, like this:


// Filter by Active Staff and Add Branch-Staff Relation
var bucket = new RelationPredicateBucket();
var staffFilter = new PredicateExpression();
IPredicate filterElement = (StaffFields.Active == true);
staffFilter.Add(filterElement);
bucket.PredicateExpression.Add(staffFilter);
bucket.Relations.Add(BranchEntity.Relations.StaffEntityUsingBranchId);

// Include Fields
var includedFields = new ExcludeIncludeFieldsList();
includedFields.ExcludeContainedFields = false;
includedFields.Add(new EntityField2("StaffName", StaffFields.Fname + " " + StaffFields.Lname, typeof(string)));
includedFields.Add(StaffFields.UserName);
includedFields.Add(StaffFields.Curp);
includedFields.Add(StaffFields.Phone);
includedFields.Add(StaffFields.Mobile);
includedFields.Add(StaffFields.HireDate);
includedFields.Add(BranchFields.Name);
includedFields.Add(StaffFields.Mail);
includedFields.Add(StaffFields.Active);

PrefetchPath2 path = new PrefetchPath2(EntityType.StaffEntity);
path.Add(StaffEntity.PrefetchPathBranch, includedFields);

using (var staff = new EntityCollection<StaffEntity>())
{
    var sorter = new SortExpression(StaffFields.Fname | SortOperator.Ascending);

    using (var adapter = new DataAccessAdapter(_newMySqlConnString))
    {
        // Fetch a Collection of Staff Members
        adapter.FetchEntityCollection(staff, bucket, 0, sorter, path, includedFields);
        // Bind to BindingSource
        bindingStaff.DataSource = staff;
        // Bind to DataGridView
        gridControlListStaff.DataSource = bindingStaff;
        // Get Staff Count
        lblStaffCount.Caption = String.Format("Total: {0}", gridViewListStaff.RowCount.ToString());
    }
}

But it doesn't work. I got an Unhandled Exception:

"Index was out of range. Must be non-negative and less than the size of the collection."

And is also a lot more of code, so I was wondering if there's a way to get an entity from a selected row using the first code or perhaps there's another efficient way of achieving what I want.

PS I'm using LLBLGen Pro v3 (October 1st, 2010)

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 08-Oct-2010 06:15:06   

Where do you get the exception? Please post the complete error message and stack trace.

David Elizondo | LLBLGen Support Team
iDave avatar
iDave
User
Posts: 67
Joined: 14-Jun-2010
# Posted on: 08-Oct-2010 06:31:55   

daelmo wrote:

Where do you get the exception? Please post the complete error message and stack trace.

This is the error I got:

Unhandled Exception, Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index; 
   at System.ThrowHelper.ThrowArgumentOutOfRangeException()
   at SD.LLBLGen.Pro.ORMSupportClasses.EntityViewBase`1.GetEntityAtIndex(Int32 index) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v3.0\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\Core\EntityViewBase.cs:line 624
   at SD.LLBLGen.Pro.ORMSupportClasses.EntityViewBase`1.System.Collections.IList.get_Item(Int32 index) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v3.0\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\Core\EntityViewBase.cs:line 1607
   at System.Windows.Forms.BindingSource.get_Item(Int32 index)
   at DevExpress.Data.Helpers.BaseListDataControllerHelper.GetRow(Int32 listSourceRow)
   at DevExpress.Data.Helpers.BaseDataControllerHelper.GetRowKey(Int32 listSourceRow)
   at DevExpress.Data.DataController.GetRowKey(Int32 controllerRow)
   at DevExpress.XtraGrid.Views.Base.ColumnView.GetRowKey(Int32 rowHandle)
   at DevExpress.XtraGrid.Views.Grid.ViewInfo.GridViewInfo.LoadRowsCore(GridRowsLoadInfo e)
   at DevExpress.XtraGrid.Views.Grid.ViewInfo.GridViewInfo.LoadRows(GridRowsLoadInfo e)
   at DevExpress.XtraGrid.Views.Grid.ViewInfo.GridViewInfo.Calc(Graphics g, Rectangle bounds)
   at DevExpress.XtraGrid.Views.Base.ColumnView.DoInternalLayout()
   at DevExpress.XtraGrid.Views.Base.ColumnView.CalculateLayout()
   at DevExpress.XtraGrid.Views.Grid.GridView.LayoutChanged()
   at DevExpress.XtraGrid.Views.Base.BaseView.LayoutChangedSynchronized()
   at DevExpress.XtraGrid.Views.Grid.GridView.SetViewRect(Rectangle newValue)
   at DevExpress.XtraGrid.Views.Base.BaseView.InternalSetViewRectCore(Rectangle rect)
   at DevExpress.XtraGrid.Views.Grid.Handler.GridHandler.OnResize(Rectangle clientRect)
   at DevExpress.Utils.Controls.BaseHandler.ProcessEvent(EventType etype, Object args)
   at DevExpress.XtraGrid.Views.Base.Handler.BaseViewHandler.ProcessEvent(EventType etype, Object args)
   at DevExpress.XtraGrid.GridControl.OnResize(EventArgs e)
   at System.Windows.Forms.Control.OnSizeChanged(EventArgs e)
   at System.Windows.Forms.Control.UpdateBounds(Int32 x, Int32 y, Int32 width, Int32 height, Int32 clientWidth, Int32 clientHeight)
   at System.Windows.Forms.Control.UpdateBounds()
   at System.Windows.Forms.Control.WmWindowPosChanged(Message& m)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at DevExpress.XtraEditors.Container.EditorContainer.WndProc(Message& m)
   at DevExpress.XtraGrid.GridControl.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
Walaa avatar
Walaa
Support Team
Posts: 14994
Joined: 21-Aug-2005
# Posted on: 08-Oct-2010 09:12:45   

At which line of code is the Exception thrown.

Also what's more important, if you are going to edit data from the dataGridView, why don't you bind an EntityCollection rather than a TypedView?

iDave avatar
iDave
User
Posts: 67
Joined: 14-Jun-2010
# Posted on: 08-Oct-2010 09:40:37   

Walaa wrote:

At which line of code is the Exception thrown.

Also what's more important, if you are going to edit data from the dataGridView, why don't you bind an EntityCollection rather than a TypedView?

Well, I used a TypedView because it was more easy to display the data I want in the XtraGrid and because it will be read only (for showing purposes, not allowing to edit it directly), the Edit button displays a new form filled with all the data from the entity, etc, from the XtraGrid's selected row using a workaround as I mentioned earlier.

That's the reason I tried using an EntityCollection, so the following line will work:

var staffMember = (StaffEntity)gridViewListStaff.GetRow(rowHandle);

and I'll not have to use the workaround anymore. But as I have said, I got an exception when filling the XtraGrid. disappointed

About the line of code the exception is thrown, I'll check it and post it when I get back to the office.

Walaa avatar
Walaa
Support Team
Posts: 14994
Joined: 21-Aug-2005
# Posted on: 08-Oct-2010 10:37:15   

Here are some comments on what you have said:

The data displays in the XtraGrid without any problems. But then when a user clicks on the Edit button, the following of course don't work:

Code: var staffMember = (StaffEntity)gridViewListStaff.GetRow(rowHandle);

because it's an object of type 'System.Data.DataRowView'.

I'm using a workaround to get some values from the selected row and based on that get the StaffEntity, but I think there's a better solution for that.

As you said, the cast will not work, and all you can do is pick the selected row values, and create a new entity, set it's fields by values captured from the selected row. And set it's IsNew property to false, and pass it to the Edit form. There is no better solution, unless you want to only pass the PK value to the Edit Form, and there you let the Form fetch the entity from the database. Which is easy and very acceptable solution.

Then I though I can fetch the data including only the fields I want, like this:

Code:

// Filter by Active Staff and Add Branch-Staff Relation var bucket = new RelationPredicateBucket(); var staffFilter = new PredicateExpression(); IPredicate filterElement = (StaffFields.Active == true); staffFilter.Add(filterElement); bucket.PredicateExpression.Add(staffFilter); bucket.Relations.Add(BranchEntity.Relations.StaffEntityUsingBranchId);

// Include Fields var includedFields = new ExcludeIncludeFieldsList(); includedFields.ExcludeContainedFields = false; includedFields.Add(new EntityField2("StaffName", StaffFields.Fname + " " + StaffFields.Lname, typeof(string))); includedFields.Add(StaffFields.UserName); includedFields.Add(StaffFields.Curp); includedFields.Add(StaffFields.Phone); includedFields.Add(StaffFields.Mobile); includedFields.Add(StaffFields.HireDate); includedFields.Add(BranchFields.Name); includedFields.Add(StaffFields.Mail); includedFields.Add(StaffFields.Active);

PrefetchPath2 path = new PrefetchPath2(EntityType.StaffEntity); path.Add(StaffEntity.PrefetchPathBranch, includedFields);

using (var staff = new EntityCollection<StaffEntity>()) { var sorter = new SortExpression(StaffFields.Fname | SortOperator.Ascending);

using (var adapter = new DataAccessAdapter(_newMySqlConnString))
{
    // Fetch a Collection of Staff Members
    adapter.FetchEntityCollection(staff, bucket, 0, sorter, path, includedFields);
    // Bind to BindingSource
    bindingStaff.DataSource = staff;
    // Bind to DataGridView
    gridControlListStaff.DataSource = bindingStaff;
    // Get Staff Count
    lblStaffCount.Caption = String.Format("Total: {0}", gridViewListStaff.RowCount.ToString());
}

}

But it doesn't work. I got an Unhandled Exception:

"Index was out of range. Must be non-negative and less than the size of the collection."

Are you fetching the data for the Edit Form? If yes then why are you using prefetchPaths, and where do you filter by the selected row's PK value? Also you can't use the same ExcludeIncludeFieldsList to feth 2 different types of entities.

iDave avatar
iDave
User
Posts: 67
Joined: 14-Jun-2010
# Posted on: 08-Oct-2010 11:44:02   

As you said, the cast will not work, and all you can do is pick the selected row values, and create a new entity, set it's fields by values captured from the selected row. And set it's IsNew property to false, and pass it to the Edit form. There is no better solution, unless you want to only pass the PK value to the Edit Form, and there you let the Form fetch the entity from the database. Which is easy and very acceptable solution.

Hmmm, I thought there was a better solution. disappointed

Are you fetching the data for the Edit Form? If yes then why are you using prefetchPaths, and where do you filter by the selected row's PK value? Also you can't use the same ExcludeIncludeFieldsList to feth 2 different types of entities.

Nope, I'm replacing the TypedView with an EntityCollection to display in the XtraGrid, using ExcludeIncludeFieldsList to only get the data I want. But now that I'm looking again at the posted code, I'm seeing some mistakes... flushed

Anyway, I want to clarify something, please tell me if I'm wrong.

Can I use an EntityCollection with ExcludeIncludeFieldsList to only fetch specific data and show it in a DataGrid like I have done with the TypedView? I mean, showing it like this:

(trying to represent a DataGridView... excuse my poor representation, I'm not an artist. stuck_out_tongue_winking_eye )


-------------------------------------------------------------------------------------
| StaffName | UserName | Phone | Mobile | Mail | HireDate | BranchName | Active |
-------------------------------------------------------------------------------------
|                   |                   |           |             |       |                |                        |            |
-------------------------------------------------------------------------------------
|                   |                   |           |             |       |                |                        |            |
-------------------------------------------------------------------------------------

Where StaffName is a concat of StaffEntity.FName and StaffEntity.LName and BranchName is from BranchEntity.BName and ordering by StaffName in Ascending order.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 09-Oct-2010 02:53:53   

iDave wrote:

As you said, the cast will not work, and all you can do is pick the selected row values, and create a new entity, set it's fields by values captured from the selected row. And set it's IsNew property to false, and pass it to the Edit form. There is no better solution, unless you want to only pass the PK value to the Edit Form, and there you let the Form fetch the entity from the database. Which is easy and very acceptable solution.

Hmmm, I thought there was a better solution. disappointed

As a matter of fact, that is the best way I think. I mean, using a TypedList/DynamicList then retrieving the PK from the selected row and fetch the entity for edition.

iDave wrote:

Anyway, I want to clarify something, please tell me if I'm wrong.

Can I use an EntityCollection with ExcludeIncludeFieldsList to only fetch specific data and show it in a DataGrid like I have done with the TypedView? I mean, showing it like this....

No directly. You can use ExcludeIncludeFieldsList to avoid fetching some fields from DB and fill them in the entity, but the Entity always have all fields (some fetched/filled and some not). If you want to use ExcludeIncludeFieldsList in related entities you have to specify a different ExcludeIncludeFieldsList object and pass it to the prefetchPath. You can Bind fields in related entities in you grid, but you have to create custom properties in the main entity that access the related fields.

All this may be seeing complicated, but it's not. Anyway I think what you should do is: 1. Bind the typedList just as you are doing right now. 2. When users clicks the row, retrieve the primary key. 3. Pass the PK to a Edit form 4. In Edit form, fetch the entity to edit. 5. Etc.

David Elizondo | LLBLGen Support Team
iDave avatar
iDave
User
Posts: 67
Joined: 14-Jun-2010
# Posted on: 09-Oct-2010 06:06:16   

daelmo wrote:

iDave wrote:

As you said, the cast will not work, and all you can do is pick the selected row values, and create a new entity, set it's fields by values captured from the selected row. And set it's IsNew property to false, and pass it to the Edit form. There is no better solution, unless you want to only pass the PK value to the Edit Form, and there you let the Form fetch the entity from the database. Which is easy and very acceptable solution.

Hmmm, I thought there was a better solution. disappointed

As a matter of fact, that is the best way I think. I mean, using a TypedList/DynamicList then retrieving the PK from the selected row and fetch the entity for edition.

iDave wrote:

Anyway, I want to clarify something, please tell me if I'm wrong.

Can I use an EntityCollection with ExcludeIncludeFieldsList to only fetch specific data and show it in a DataGrid like I have done with the TypedView? I mean, showing it like this....

No directly. You can use ExcludeIncludeFieldsList to avoid fetching some fields from DB and fill them in the entity, but the Entity always have all fields (some fetched/filled and some not). If you want to use ExcludeIncludeFieldsList in related entities you have to specify a different ExcludeIncludeFieldsList object and pass it to the prefetchPath. You can Bind fields in related entities in you grid, but you have to create custom properties in the main entity that access the related fields.

All this may be seeing complicated, but it's not. Anyway I think what you should do is: 1. Bind the typedList just as you are doing right now. 2. When users clicks the row, retrieve the primary key. 3. Pass the PK to a Edit form 4. In Edit form, fetch the entity to edit. 5. Etc.

It seems that I tend to complicate things... disappointed

Well, I let it as it was and discarded the option of using an EntityCollection.

The workaround I did was:

  1. Bind TypedView to BindingSource; BindingSource to XtraGrid.
  2. Retrieve the Entity using the entity's unique constraint from the selected row.
  3. Pass the Entity to the Edit form.

Here's the code to retrieve the Entity (in case someone need it wink ):


public static StaffEntity GetStaffEntity(string username)
{
    using (var adapter = new DataAccessAdapter(_newMySqlConnString))
    {
        var staff = new StaffEntity { UserName = username };

        adapter.FetchEntityUsingUniqueConstraint(staff, staff.ConstructFilterForUCUserName());

        if (!staff.IsNew) return staff;
        return null;
    }
}

And the code in of the Edit button event:


var staff = CommonData.GetStaffEntity(gridViewListStaff.GetRowCellValue(rowHandle, "UserName").ToString());

using (var frmStaffDlg = new frmStaff(staff))
{
    if (frmStaffDlg.ShowDialog() == DialogResult.OK)
    {
        // Refresh DataGridView
    }
}

The only problem I see here is that I need to refetch the whole TypedView instead of only the modified row.