2010-05-27 4 views
0

У меня есть уровень доступа к данным, который возвращает IDataRecord. У меня есть служба WCF, которая обслуживает DataContracts (dto's). Эти DataContracts инициируются параметризованном конструктор, содержащий IDataRecord следующим образом:Общий объект для сопоставления объектов с параметризованным конструктором

[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 Я не могу изменить DAL, поэтому я обязан работать с IDataRecord в качестве входных данных. Но в генерале это работает очень хорошо. Сопоставления довольно простые в большинстве случаев, иногда они немного сложнее, но не ракетостроение.

Однако теперь я хотел бы использовать обобщения для создания экземпляров различных DataContracts для упрощения методов службы WCF. Я хочу быть в состоянии сделать что-то вроде:

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

Так что я попытался следующие решения:

  1. Используйте общий типизированных интерфейс с конструктором. не работает: конечно, мы не можем определить конструктор в интерфейсе

  2. Использовать статический метод для создания экземпляра DataContract и создания типизированного интерфейса, содержащего этот статический метод. не работает: конечно, мы не можем определить статический метод в интерфейсе

  3. Использовать общий типизированный интерфейс, содержащий новое() ограничение не работает: новое() ограничение не может содержать параметр (IDataRecord)

  4. Использование фабричного объекта для выполнения сопоставления на основе типа DataContract. действительно работает, но: не очень чистый, потому что теперь у меня есть оператор switch со всеми отображениями в одном файле.

Я не могу найти настоящего чистого решения для этого. Может ли кто-нибудь пролить свет на это для меня? Проект слишком мал для любых сложных методов сопоставления и слишком велик для реализации на основе «коммутатора».

ответ

2

Я думаю, вы могли бы использовать AutoMapper для этого. Это проект с открытым исходным кодом, который позволит делать то, что вы хотите. В вашем сделать кое-что вы могли бы сделать это:

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

Перед тем, как можно сделать что бы вы создали карту

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

Я на самом деле пошел так. Я знал automapper, но я предполагал, что это слишком сложно для этого случая. Однако получается, что это было сложно и очень просто! Благодаря! –

2

Вы можете вызвать конструктор типов с помощью отражения:

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

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

EDIT: Изложенный подход является весьма хрупким и опирается на конвенции, так и другой подход будет создавать интерфейс для ваших контрактов данных, которые позволяют инициализации:

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

верно, но я хочу, чтобы иметь возможность быть уверенным, что все DataContracts реализовать этот конструктор , поэтому я хотел бы использовать интерфейс для DataContracts. Однако, если я использую отражение, я не уверен, что DataContract, «сконструированный», имеет правильный конструктор. –

1

Вот как я это делаю ..

методы расширения для DataRow тха t позволяет мне преобразовать бизнес-объект в DataRow и наоборот.

/// <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); 
    } 
} 

Фактические вспомогательные методы, которые делает преобразование ..

/// <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); 
       } 
      } 
     } 
    } 
} 

А вот как мой образец бизнес-объект выглядит. Обратите внимание на пользовательские атрибуты.

/// <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 

} 

А вот мой клиент-код ..

/// <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; 
     } 
    }