I'm building a search engine using Lucene.NET / Solr.NET, and I'm wondering should search hits be returned as a dictionary or strongly typed object.

public class SearchResult
{
    public string SearchTerm { get; set; }
    public List<Dictionary<string, object>> SearchHits { get; set; }
    ...
}

// where search hit looks like this
Dictionary<string, object> searchHit = new Dictionary<string, object>
{
    { "Name", "John Doe" },
    { "Address", "Some street" },
};


vs

public class SearchResult
{
    public string SearchTerm { get; set; }
    public List<Foo> SearchHits { get; set; }
    ...
}

// where search hit looks like this
var searchHit = new SearchHit
{
    Name = "John Doe",
    Address = "Some address"
};


I will be indexing different types of objects (People, Web pages, Files, etc.).
Each object has a different set of properties (people have name and address, while web pages have URLs and titles), and search hits don't have the same set of properties as original objects (WebPage class has the MainContent property, but after indexing, the search hit that represents this object will have the PreviewText property (the first 300 characters of MainContent property with stripped HTML tags).

I don't want to duplicate the code all over the place and have: IPeopleSearchService, IWebPagesSearchService, IFilesSearchService, etc. and corresponding search results: PeopleSearchResult, FileSearchResult, etc.

interface IFooService
{
    FooSearchResult Search(string text);
    void Index(Foo foo);
    ...
}

interface IBarService
{
    BarSearchResult Search(string text);
    void Index(Bar bar);
    ...
}
...


I would like to have a more generic solution, but I don't like over-complexity. It should be readable and easily understandable by junior developer.

interface ISearchService<TEntity, TSearchHit>
{
    SearchResult<TSearchHit> Search(string text);
    void Index(TEntity entity);
}


How would you design such a system? Any help would be greatly appreciated! 

Let's say you have some sort of "repository" class, and that repository is responsible for handing data to you from a data source.

The repository could establish a connection to the data source by itself.  But what if it allowed you to pass in a connection to the data source through the repository's constructor?  

By allowing the caller to provide the connection, you have decoupled the data source connection dependency from the repository class, allowing any data source to work with the repository, not just the one that the repository specifies.  

You have inverted control by handing the responsibility of creating the connection from the repository class to the caller.

Martin Fowler suggests using the term "Dependency Injection" to describe this type of Inversion of Control, since Inversion of Control as a concept can be applied more broadly than just injecting dependencies in a constructor method.
