| 

.NET C# Java Javascript Exception

4
Aufgabe:

Gegeben sei eine generische Methode GetRepository<TEntity>, die ein Repository für Entitäten des Typs TEntity zurückgibt. Das Repository soll eine stark typisierte Load-Methode haben, also beispielsweise:

repo.Load(42);
repo.Load(Guid.Empty);

Der Typ des Parameters von Load soll dabei nicht explizit angegeben werden, um zu verhindern, dass ein Anwender verschiedene Typen angeben kann:
var repo = GetRepository<Person, Guid>();
var repo = GetRepository<Person, int>();

Das Repository für Person soll also immer mit dem gleichen Typ für die ID zurückgegeben werden. Diesen Typen zu ermitteln, ist per Reflection kein Problem. Allerdings ist die Methodensignatur

public IRepository<TEntity, TIdentity> GetRepository<TEntity>();

aus naheliegenden Gründen nicht gültig in C#. Die Idee war nun, einen Lambda-Ausdruck zu übergeben, der die ID-Property spezifiziert:

public IRepository<TEntity, TIdentity>
GetRepository<TEntity, TIdentity>(Func<TEntity, TIdentity>);

Der Aufruf sähe dann folgendermaßen aus:

var repo = GetRepository<Person>(p => p.Id);

Leider funktioniert auch das nicht, weil der Compiler nicht in der Lage ist, einen generischen Typparameter aus einem Func herzuleiten.

Wie könnte man dieses Problem elegant lösen, ohne explizit Guid oder int angeben zu müssen?
12.01.2011
Golo Roden 2,7k 3 9
Wäre sowas eine akzeptable Lösung:
var repos = GetRepository(Identity<Person>.Is(p => p.Id));
779 12.01.2011
3 Antworten
6
Folgender Code läßt sich compilieren und in meinen Augen recht gut lesen:

using System;

namespace ReposTest
{
public class Person
{
public int Id { get; set; }
}

public interface IRepository<TEntity, TIdentity> {}

public class Root
{
public static IRepository<TEntity, TIdentity> Get<TEntity, TIdentity>(Func<TEntity,TIdentity> ident)
{
return null;
}
}

public class RepositoryFor<TEntity>
{
public static Func<TEntity, TIdentity> UsingIdentity<TIdentity>(Func<TEntity,TIdentity> ident)
{
return ident;
}
}

class Program
{
static void Main(string[] args)
{
var repos = Root.Get(RepositoryFor<Person>.UsingIdentity(p => p.Id));
}
}
}
12.01.2011
779 76 2
+1 nicht schlecht. Und ich war daran gescheitert zu erfassen was er eigendlich wollte. Ich glaub ich brauch noch ne Aspirin. Wobei, wieviele darf man davon am Tag überhaupt nehmen? BTT: der Code sieht gut aus und man kann ihn gut lesen.
Floyd 12.01.2011
Vielleicht stehe ich gerade auf dem Schlauch - aber repos ist im Main doch null?
Golo Roden 12.01.2011
@Golo: Verstehe die Frage nicht!? "Get" liefert den Typ zurück, den du haben wolltest und alle nötigen Infos sind in der Methode verfügbar. "int" wurde nicht explizit angegeben. Die konkrete Implementierung von "Get" überlasse ich dir. ;-) Du kannst aber "return null" problemlos durch "return new MyRepository<TEntity,TIdentity>(...)" oder sowas ersetzen.
779 12.01.2011
*patsch*

Wie gesagt: Ich stand auf dem Schlauch ... danke :-)!

Funktioniert perfekt!
Golo Roden 12.01.2011
Nachtrag: Die Signatur von Get ist natürlich nicht sooo schön und intuitiv. Ich würde ja sowas wie IRepositorySelector<TEntity,TIdentity> einführen. UsingIdentity würde dann Func<TEntity,TIdentity> erwarten, aber IRepositorySelector<TEntity,TIdentity> zurück liefern. In der Realität macht es bestimmt auch Sinn, teile von Root in die Implementierung von IRespository<> auszulagern. Ich mag es eben sehr, wenn Lösungen quasi zwangsweise besser entkoppelten Code nach sich ziehen. ;-)
779 12.01.2011
Das mit dem IRepositorySelector ist auch noch eine gute Idee - sehr, sehr cool :-)!

Vielen Dank für die tolle Hilfe!
Golo Roden 12.01.2011
0
kT (verklickt)
12.01.2011
Golo Roden 2,7k 3 9
0
Ui, saugeil!
Ich lag ja gar nicht so falsch mit der Idee, Generische Argument für die Klasse selber auch zu benutzen. *hust*
12.01.2011
Peter Bucher 178 5

Stelle deine .net-Frage jetzt!