2 votes

Comment puis-je coupler librement des colonnes de base de données dans une application .NET ?

J'ai deux versions d'une base de données presque identique. J'ai créé ci-dessous une table d'exemple pour démontrer les différences fondamentales, à savoir que la colonne ID est passée d'une identité entière à un GUID et que diverses propriétés ont été mises à jour ; dans l'exemple, archivé a été remplacé par readOnly et hidden :

Version héritée :

CREATE TABLE Example
(
    --Data Identity (maps to DbId in the example code)
    Id int IDENTITY PRIMARY KEY,

    --Example columns
    SomeValue varchar(50),
    AnotherValue int,

    --Data Properties
    Archived bit
)

Nouvelle version :

CREATE TABLE Example
(
    --Data Identity (maps to DbId in the example code)
    Id uniqueidentifier PRIMARY KEY,

    --Example columns
    SomeValue varchar(50),
    AnotherValue int,

    --Data Properties
    ReadOnly bit,
    Hidden bit
)

Je dois pouvoir utiliser un mappeur O/R tel que NHibernate pour me connecter à l'une ou l'autre de ces versions de base de données. J'aimerais pouvoir indiquer à l'application quelle version utiliser par le biais de paramètres dans un fichier de configuration.

Mon plan initial consistait à créer une interface commune pour la logique commerciale et à utiliser un conteneur IoC tel que Unity pour échanger les classes concrètes pertinentes dans le fichier de configuration.

Vous trouverez ci-dessous un exemple du code que j'ai créé pour tester cette théorie :

public interface IDataIdentity
{
    object Id { get; }
}

public class LegacyDataIdentity : IDataIdentity
{
    public virtual long DbId { get; set; }

    public object Id
    {
        get { return DbId; }
    }
}

public class NewDataIdentity : IDataIdentity
{
    public virtual Guid DbId { get; set; }

    public object Id
    {
        get { return DbId; }
    }
}

public interface IDataProperties
{
    bool ReadOnly { get; set; }
    bool Hidden { get; set; }
}

public class LegacyDataProperties : IDataProperties
{
    public virtual bool Archived { get; set; }

    public bool ReadOnly
    {
        get { return Archived; }
        set { Archived = value; }
    }

    public bool Hidden
    {
        get { return Archived; }
        set { Archived = value; }
    }
}

public class NewDataProperties : IDataProperties
{
    public virtual bool ReadOnly { get; set; }
    public virtual bool Hidden { get; set; }
}

public class DataItem
{
    public DataItem(IDataIdentity dataIdentity, IDataProperties dataProperties)
    {
        DataIdentity = dataIdentity;
        DataProperties = dataProperties;
    }

    public IDataIdentity DataIdentity { get; set; }
    public IDataProperties DataProperties { get; set; }
}

public class Example : DataItem
{
    public Example(IDataIdentity dataIdentity, IDataProperties dataProperties)
        : base(dataIdentity, dataProperties)
    {
    }

    public virtual string SomeValue { get; set; }
    public virtual int AnotherValue { get; set; }
}

Quelqu'un peut-il nous dire si c'est possible (en particulier avec Unity et NHibernate) et si oui, comment créer les fichiers de mapping NHibernate appropriés ?

Par ailleurs, quelqu'un peut-il suggérer une solution au problème en utilisant d'autres méthodes ou d'autres outils de cartographie IoC et O/R (commerciaux ou open source) ?

Merci beaucoup,

Paul

2voto

Andy Points 756

Pourquoi ne pas abstraire votre fournisseur de données, en mettant en œuvre deux versions (l'une avec des mappings nhibernate pour l'ancien élément de données, et l'autre pour le nouvel élément de données).

Pour paraphraser légèrement votre code (simplifié pour plus de clarté) :

public abstract class AbstractData
{
    public abstract string SomeValue { get; set; }
    public abstract bool ReadOnly { get; set; }
    //etc.
}

public interface IDataProvider
{
    AbstractData Get(object id);
}

public class LegacyData : AbstractData
{
     // Implement AbstractData, and
     public virtual long Id { get { return m_Id; } set { m_Id = value; };
     private long m_Id;
}

public class LegacyDataNHibernateProvider : IDataProvider
{
     public LegacyDataProvider()
     {
         // Set up fluent nhibernate mapping 
     }

     public AbstractData Get(object id)
     {
           // Interpret id as legacy identifier, retrieve LegacyData item, and return
     }
};

// Same again for new data provider

De cette façon, vous n'êtes pas lié à nhibernate (ou à une base de données, d'ailleurs), et vous pouvez spécifier des classes concrètes avec des identifiants correctement typés (que nhibernate peut gérer). C'est l'approche que j'adopte, car je dois actuellement mapper contre les SPs d'une base de données existante, mais je vais plus tard migrer vers un nouveau système basé sur l'ORM.

0voto

mkchandler Points 2389

Peut-être que je ne comprends pas bien votre question, mais il me semble que vous devez mettre en œuvre quelque chose comme le "Factory Pattern".

J'ai récemment utilisé le factory pattern pour coder (c#) sur deux couches de données lorsque la société pour laquelle je travaille est passée de JDE à SAP. J'ai pu basculer un commutateur de configuration pour passer d'une couche de données à l'autre, sans que l'interface graphique ne s'en aperçoive.

Voici quelques liens que j'ai trouvés :

http://msdn.microsoft.com/en-us/library/ms954600.aspx http://www.allapplabs.com/java_design_patterns/factory_pattern.htm http://en.wikipedia.org/wiki/Abstract_factory_pattern

En ce qui concerne NHibernate, je ne le connais pas... désolé. J'espère que cela vous aidera.

0voto

Timur Fanshteyn Points 981

Difficile à recommander sans connaître tous les détails, mais.... Vous pouvez créer une instruction dans les SP en faisant en sorte que le SP renvoie un jeu de données identique pour les deux structures de table.

Une autre solution à laquelle je pense est que vous pouvez en fait spécifier différents fichiers de mapping hybernate et initialiser Hibernate avec un fichier différent en fonction de la base de données à laquelle vous vous connectez.

0voto

user55255 Points 1

Merci pour tous vos commentaires, l'approche d'Andy semble certainement répondre à toutes les exigences. Cependant, j'ai choisi de stocker l'ID en tant qu'objet et d'injecter les propriétés avec Unity. Hibernate ne vous permet pas de définir l'ID comme une propriété de type objet, vous ne pouvez donc pas le mapper directement dans le fichier de configuration. Cependant, comme NHibernate sait ce qu'est la propriété ID, vous pouvez appeler session.GetIdentifier(...) directement après l'initialisation pour la définir.

Je pense que la solution d'Andy permet d'obtenir un motif beaucoup plus net, c'est un peu un bricolage, mais je l'ai fait car le temps pressait et j'avais besoin de passer à autre chose. Il se peut que je revienne sur ce point et que je le réévalue à l'avenir.

Merci beaucoup,

Paul

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X