Как можно в общем случае сопоставить DbDataReader с разрешенным типом Castle.Windsor? - PullRequest
0 голосов
/ 26 августа 2009

Это сбивает с толку меня, поэтому этот вопрос, вероятно, будет сбивать с толку.

У меня есть приложение, которое использует реализации интерфейса IJob для выполнения различных задач.

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

Я использую Castle.Windsor.WindsorContainer для разрешения этих реализаций и использую идентификатор службы для их идентификации.

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 Ответ

1 голос
/ 27 августа 2009

Ну, во-первых, вызов методов через отражение обычно нехорошо ... и похоже, что вы используете Виндзор как словарь типов, а это не так ...

Я бы написал неуниверсальный 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
  }
}
...