2010-05-27 5 views
0

J'ai une couche d'accès aux données qui retourne un IDataRecord. J'ai un service WCF qui sert DataContracts (dto's). Ces DataContracts sont initiées par un constructeur contenant le IDataRecord paramétrisé comme suit:Mappage objet-objet générique avec constructeur paramétré

[DataContract] 
public class DataContractItem 
{ 
    [DataMember] 
    public int ID; 
    [DataMember] 
    public string Title; 

    public DataContractItem(IDataRecord record) 
    { 
     this.ID = Convert.ToInt32(record["ID"]); 
     this.Title = record["title"].ToString(); 
    } 
} 

Unfortanately Je ne peux pas changer le DAL, donc je suis obligé de travailler avec le IDataRecord en entrée. Mais en général cela fonctionne très bien. Les cartographies sont assez simples la plupart du temps, parfois elles sont un peu plus complexes, mais pas de science des fusées.

Cependant, maintenant j'aimerais pouvoir utiliser des génériques pour instancier les différents DataContracts afin de simplifier les méthodes de service WCF. Je veux être capable de faire quelque chose comme:

public T DoSomething<T>(IDataRecord record) { 
    ... 
return new T(record); 
} 

J'avais essayé de solutions suivantes:

  1. Utilisez une interface typée générique avec un constructeur. ne fonctionne pas: bien sûr, nous ne pouvons pas définir un constructeur dans une interface

  2. Utilisez une méthode statique pour instancier le DataContract et créer une interface typée contenant cette méthode statique. ne fonctionne pas: ofcourse nous ne pouvons pas définir une méthode statique dans une interface

  3. Utilisez une interface typée générique contenant la nouvelle() contrainte ne fonctionne pas: nouvelle() contrainte ne peut pas contenir un paramètre (IDataRecord)

  4. Utilisation d'un objet de fabrique pour effectuer le mappage basé sur le type DataContract. ne fonctionne pas, mais: pas très propre, parce que j'ai maintenant une déclaration de commutateur avec tous les mappages dans un fichier.

Je ne trouve pas de solution vraiment propre pour cela. Quelqu'un peut-il nous éclairer là-dessus? Le projet est trop petit pour les techniques de mappage complexes et trop grand pour une implémentation d'usine "basée sur un commutateur".

Répondre

2

Je pense que vous pouvez utiliser AutoMapper pour cela. C'est un projet open source et cela permettrait de faire ce que vous voulez. Dans votre do quelque chose que vous feriez ceci:

public TDestination DoSomething<TDestination>(IDataRecord record) { 
    return Mapper.Map(reader, reader.GetType(), typeof(TDestination)); 
} 

Avant de pouvoir faire que vous mettre en place une carte

Mapper.CreateMap<IDataRecord, MyDestinationType>() 
+0

Je suis effectivement allé de cette façon. Je connaissais automapper, mais je supposais que c'était trop complexe pour ce cas. Cependant, il s'avère que ce n'était pas complexe et très facile! Merci! –

2

Vous pouvez appeler un type constructeur par la réflexion:

public T DoSomething<T>(IDataRecord record) 
{ 
    //do something... 

    var ci = typeof(T).GetConstructor(new[] { typeof(IDataRecord) }); 
    return (T)ci.Invoke(new object[] { record }); 
} 

EDIT: L'approche ci-dessus est tout à fait fragile et repose sur la convention, donc une autre approche serait de créer une interface pour vos contrats de données qui permettent l'initialisation:

public interface IDataContract 
{ 
    void Initialise(IDataRecord record); 
} 

public T DoSomething<T>(IDataRecord record) where T : IDataContract, new() 
{ 
    //do something 

    T contract = new T(); 
    contract.Initialise(record); 

    return contract; 
} 
+0

vrai, mais je veux être en mesure d'être sûr que tous les DataContracts mettre en œuvre ce constructeur , donc je voudrais utiliser une interface pour les DataContracts. Cependant si j'utilise la réflexion je ne suis pas sûr que le DataContract étant "construit" a le bon constructeur. –

1

Voilà comment je le fais ..

méthodes d'extension pour DataRow tha t me permet de transformer un objet métier en DataRow et vice-versa.

