你如何将DbDataReader一般映射到Castle.Windsor解析类型?

时间:2009-08-26 19:17:36

标签: inversion-of-control castle-windsor data-access-layer

这让我感到困惑,所以这个问题可能会令人困惑。

我有一个应用程序,它使用IJob接口的实现来完成不同的任务。

public interface IJob
{
  int Id { get; set; }
  string Name { get; set; }
  void Run();
}

我使用Castle.Windsor.WindsorContainer来解决这些实现,并使用服务ID来帮助识别它们。

WindsorContainer container = new WindsorContainer(new XmlInterpreter());
IJob jobToExecute = container.Resolve<IJob>("nameOfJob");

我写了一个小的通用扩展方法,它只是将SQL列的值放入相应的属性中。

    public static void MapTo<T>(this DbDataReader reader, ref T instance) where T : class
    {
        Type objectType = typeof(T);

        foreach (PropertyInfo propertyInfo in objectType.GetProperties())
        {
            if (propertyInfo.CanWrite)
            {
                int ordinal = -1;
                try
                {
                    ordinal = reader.GetOrdinal(propertyInfo.Name);
                    object value = reader[ordinal] == DBNull.Value ? null : reader[ordinal];
                    propertyInfo.SetValue(instance, value, null);
                }
                catch (IndexOutOfRangeException ex)
                {
                    continue;
                }

            }
        }
    }

现在,因为您无法实例化接口的实例,所以将IJob传递给此方法将不起作用。但是,为了获得IoC容器的好处,我需要使用IJob接口在我的存储库中完成所有操作。所以,我写这篇文章来解决IJob实现,并将它传递给MapTo方法来填充必要的属性:

    public IJob GetJobById(int id)
    {
        string cmdTxt = "SELECT Id, Name, Description, DateStarted, ScheduledCompletion, Completed FROM Jobs WHERE Id = @id";

        using (DbCommand cmd = _dataFactory.CreateCommand(cmdTxt))
        {
            _dataFactory.AddParam(cmd, "id", id, DbType.Int32);
            using (DbDataReader rdr = cmd.ExecuteReader())
            {
                if (rdr.Read())
                {
                    IJob job = _container.Resolve<IJob>("job.implementation");
                    rdr.MapTo<IJob>(ref job);
                    return job;
                }
                else
                {
                    return null;
                }
            }
        }
    }

这是一个好的设计决定吗?你看到有什么问题吗?

1 个答案:

答案 0 :(得分:1)

嗯,首先,通过反射调用方法通常不太好...而且看起来你正在使用Windsor作为类型字典,它不是......

我会编写一个非通用的MapTo(可以使用Type作为参数),它对已经存在的实例进行操作(当您使用Activator.CreateInstance创建新实例时丢弃Windsor已解决的实例),然后在ComponentCreatedEvent中的IKernel事件中使用它。像这样:

container.Kernel.ComponentCreated += (model, instance) => {
  if (model.Service == typeof(IJob)) {
    // select id,name from jobs where id = model.Name
    // use MapTo to fill id,name into instance
  }
}