开发者

DataSet with multiple levels of related tables

I am trying to use DataSet and DataAdapter to "filter" and "navigate" DataRows in DataTables.

THE SITUATION: I have multiple log开发者_开发知识库ical objects, e.g. Car, Door and Hinge. I am loading a Form which will display complete Car information including each Door and their respective Hinges. In this senario, The form should display info for 1 car, 4 doors and 2 hinges for each door.

Is it possible to use a SINGLE DataSet to navigate this Data? i.e. 1 DataRow in car_table, 4 DataRow in door_table and 8 DataRow in hinge_table, and still be able to navigate correctly between the different object and their relations? AND, able to DataAdapter.Update() easily?

I have read about DataRelation but don't really understand how to use it. Not sure if it is the correct direction for my problem.


If you're looking for an answer towards using DataSets, this won't be it. Instead, I would like to offer an alternative suggestion: If possible, don't use DataSets.

(This is of course a matter of preference, but here's the reason for my suggestion: DataSets, especially when they're of the untyped sort, have the disadvantage of being very generic -- they have no inherent knowledge of what kind of objects are stored inside them. Contrast this with your "logical objects" who have a notion what they are, and what capabilities they might have. Later, when you'll need to maintain your code, such objects will be far easier to understand than an opaque DataSet! Add to this the fact that you seem to already have identified your domain objects and seem to have difficulties with the DataSet in the first place.)

Instead of using a DataSet for your scenario, I would write some data provider classes for these logical objects (Car, Door, and Hinge each get their corresponding data provider class: ICarProvider, IDoorProvider, IHingeProvider) and gather the required data from them when you load your form with data.

var carId = ...;
Car car = carProvider.GetCarById(carId);

You would then delegate loading of the door data to the Car class (since the doors "belong" to the car). Make that class have a collection property, Doors (e.g. of type ICollection<Door>). Inside your Car class, you might load door data as follows:

public ICollection<Door> Doors { get; private set; }

private readonly IDoorProvider doorProvider = ...;

...
this.Doors = doorProvider.GetDoorsOfCar(this);

Likewise, delegate loading of the hinges data to the Door class, since the hinges seem to logically "belong" to a door. The Door class would then have a Hinges property (again, e.g. of type ICollection<Hinge>). Again, your Door class might load hinge data as follows:

public ICollection<Hinge> Hinges { get; private set; }

private readonly IHingeProvider hingeProvider = ...;

...
this.Hinges = hingeProvider.GetHingesOfDoor(this);

When you then load your form, you simply set your controls' values to the corresponding properties of your car object.


P.S.: My answer makes quite a few assumptions about your object model and the relationships between your objects. Feel free to adapt my answer to the object model you've actually got.

P.P.S.: You could even go a step further. For your form, define various user controls that represent one door, or one hinge. They can get their data directly from a Door or Hinge class. Then, create a user control that can initialise itself from an ICollection<Door> (or an ICollection<Hinge>, respectively), and that creates the right number of Door/Hinge child controls inside itself. That way, you should be able to use data binding to load your form data directly from a Car object.


Reply to the first two comments by Jake:

No, I meant something different. What I said was that you can do better than using DataSets. I would personally strive for a solution that does not use DataSets at all. I had something in mind like this:

interface ICarProvider
{
    Car GetCarById(int id);
    ICollection<Car> GetAllCars();
}

public CarProvider : ICarProvider { ... }
// ^ implementation that loads Car objects, e.g. from a relational database

public class Car
{
    public int Id { get; private set; }
    public ICollection<Door> Doors { get; private set; }

    public Car(int id, IDoorProvider doorProvider)
    {
        this.Id = id;
        this.Doors = doorProvider.GetDoorsOfCar(this);
    }
    ...
}

public interface IDoorProvider
{
    ICollection<Door> GetDoorsOfCar(Car car);
    ...
}

public class DoorProvider : IDoorProvider { ... }
// ^ similar to CarProvider, but loads data for Doors instead.

public class Door
{
    public int Id { get; private set; }
    public ICollection<Hinge> Hinges { get; private set; }

    public Door(int id, IHingeProvider hingeProvider)
    {
        this.Id = id;
        this.Hinges = hingeProvider.GetHingesOfDoor(this);
    }
    ...
}

...

You would then data-bind your form controls against these objects directly (via someControlDisplayingCarData.DataSource = someCar;), not against DataSets.


Just add few properties to a Car object, so you will be able to display car information any way you prefer.

partial class Car
{
  public string Doors
  {
     get
     {
        var sb = new StringBuilder();
        foreach(var door in this.Doors)
        {
           sb.Append(door.Name);
        }
     }
     return sb.ToString();
  }

  public string Hinges
  {
     get
     {
        var sb = new StringBuilder();
        foreach(var door in this.Doors)
        {
           foreach(var hinge in door.Hinges)
           {
             sb.Append(hinge.Name);
           }
        }
        return sb.ToString();
     }
   }
}

EDIT: If you wish to be able to modify doors through Car object just add setters to these properties with decoupling logic being reverse to concatenation done in getters.

If you wish to update data to database after changing objects, there are several ways of doing it, yet the use of DataTables limits your possibilities and forces to implement code manually.

You can track changes of objects by implementing INotifyPropertyChanged interface and setting some boolean property storing information if object was modified, you can also store copy of original object and compare it's properties - all this leads to conclusion that you probably will end up implementing your own ORM.

So basically it would be better for you when presentation part requires updating the database to use existing ORM solution for read-write data, LINQ2SQL has a short learning curve.


You could do this with a single dataset, but it depends on whether you are storing complex objects/UD-types or simple related tuples in the db. Here's an example of the latter:

           Car
           id
           door1id FK references Door
           door2id ditto
           door3id ditto
           door4id ditto


           Door
           id
           position (e.g. drivers side front, driver's side rear, passenger-front, passenger-rear
           hinge1id FK references Hinges
           hinge2id ditto


           Hinges
           id
           hingetypeid FK references HingeType


           HingeType
           id
           hingetype

With ADO.NET you could create the dataset and define relations in the client-side disconnected dataset.

The alternative is to define the (nested) types and pass types to the back-end. Manufactured assemblies do go well with OODBMS.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