/// <summary> 
/// Extension methods for DataRow. 
/// </summary> 
public static class DataRowExtensions 
{ 
    /// <summary> 
    /// Converts DataRow into business object. 
    /// </summary> 
    /// <typeparam name="TEntity">A type that inherits EntityBase.</typeparam> 
    /// <param name="dataRow">DataRow object to convert to business object.</param> 
    /// <returns>business object created from DataRow.</returns> 
    public static TEntity ToEntity<TEntity>(this DataRow dataRow) 
     where TEntity : EntityBase, new() 
    { 
     TEntity entity = new TEntity(); 
     ExtensionHelper.TransformDataRowToEntity<TEntity, DataRow>(ref dataRow, ref entity); 

     return entity; 
    } 

    /// <summary> 
    /// Converts business object into DataRow. 
    /// </summary> 
    /// <typeparam name="TEntity">A type that inherits EntityBase.</typeparam> 
    /// <param name="dataRow">DataRow object to convert business object into.</param> 
    /// <param name="entity">Business object which needs to be converted to DataRow.</param> 
    public static void FromEntity<TEntity>(this DataRow dataRow, TEntity entity) 
     where TEntity : EntityBase, new() 
    { 
     ExtensionHelper.TransformEntityToDataRow<TEntity, DataRow>(ref entity, ref dataRow); 
    } 
} 

Les méthodes d'aide réelle qui fait la transformation ..

/// <summary> 
/// Helper methods for transforming data objects into business objects and vice versa. 
/// </summary> 
/// <remarks> 
/// <para>Most important implementation that takes care of universal transformation between business objects and data object.</para> 
/// <para>Saves programmers from writing the same old code for every object in the system.</para> 
/// </remarks> 
public static class ExtensionHelper 
{ 
    /// <summary> 
    /// Transforms business object into DataRow. 
    /// </summary> 
    /// <typeparam name="TEntity">A type that inherits EntityBase.</typeparam> 
    /// <typeparam name="TDataRow">A type that inherits DataRow.</typeparam> 
    /// <param name="entity">business object which is transformed into DataRow object.</param> 
    /// <param name="dataRow">DataRow object which is transformed from business object.</param> 
    public static void TransformEntityToDataRow<TEntity, TDataRow>(ref TEntity entity, ref TDataRow dataRow) 
     where TDataRow : DataRow 
     where TEntity : EntityBase 
    { 
     IQueryable<DataField> entityFields = entity.GetDataFields(); 

     foreach (DataColumn dataColoumn in dataRow.Table.Columns) 
     { 
      if (!dataColoumn.ReadOnly) 
      { 
       var entityField = 
        entityFields.Single(e => e.DataFieldMapping.MappedField.Equals(dataColoumn.ColumnName, StringComparison.OrdinalIgnoreCase)); 

       if (entityField.Property.GetValue(entity, null) == null) 
       { 
        if (dataColoumn.AllowDBNull) 
        { 
         dataRow[dataColoumn] = System.DBNull.Value; 
        } 
        else 
        { 
         throw new Exception(dataColoumn.ColumnName + " cannot have null value."); 
        } 
       } 
       else 
       { 
        if (entityField.Property.GetType().IsEnum) 
        { 
         dataRow[dataColoumn] = Convert.ToByte(entityField.Property.GetValue(entity, null)); 
        } 
        else 
        { 
         dataRow[dataColoumn] = entityField.Property.GetValue(entity, null); 
        } 
       } 
      } 
     } 
    } 

    /// <summary> 
    /// Transforms DataRow into business object. 
    /// </summary> 
    /// <typeparam name="TEntity">A type that inherits EntityBase.</typeparam> 
    /// <typeparam name="TDataRow">A type that inherits DataRow.</typeparam> 
    /// <param name="dataRow">DataRow object which is transformed from business object.</param> 
    /// <param name="entity">business object which is transformed into DataRow object.</param> 
    public static void TransformDataRowToEntity<TEntity, TDataRow>(ref TDataRow dataRow, ref TEntity entity) 
     where TDataRow : DataRow 
     where TEntity : EntityBase 
    { 
     IQueryable<DataField> entityFields = entity.GetDataFields(); 

     foreach (var entityField in entityFields) 
     { 
      if (dataRow[entityField.DataFieldMapping.MappedField] is System.DBNull) 
      { 
       entityField.Property.SetValue(entity, null, null); 
      } 
      else 
      { 
       if (entityField.Property.GetType().IsEnum) 
       { 
        Type enumType = entityField.Property.GetType(); 
        EnumConverter enumConverter = new EnumConverter(enumType); 
        object enumValue = enumConverter.ConvertFrom(dataRow[entityField.DataFieldMapping.MappedField]); 
        entityField.Property.SetValue(entity, enumValue, null); 
       } 
       else 
       { 
        entityField.Property.SetValue(entity, dataRow[entityField.DataFieldMapping.MappedField], null); 
       } 
      } 
     } 
    } 
} 

