Jump to content
Xtreme .Net Talk

XyBoy

Members
  • Posts

    18
  • Joined

  • Last visited

Everything posted by XyBoy

  1. Hi, I have some little experience with asp.net but I'm juste beginning with VS2008 and Linq. I'm trying to do a little control to manage a "User group" (allowing to add and remove users from the group). So I defined a GridView like this : <asp:GridView ID="GridUsers" runat="server" DataKeyNames="UserID" onrowdeleting="GridUsers_RowDeleting" > (with a "Delete" command field). And in the code behind I have the following databinding : GridUsers.DataSource = from u in DC.OJC_UserGroups_Users where u.UserGroupID == this.UserGroupID select new { u.UserID, u.User.FirstName, u.User.LastName }; GridUsers.DataBind(); But in the "GridUsers_RowDeleting" method, I can't find how to retrieve the UserID for the deleted row : protected void GridUsers_RowDeleting(object sender, GridViewDeleteEventArgs e) { int userID = (int)e.Keys[0]; Group.Users.Remove(Group.Users.First<OJC_UserGroups_Users>(x => x.UserID == userID)); DC.SubmitChanges(); } I've looked everywhere on the web but I can't find the right way to do it. I found this topic : http://forums.asp.net/t/1107245.aspx but the answer given by "adefwebserver" does not work (e.RowItem is not the Key : when I have on single row in my table, with UserID=2, e.RowItem = 0 !). Please tell me if I'm engaged in a wrong way. Thank you. Olivier.
  2. So you want to serialize a whole DataSet into an XML string, and store this string into a DataBase (so that 1 single field of your database would be used to recreate a full DataSet) ? There is indeed a problem to Write/Read XML to/from a string. The methods ReadXml and WriteXml work with streams, files or XMLReader. The GetXml returns a string, but it can only be used to write the string, and you cannot be sure that it is fully compatible with the ReadXml method. So I would use ReadXml and WriteXml. The problem is : how to access a string ? Well, there are several solutions : 1. you can try to make a stream from your string. Have a look at the System.IO.MemoryStream class. You create a stream of a given length, like this : MemoryStream memStream = new MemoryStream(yourString.Length) then you can write to the memory stream (the memory stream is just a kind of array of bytes that implements some standard methods for writing/reading/seeking data) : memStream.Write(yourString, 0 , yourString.Length); Now you have a stream that contains your XML, and you can read it with the ReadXml method. To write the XML back to the DB, it is the same (unless you use the GetXml method) : you create a MemoryStream of a sufficient length, and then you call WriteXml to write the DataSet's content into the stream. Reading the content of the stream into a string is very easy ("memStream.Read (yourString, 0, x);"). 2. you can create a TextReader from your string. Use the System.IO.StringReader class, like this : StringReader strReader = new StringReader(yourString); Then pass this reader to the ReadXml method. To write the XML back, you can probably use the WriteXml method with a TextWriter (use the System.IO.StringWriter class, and its ToString() method to get the corresponding string). 3. create a XmlReader from your string. Use the System.Xml.XmlTextReader class. Create it with a TextReader created as in case 2 : XmlTextReader yourXmlReader = new XmlTextReader (new StringReader (yourString)); The only advantage of this solution is that it checks that the string is well-formed XML. I don't think it is really necessary, since the DataSet's ReadXml method will probably check it also when you give it a TextReader (case 2 ; actually I think that it checks the content of the TextReader as described here, by creating a XmlTextReader from it). Conclusion : use case 2 ! Hope this helps ! Olivier.
  3. Well, first, you create a table (DataTable1) but you don't add it to your DataSet, so I think it is not used (I think you don't need to create it since the Adapter will create it in the DataSet. Second, are you sure the query works when you type it in SQL*Plus, or any other SQL client ? And third, could you give us the messages that come with the OleDbException ? Olivier.
  4. I'm currently looking at a free library to operate the object/relational mapping : OPF.Net. I'm not sure it is as efficient as ORM.Net, but it has already a lot of features and it is probably a good project to get involved in !
  5. Well, I don't quite agree with this. The relationship between Primary Key and Foreign Key depends on your "object structure". Look at this scenarii : 1. An object "contains" other objects. For instance, a building contains flats. In this case, if the buildings are identified by "Building_IDs", I would identify each flat with "Building_ID", "Flat_ID" : the foreign key (building_id) is part of the primary key. Flat_ID is unique within a given building. Advantage : when your application works on a given building, it will load the tables "buildings" and "flats" with the same 'where' clause ("where building_id=xxx"). Although a subset of the "flats" table is loaded, you can add a new flat for this building, and still compute its key : "building_id", "max(flat_id)+1" (computed on the loaded rows, and not on all the table ! This is important !). So you don't need an round-trip to the database (as you would if you used a Sequence or a Primary Key that does not contain Building_id). 2. An object contains "references" to other objects (one-to-many). For instance, a team "loosely" contains employees, it contains references to employees. It looks a little like the flats example, but although an employee belongs to only one team, it can be moved from one team to another (a flat cannot be moved from one building to another ;-) ). You would not like to have to change an employee's ID when you move him from a team to another. So the team ID should not be part of the employee primary key. Hence the primary key for the employee would be "Employee_ID", and the foreign key would be "team_id", in an other field. In this case you would need to use a sequence to generate the "Employee_IDs". If you don't, you will have to compute the employee_id based on the full "employee" table ("select max(employee_id) + 1 from employee;"). In either case you get a round-trip to the database. 3. An object contains references to other objects (many-to-many). For instance, an order contains products. In this case, a product can be part of several orders, so it's a many-to-many relationship, and you need a separate ("intermediate") table : "OrderDetails". In fact it is a combination of cases 2 & 3 : the Order object contains OrderDetails objects, so the Primary Key of the OrderDetails table would be "Order_ID", "OrderDetail_ID" (the foreign key to the order table is part of the PK of the orderDetail table ; OrderDetail_ID is unique within a given order), and the foreign key to the product would be stored in another field, "Product_ID". Other cases are probably a combination of these cases. In any case, you always have to think of what data will be loaded by your application, and when. The datamodel is not to be too dependent on your application, but thinking of the way your application will handle objects will help you better determine the boundaries of the objects and the relationships between them. Good luck ! Olivier.
  6. Well, first, here is what I wanted to do. I might drop the idea as I have found a library that might do the mapping for me (see below).... The idea was to hold all the data in a dataset. In the examples given in the 15seconds article (and in the usual object architecture), each object contains its own data. If we store the data in a dataset, the objects would only contain methods, and work on a DataRow. Each class would map to a DataTable, and each instance would be attached to a DataRow of its class's DataTable. I've started to implement this using DataView and DataRowViews, but I'm having to many problems with handling DataRowViews (among other problems, they cannot be inherited). So the idea would be to have : - collection objects : map to a DataTable ; can be browsed (indexer return an object of the given type) ; implements IBindingList. - business objects : map to a DataRow of a given Table ; would contain a property per DataTable's column, plus some custom properties ; optionally implements IEditableObject (or we can add a layer of objects inheriting from the business objects and implementing IEditableObject, a little bit like the DataRow/DataRowView pattern). - the transaction is handled at a dataset level : since each object is manipulating the data of the datatables, saving (update()) a DataTable would save all the objects of a given type. The typical scheme would be to load a dataset when opening a window, create/use objects and save them (update the dataset) when closing the window. - the data could be loaded when needed. A DataTable can be loaded in several times (if the Adapter Fills the DataTable using the Primary Keys). Well, that's it for now. I may try to implement this seriously at a later time. Right now I'm getting interested in what Christian Liensberger has developed : the OPF.Net library (Object Persistent Framework for .NET). It's free and I've just tried it yesterday, it seems to work pretty well. Check this out : http://www.littleguru.net/ and http://www.codeproject.com/csharp/opfnet.asp for an introduction.
  7. I don't get it, your algorithm should loop through every row of the idxDataTable, and do something (add an item to the combo) for those where "index_id" equals the current g_DBIndex's index_id. So I can't explain why each row would be produced twice. Make sure you didn't left some code doing the same thing elsewhere. Anyway, if I were you, I would use the following syntax to make it faster and simpler : for each idxDataRow in m_idxDataTables.Select ("index_id=" + g_DBIndex.Item ("Index_ID")) cbobox.Items.Add (idxDataRow.Item ("idx_val")) next Note : I don't know in VB, but in C# you don't need to convert a value to string if you concatenate with a string Note 2 : I am not sure if you can use it in VB, but to convert anything to string, we use in C# the ToString() method : it exists for all objects, and is to be used like this : index_id_str = g_DBIndex.Item("Index_ID").ToString () Hope this helps. Olivier.
  8. the syntax { return false throw exc } looks weird to me (the return statement ends the execution of the function, so the exception is not rethrown) So that might be why you don't get any error. I wouldn't be surprised if there actually was an exception when updating. Second, I don't know if you can write the select statement without ";" at the end. ("select * from projects;")
  9. OODBMS, yes but........ Well, thanks for this link ! Object Databases are definitely an option that looks good to me. The problems with it are : 1. I don't know of a free OODBMS that is as good as Access or MS SQL Server (in terms of performance + windows compatibility (drivers) + .NET integration). Matisse seems to have all of this, but I don't know the prices. 2. We as developers aren't always able to make technology choices. Employers/Customers often want to have a .NET project running on the same DB as other, older applications. Having to switch to a new DBMS might dissuade most of them to switch to .NET.... So I would really like to discuss the solutions that we can set up for mapping a .NET object model to a relational DB. Let me first look at the so-called mapping "solutions" described in the 15seconds article : 1. Loading the objects with DataReaders As explained in the article, this is a good solution for the data to be stored only once in memory. Each object retrieves its data in the constructor (or just after, or, even better, when the corresponding properties are called (just-in-time retrieval)). Drawbacks : - the object has to manage its own data. It must in particular know what data has been modified or deleted, in order to issue the corresponding Update and Delete statements when saving (in short : each object must have a transaction management policy, or at least some transaction supporting methods (if the transaction is managed on a application or per-window scale). - loading of data must be smartly done. The same data should not be loaded twice. There must hence be some kind of collection or factory objects that manage the loading of other objects. If two methods want to access the same object, the object should be instantiated only once (we could also think about cloning the existing object, but we have to think about synchronization policy between the two instance of the same object). To achieve this, the factory object should maintain the list of currently loaded objects (a list linking each instance pointer to the object id should be ok for this). If the factory object is also a collection (exposing a list of all the objects of a given type that exist in the DB), it contains data that is also part of the instances. So we have to find a way to keep it synchronized with the data of the loaded objects (when updating the DB, each objects has to update the collection's list if necessary). 2. Loading the data through a DataSet As also said in the article, this is a good solution to handle the relations between tables : you define once the dataset, with its datatables and relations, and then it's easy to navigate between parent/child records. BUT : - I think the way the objects' data management described by this article is dumm : the data is loaded from the DataBase into DataTables, and then loaded from the DataTables into the objects (upon instantiation or, again, when needed). So the data is stored twice in memory. The article's author points this problem, but he obviously did not think (or want to tell us) of a better solution... [we'll discuss better solutions later] - besides storing data twice, this technique implies that the objects must have a transaction management policy, just as in the DataReader's case. This transaction management can be done in two ways : either the modified data is stored back in the DataTable and then the DataTable updates the DB table (all this when saving the object), or each property of the object can update the DataTable in the "set" accessor (so the change is immediate) and the DataTable updates the DB table when saving. - as in the 1st case, the synchronization of the data between two instances of the same object must be thought about... ====== ANOTHER SOLUTION ?? ====== I don't have a solution that is really good yet. I am just thinking about it and trying some techniques before I really choose one for my future development. I would be very grateful to anybody who would take part in this discussion to list the different available techniques or bring any comment/idea about this object/relation mapping problem. The aim is to find a main structure for the "business logic" layer of a typical .NET application. I) Requirements Here are some of the requirements I think we need. 1. Object-Tables correspondence I agree with the analysis of the 15seconds article (http://www.15seconds.com/issue/031013.htm) A table <-> a class A row <-> an object (intance of a class) A column <-> an object's property (I am not sure about inheritance, I believe there are several possibilities.) Note : we should have some kind of collection objects, for some of the tables. It could also be used to navigate relations between objects (see below 'properties'). A collection would contain objects of a given type. Its behavior when adding/deleting objects could vary. 2. DataBinding (entity- & collection-objects) We must be able to bind objects to forms and controls. DataBinding is a great functionnality of Windows Forms, and since it is primarily designed to display DataTables or DataViews, one is always tempted to write an application where the user interface directly access the Data Access Layer (the dataset). This is bad 'cause the business logic of the application is then written in the UI layer (baaaad !). To solve this problem, the objects should be bindable to forms (-> should implement IEditableObject) and the object collections should implement IBindingList. So we can bind to objects of our Business Logic layer, instead of binding to the DataTables/DataViews. 3. Object transaction (entity-objects) The objects should have some kind of transaction management. For now, I see the lifecycle of an object like this: - instanciation : initialization of its core properties, like the object ID. - loading of the data : the data should be loaded when needed, for instance in the Get accessors of the properties. - saving the data : the object should expose a save method that would push the modified data back to the DB. The management of the transaction itself could probably done at an upper level, with several objects relating to the same transaction. For instance, a window would instanciate several objects when loading, then work on them, and finally save them when closing. 4. Object properties An object should expose the following properties of the following types : - persistent properties : properties that correspond to a DB column. Can be read and written. - collection properties : a property that returns a collection of related objects. For instance, in the example given in the now famous article, a Task would have a "Predecessors" and a "Sucessors" properties that would return a collection of tasks. Although both are collections of tasks, their behavior would not be the same (think of an Add(aTask) method : both method will use it to add a row in the "PredecessorsSuccessors" table, but the tasks ID's would not be stored the same way!). - other properties : any custom property that we need for our business logic. This include non-persistent properties (the one that are useful only during one instanciation of the object), computed properties (concatenation of other properties, ...) etc. 5. Synchronization between objects Depending on the application, some objects should be only instantiable once (we would then use a factory), or several times. In the latter case, the data between two instances of the same object should be synchronized. When modifying an instance, the other instance would ideally receive an event. ... maybe others ? [ well, see you all soon, I will describe tomorrow the (parts of a) solution I am working on to match (some of) these requirements ;-) Until then, any comment, idea would be much appreciated !! ] Olivier.
  10. I) For the table creation : your method is right : yourCommand = new OldDbCommand ("select * from IdxVal;", yourConnection); yourAdapter = new OleDBDataAdapter(); yourAdapter.SelectCommand = yourCommand; yourAdapter.Fill (m_idxDataTable); // here your DataTable contains all the schema informations (columns) + the data itself. II) Accessing the data with your DataTable 1. you can browse the rows like this : foreach (DataRow row in m_idxDataTable.Rows) { index_value = row["Idx_Val"]; // for instance. } 2. If you want to browse a subset of the rows, you can Select them like this (sorry for the mistake : I meant Select(), not SelectRows(), look at the have the exact syntax you can use) : foreach (DataRow row in m_idxDataTable.Select (" (some filter) ") ) { Index_value = row["Idx_Val"]; } 3. You can also use a DataView to see a subset of the DataTable. This is especially useful when populating a ComboBox, because you can use it as DataSource : yourCombo.DataSource = new DataView (m_idxDataTable, " (some filter) ", " (optional sorting criteria) ", DataViewRowState.CurrentRows); // see the doc about the DataView constructors for details. yourCombo.DisplayMember = "Idx_Val"; // the field of your view to be displayed. yourCombo.ValueMemeber = "Idx_Val"; // here you would put the field that should be written to the "Index" table. See doc about DataBinding for more details (maybe a some details below ;-) ). III) Updating the DataTable when the data in the DB has changed. 1. First thought, if your config app and your client app are independent, it is not completely dumm to say that the changes made by an administrator would only be visible in the client app when it is restarted. If the two modules are part of the same app (two different windows in the same app instance), then just use the same DataTable for both. The config module would add/delete/modify data in the DataTable, and the client module would only read it. If the two apps have to run on different machines, but you really need to have them synchronized, then user Remoting. That is, you have a single instance of a kind of "server" app that just administers the data (accessing the DB, and providing the data to the "client" applications, and you have "client" applications (the config module and the "client" module) getting the data from the server process (and not from the DB). 2. To update the DataTable : just Fill() again your DataTable with your adapter. All the rows of the DB table will be loaded in the DataTable. The existing rows of your DataTable will not be erased, so the standard behavior would be that the same rows will exist several times in your DataTable, UNLESS (that is what you have to do) you have configured the Adapter to read the keys (yourDataAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey) -> it will recognize that a DB row already exists in the DataTable, and it will update it instead of adding it. III) To Update the Database with data modified in the DataTable. (that is for your config module) Use your DataAdapter (again!). You have to use the same DataAdapter that the one you used to load the data. Normally you would have to write the UpdateCommand of the adapter (like you did for the SelectCommand), but you can use a CommandBuilder to do it for you (check a CommandBuilder exists for OleDb, I know there is one for Odbc). So : yourDataAdapter = new OleDbDataAdapter(); yourCommand = new OleDbCommand ("select *...", conn); new CommandBuilder (yourDataAdapter); // a new command builder is being attached to your adapter yourDataAdpater.SelectCommand = yourCommand; // here you set the SelectCommand for the DataAdapter. The CommandBuilder is informed of this and build the Update, Insert and Delete commands accordingly. yourDataAdapter.Fill (yourDataTable); .... your app runs, modifying data in the DataTable .... yourDataAdapter.Update (yourDataTable); // send the updated data to the DB. Make sure it is the same instance of the DataAdapter as above, and that you don't have used it for filling another table -> the UpdateCommand that the CommandBuilder has set must be accurate ! IV) Writing a class that manages all the data for you Yes, that is a very good idea. Think of it before writing your forms ! But think of your form structure so that you know what you need. Have a look at databinding to see if you can use it. It is very helpful, but I agree that your "dynamic" layout might not be the easiest to implement with databinding. The basic idea of binding is that you have a list of records, and that a form displays one of this records at a time. For any control on the form, you tell wich property of the record you want to be displayed. Like this : Name_TextBox.DataBindings.Add ("Text", yourRecordsList, "Name"); yourRecordsList can be a DataTable or a DataView (to be preferred). The form displays one record (row) of yourRecordList, and automatically set the Text property of your TextBox to have the value of the "Name" field of the displayed record. (If you have programmed with MS Access, the behavior is almost the same). The problem : you have to identify in your application what would be the RecordList, and what would be a Record. I can't help you much on this... But once you have decided what is your Record, and what is your RecordList, you will have to build your DataSet so that it contains a DataTable that corresponds to the so-called RecordList. Good Luck ! Olivier.
  11. Ok, so first, here is what I understand from reading your code (a small description for those who don't want to open it) : - the "IndexSet" table describes some kind of "page" - the "Index" table describes the items ("indices") the page contains. When loading your form, you add a control per "item" for the selected "page". Each control can be one of the following, depending on the "Index_Type" field of the "Index" table : - TextBox (IndexType="TB") : a simple TextBox is added with just a Label in front of it - DateTimePicker (IndexType="DB") : a dropdown calendar is added with the same label in front of it. - ComboBox (IndexType="LB") : a comboBox is added AND filled with values for the user to select. These values come from the third table, IdxVal. So : 1. It would not be wise to use a join, because IdxVal contain data only for some of the records of Index (the ones with IndexType="LB"). 2. You can load the data in two times, like this : - load the content of the first Reader (description of each item) into an Array. This array must have one row per item to display, and on column per "useful" field ("Index_Type", "Index_Label", "Index_Id"). - then close your Reader and run your code browsing your Array. You open a Reader to read the IdxVal table only when the Index_Type is "LB". 3. OR (and this is the solution I would use if the "IdxVal" table is not too big), load the Values into a DataTable first, and then use the Reader to get each Item, like this : - Use a DataAdapter to Fill() a DataTable with the content of the IdxVal table. - Open the Reader with your join query describing each Item. - For each Item, do exactly as you are doing now, but instead of retrieving the IdxVal data with the reader, just do foreach (DataRow row in IdxValDataTable.SelectRows("Index_ID=" + g_DBIndex.Item("Index_ID") ) ) { cboIndexSet.Items.Add(row.items("idx_val")); } Simple, isn't it ? The big advantage of this solution is that you retrieve the IdxVal table only once, so it is better if several ComboBoxes use the same data (which also means "same Index_ID for 2 items", I don't know if it is possible with your current datamodel. Think about it, though, it might be interesting to change it...) Hope this helps. Tell me if you need more details.... Olivier.
  12. give us your code so we can see...
  13. I don't think it makes much of difference. Have a look at the XPath specs or at a tutorial. I haven't been writing XSL for some time, but as far as I can remember, you can use XPath expressions with the XML DOM, like this : yourXMLDocument.SelectNodes (XPathExpression) -> it returns all the nodes that match the expression. XPath allows you to select a node based on its attributes and/or on its parents/children nodes values. For instance, your XPath expression would be something like : 1. for the first structure you described : /DS/Value[@D1=x AND @D2=y] (returns the nodes of type "Value" that have a parent of type "DS" that is at the root of the doc AND for which the D1 attribute equals x and the D2 attribute equals y; I'm not sure of the "AND" syntax, look at the specs). 2. for the second structure : /DS/Value[D1=1 AND D2=2] (the same, but D1 and D2 are children, not attributes). Note : I don't think you need the double-quotes in your XML file : <D3>3</D3> is ok. Hope this helps. To adapt this to an XSLT file, it's not very difficult. I can show you an example if you want. Bye. Xyboy.
  14. Well, no, just describe the tables, or at least show us the SQL queries you are trying to use in your 2 DataReaders... (give us the code you are trying to run, so we can also see the "linking" you want between the two). Don't send your DB, since I don't have Access here. A good description should be enough ;-)
  15. Feel free to describe the problem and the solution you've found, it might help other crystal reports' users ;-)
  16. Yes, your error message confirms what I said. If you give me more information about your datamodel (the structure of the 3 tables you are querying), maybe I can help you. 3-tables joins are not especially difficult, but it might not always make sense (let's see the datamodel to decide). Or try the 1st method, loading the content of the first reader into an Array or a Collection. Can you describe your tables, and the 2 queries you are trying to use in your DataReaders (with the join) ?
  17. Could you give details about the error you're having ? I believe you cannot have several Readers open at the same time on the same DB Connection. The first reader must be closed before the other to be open. So you have two options : 1. Change your script so that the first Reader's data is loaded in an in-memory structure, like an Array or a Collection. Then open your second Reader and retrieve the data. It would give something like this : int i = 0; while (DBIndexSet.Read) { yourLocalStructure[i++]["aFieldName"] = DBIndexSet["aFieldName"]; // (find a correct syntax for the local structure you are using) } DBIndexSet.Close (); // close the first reader for (i=0; i <= yourLocalStructure.Length ; i++) { // open your secondReader while (secondReader.Read) { ... } // don't forget to close your second Reader each time, if you have to reopen it with a different query for each record of yourLocalStructure ! } 2. Change your SQL query so that it retrieves all the data you want. It depends on what you are trying to do, but usually this kind of problem can be solved with a join. Hope this helps ! Olivier.
  18. Hi, I'm new to .NET and am trying to take advantage of the object structure while accessing relational databases. I started a small project for managing "projects". ==== Data Model ==== Here is a description of my data structure : A project would be made of tasks, and each task can contain sub-tasks, so I would have something like - Project1 - task1 - subtask A - subtask B - task 2 - Project2 ... (each line being a task). Then, each task can "depend" on others : it could only start when all the tasks it "depends on" are performed. For instance : Project1/task2 "depends on" Project1/task1/subtaskA. So I end up with the following datamodel : [Table "TASKS"] int id_task [key] : unique identifier of the task string name, description : ... int id_parent : identifier of the parent task [Table "TASK_RELATIONS"] int id_task_1 [key] : identifier of the "preceding" task int id_task_2 [key] : identifier of the "following" task (the one that can only be started once id_task_1 is done). ==== Object Model ==== And (here comes the problem), I would like to have an object structure close to the following (I describe each type ; "+" indicates a public property or method of the described type) : public static "MyApplication" : "global" container object. + TaskCollection "Projects" {get;} : collection of all the top-level tasks (id_parent=0). + TaskCollection "AllTasks" {get;} : collection of all the tasks. public TaskCollection : a collection of "Task"s. + Task this[int index] {} public Task : a task + int ID (this one might not even be useful as a public prop) + string Name, Description : ... + string FullName {get;} // returns the concatenation of this task and of all its ascendants : "Project1/task1/subtaskA" for instance. + Task Parent {get;} // returns the task's parent task. + TaskCollection Children {get;} // returns a collection with all the children tasks + TaskCollection PrecedingTaks // returns the tasks that must be completed before this task can begin + TaskCollection FollowingTask // returns the task that can begin once this one is done. MyApplication contains also a "Data" property that is a an object holding my 2 DataTables (MyApplication.Data.Tasks and MyApplication.Data.Task_relations). ==== Mapping the 2 models ?? ==== My BIG question is : how do I operate the mapping between the two (the DB data model and the Object structure) ? To have you better understand the requirements of my model, here is a description of a "Projects" Form I already started to write to allow the user to edit the projects : It contains a tree on the left (that uses MyApplication.Projects to display all the tasks), and a "detail" pane on the right. The detail pane is bound to MyApplication.AllTasks, and the position of the BindingManager is set by the Tree when the user clicks a task. The pane on the right contains standard controls (TextBox "txt_Name", with property Text bound to "Name") and some more complex controls (ListBox "lst_preceding_tasks" with property DataSource bound to "PrecedingTask"). Here are the first conclusions I could draw from reading the documentation : - for my "Tasks" to be bindable and editable in Windows Forms, TaskCollection should implement IBindingList, and Task should implement IEditableObject. - I wanted to extend the DataView and DataRowView classes as follow : Task : DataRowView {} TaskCollection : DataView {} and make them "point" to the MyApplication.Data.Tasks DataTable. I had to give up as I saw that DataRowView cannot be inherited (problem with the constructor, which is internal, or something like that... [could you inherit it ?]). - Finally I tried to write to new classes "containing" the DataRowView and DataView classes : public class Task : IEditabledObject { private DataRowView row; public Task (DataRowView aRow) { row = aRow; } public string FullName {...} ...etc. } public class TaskCollectoin : IBindingList { private DataView view; public TaskCollection (string filter) { view = new DataView (MyApplication.Data.Tasks, filter, "", DataViewRowState.CurrentRows); } ... (implementation of some of the IBindingList interface methods and properties) : object AddNew() { return new Task(view.AddNew()); } object this[int index] { get { return new Task (view[int]); // what do you think of this ? } } ... (mapping of ALL other methods and properties on the view object) : bool IBindingList.AnyProperty { get{return view.AnyProperty;}} ... } What do you think of this solution ? Is it the right way to do it ? Here is a first problem, to begin with : "Linking" a TaskCollection to a DataView of MyApplication.Data.Tasks is OK as long as I want to use it to edit the tasks. So for MyApplication.Projects it is fine. I can Add Tasks to the Collection, it will insert a new row in my "Tasks" table. But for the "Children" or "PrecedingTasks" properties of the Task object, the collection should rather be linked to the MyApplication.Data.Task_relations table. Adding a Task to the collection should insert a row in the "Task_relations" table ! Should I have two different objects ? For instance a TaskCollectionBase object, and one extension of this class per type of collection I need ? (PrecedingTaskCollection and FollowingTaskCollection would point to the same table, but adding a Task to them would insert a row with id_task_1=this.row[id_task] for the one and with id_task_2=this.row[id_task] for the other). Well, thank you very much for any hint, piece of advice, or comment. Please tell me if you know of any documentation on that topic, or forums more specifically dealing with that kind of problems... Thanks again ! Olivier.
×
×
  • Create New...