Et voici comment mon exemple regarde objet métier. S'il vous plaît faites attention aux attributs personnalisés.

/// <summary> 
/// Represents User. 
/// </summary> 
[TableMapping("Users", "User", "Users")] 
public class User : EntityBase 
{ 
    #region Constructor(s) 
    /// <summary> 
    /// Initializes a new instance of the User class. 
    /// </summary> 
    public User() 
    { 
    } 
    #endregion 

    #region Properties 

    #region Default Properties - Direct Field Mapping using DataFieldMappingAttribute 

    /// <summary> 
    /// Gets or sets the ID value of the AppUser object. 
    /// </summary> 
    [DataFieldMapping("UserID")] 
    [DataObjectFieldAttribute(true, true, false)] 
    [NotNullOrEmpty(Message = "UserID From UserDetails Table Is Required.")] 
    public override int Id 
    { 
     get; 
     set; 
    } 

    /// <summary> 
    /// Gets or sets the Username value of the User object. 
    /// </summary> 
    [DataFieldMapping("UserName")] 
    [Searchable] 
    [NotNullOrEmpty(Message = "Username Is Required.")] 
    public string UserName 
    { 
     get; 
     set; 
    } 

    /// <summary> 
    /// Gets or sets the FirstName value of the AppUser object. 
    /// </summary> 
    [DataFieldMapping("FirstName")] 
    [Searchable] 
    public string FirstName 
    { 
     get; 
     set; 
    } 

    /// <summary> 
    /// Gets or sets the LastName value of the AppUser object. 
    /// </summary> 
    [DataFieldMapping("LastName")] 
    [Searchable] 
    public string LastName 
    { 
     get; 
     set; 
    } 

    /// <summary> 
    /// Gets or sets the WebSite value of the AppUser object. 
    /// </summary> 
    [DataFieldMapping("WebSite")] 
    [ValidURL(Message = "Website is not in Proper Format.")] 
    public string WebSite 
    { 
     get; 
     set; 
    } 

    /// <summary> 
    /// Gets or sets the ContactNumber value of the AppUser object. 
    /// </summary> 
    [DataFieldMapping("ContactNumber")] 
    [Searchable] 
    public string ContactNumber 
    { 
     get; 
     set; 
    } 

    /// <summary> 
    /// Gets or sets a value indicating whether AppUser Object is active or inactive. 
    /// </summary> 
    [DataFieldMapping("IsActive")] 
    public bool IsActive 
    { 
     get; 
     set; 
    } 

    /// <summary> 
    /// Gets or sets the BirthDate value of the AppUser object. 
    /// </summary> 
    [DataFieldMapping("BirthDate")] 
    public DateTime? BirthDate 
    { 
     get; 
     set; 
    } 

    #region Derived Properties 

    /// <summary> 
    /// Gets the full name of the AppUser 
    /// </summary> 
    public string FullName 
    { 
     get { return this.FirstName + " " + this.LastName; } 
    } 

    /// <summary> 
    /// Gets the Age value of the AppUser 
    /// </summary> 
    public int Age 
    { 
     get { return this.BirthDate.HasValue ? this.BirthDate.Value.AgeInYears() : 0; } 
    } 

    #endregion 

    #endregion 

} 

Et voici mon client code ..

/// <summary> 
    /// Gets User object by user name. 
    /// </summary> 
    /// <param name="username">UserName of the user</param> 
    /// <returns>User Object</returns> 
    public static User GetUserByUsername(string username) 
    { 
     try 
     { 
      return Adapter.GetUserByUserName(username)[0].ToEntity<User>(); 
     } 
     catch 
     { 
      return null; 
     } 
    } 
Questions connexes