suchen
HeimBackend-EntwicklungC++Erweitern von Ardalis.Specification für NHibernate mit Linq, Criteria API und Query Over

Extending Ardalis.Specification for NHibernate with Linq, Criteria API, and Query Over

Ardalis.Specification ist eine leistungsstarke Bibliothek, die das Spezifikationsmuster zum Abfragen von Datenbanken ermöglicht. Sie wurde hauptsächlich für Entity Framework Core entwickelt. Hier zeige ich jedoch, wie Sie Ardalis.Specification für die Verwendung erweitern können NHibernate auch als ORM.

In diesem Blogbeitrag wird davon ausgegangen, dass Sie über Erfahrung mit Ardalis.Specification verfügen und es in einem Projekt mit NHibernate verwenden möchten. Wenn Sie mit Ardalis.Specification noch nicht vertraut sind, lesen Sie die Dokumentation, um mehr zu erfahren.

Erstens gibt es in NHibernate drei verschiedene integrierte Möglichkeiten, Abfragen durchzuführen

  • Linq zur Abfrage (mit IQueryable)
  • Kriterien-API
  • Abfrage beendet

Ich werde durchgehen, wie Sie Ardalis.Specification erweitern können, um alle drei Möglichkeiten zu verarbeiten, aber da Linq to Query auch mit IQueryable wie Entity Framework Core funktioniert, werde ich zuerst diese Option durchgehen.

Linq zur Abfrage

Es gibt eine kleine Nuance zwischen Entity Framework Core und NHIbernate, wenn es um die Erstellung von Join-Beziehungen geht. In Entity Framework Core haben wir Erweiterungsmethoden für IQueryable: Include und ThenInclude (dies sind auch die Methodennamen, die in Ardalis.Specification verwendet werden).

Fetch, FetchMany, ThenFetch und ThenFetchMany sind NHibernate-spezifische Methoden auf IQueryable, die Verknüpfungen durchführen. Der IEvaluator bietet uns die Erweiterbarkeit, die wir benötigen, um die richtige Erweiterungsmethode aufzurufen, wenn wir mit NHibernate arbeiten.

Fügen Sie eine Implementierung von IEvaluator wie folgt hinzu:

public class FetchEvaluator : IEvaluator
{
   private static readonly MethodInfo FetchMethodInfo = typeof(EagerFetchingExtensionMethods)
        .GetTypeInfo().GetDeclaredMethods(nameof(EagerFetchingExtensionMethods.Fetch))
        .Single();

   private static readonly MethodInfo FetchManyMethodInfo = typeof(EagerFetchingExtensionMethods)
       .GetTypeInfo().GetDeclaredMethods(nameof(EagerFetchingExtensionMethods.FetchMany))
       .Single();

   private static readonly MethodInfo ThenFetchMethodInfo
       = typeof(EagerFetchingExtensionMethods)
           .GetTypeInfo().GetDeclaredMethods(nameof(EagerFetchingExtensionMethods.ThenFetch))
           .Single();

   private static readonly MethodInfo ThenFetchManyMethodInfo
       = typeof(EagerFetchingExtensionMethods)
           .GetTypeInfo().GetDeclaredMethods(nameof(EagerFetchingExtensionMethods.ThenFetchMany))
           .Single();

    public static FetchEvaluator Instance { get; } = new FetchEvaluator();

    public IQueryable<t> GetQuery<t>(IQueryable<t> query, ISpecification<t> specification) where T : class
    {
        foreach (var includeInfo in specification.IncludeExpressions)
        {
            query = includeInfo.Type switch
            {
                IncludeTypeEnum.Include => BuildInclude<t>(query, includeInfo),
                IncludeTypeEnum.ThenInclude => BuildThenInclude<t>(query, includeInfo),
                _ => query
            };
        }

        return query;
    }

    public bool IsCriteriaEvaluator { get; } = false;

    private IQueryable<t> BuildInclude<t>(IQueryable query, IncludeExpressionInfo includeInfo)
    {
        _ = includeInfo ?? throw new ArgumentNullException(nameof(includeInfo));

        var methodInfo = (IsGenericEnumerable(includeInfo.PropertyType, out var propertyType)
            ? FetchManyMethodInfo 
            : FetchMethodInfo);

       var method = methodInfo.MakeGenericMethod(includeInfo.EntityType, propertyType);

       var result = method.Invoke(null, new object[] { query, includeInfo.LambdaExpression });
        _ = result ?? throw new TargetException();

        return (IQueryable<t>)result;
    }

    private IQueryable<t> BuildThenInclude<t>(IQueryable query, IncludeExpressionInfo includeInfo)
    {
        _ = includeInfo ?? throw new ArgumentNullException(nameof(includeInfo));
        _ = includeInfo.PreviousPropertyType ?? throw new ArgumentNullException(nameof(includeInfo.PreviousPropertyType));

        var method = (IsGenericEnumerable(includeInfo.PreviousPropertyType, out var previousPropertyType)
            ? ThenFetchManyMethodInfo
            : ThenFetchMethodInfo);

        IsGenericEnumerable(includeInfo.PropertyType, out var propertyType);

        var result = method.MakeGenericMethod(includeInfo.EntityType, previousPropertyType, propertyType)
            .Invoke(null, new object[] { query, includeInfo.LambdaExpression });

        _ = result ?? throw new TargetException();

        return (IQueryable<t>)result;
    }

    private static bool IsGenericEnumerable(Type type, out Type propertyType)
    {
        if (type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(IEnumerable)))
        {
            propertyType = type.GenericTypeArguments[0];

            return true;
        }

        propertyType = type;

        return false;
    }
}
</t></t></t></t></t></t></t></t></t></t></t></t>

Als nächstes müssen wir ISpecificationEvaluator für die Verwendung unseres FetchEvaluator (und anderer Evaluatoren) konfigurieren. Wir fügen wie folgt eine Implementierung ISpecificationEvaluator hinzu, wobei die Evaluatoren im Konstruktor konfiguriert sind. WhereEvaluator, OrderEvaluator und PaginationEvaluator sind alle in der Ardalis.Specification enthalten und funktionieren auch gut mit NHibernate.

public class LinqToQuerySpecificationEvaluator : ISpecificationEvaluator
{
    private List<ievaluator> Evaluators { get; } = new List<ievaluator>();

    public LinqToQuerySpecificationEvaluator()
    {
        Evaluators.AddRange(new IEvaluator[]
        {
            WhereEvaluator.Instance,
            OrderEvaluator.Instance,
            PaginationEvaluator.Instance,
            FetchEvaluator.Instance
        });
    }


    public IQueryable<tresult> GetQuery<t tresult>(IQueryable<t> query, ISpecification<t tresult> specification) where T : class
    {
        if (specification is null) throw new ArgumentNullException(nameof(specification));
        if (specification.Selector is null && specification.SelectorMany is null) throw new SelectorNotFoundException();
        if (specification.Selector is not null && specification.SelectorMany is not null) throw new ConcurrentSelectorsException();

        query = GetQuery(query, (ISpecification<t>)specification);

        return specification.Selector is not null
            ? query.Select(specification.Selector)
            : query.SelectMany(specification.SelectorMany!);
    }

    public IQueryable<t> GetQuery<t>(IQueryable<t> query, ISpecification<t> specification, bool evaluateCriteriaOnly = false) where T : class
    {
        if (specification is null) throw new ArgumentNullException(nameof(specification));

        var evaluators = evaluateCriteriaOnly ? Evaluators.Where(x => x.IsCriteriaEvaluator) : Evaluators;

        foreach (var evaluator in evaluators)
           query = evaluator.GetQuery(query, specification);

        return query;
    }
}
</t></t></t></t></t></t></t></t></tresult></ievaluator></ievaluator>

Jetzt können wir in unserem Repository einen Verweis auf LinqToQuerySpecificationEvaluator erstellen, der etwa so aussehen könnte:

public class Repository : IRepository
{
    private readonly ISession _session;
    private readonly ISpecificationEvaluator _specificationEvaluator;

    public Repository(ISession session)
    {
        _session = session;
        _specificationEvaluator = new LinqToQuerySpecificationEvaluator();
    } 

    ... other repository methods

    public IEnumerable<t> List<t>(ISpecification<t> specification) where T : class
    {
        return _specificationEvaluator.GetQuery(_session.Query<t>().AsQueryable(), specification).ToList();
    }

    public IEnumerable<tresult> List<t tresult>(ISpecification<t tresult> specification) where T : class
    {    
        return _specificationEvaluator.GetQuery(_session.Query<t>().AsQueryable(), specification).ToList();
    }

    public void Dispose()
    {
        _session.Dispose();
    }
}

</t></t></t></tresult></t></t></t></t>

Das ist es. Wir können jetzt Linq to Query in unseren Spezifikationen verwenden, genau wie wir es normalerweise mit Ardalis tun.Specification:

public class TrackByName : Specification<core.entitites.track>
{
    public TrackByName(string trackName)
    {
        Query.Where(x => x.Name == trackName);
    }
}
</core.entitites.track>

Nachdem wir uns nun mit Linq-basierten Abfragen befasst haben, gehen wir zum Umgang mit Criteria API und Query Over über, die einen anderen Ansatz erfordern.

Mischen von Linq, Criteria und Query Over in NHibernate

Da Criteria API und Query Over über eine eigene Implementierung zum Generieren von SQL verfügen und nicht IQueryable verwenden, sind sie nicht mit der IEvaluator-Schnittstelle kompatibel. Meine Lösung besteht darin, in diesem Fall die Verwendung der IEvaluator-Schnittstelle für diese Methoden zu vermeiden und mich stattdessen auf die Vorteile des Spezifikationsmusters zu konzentrieren. Aber ich möchte auch mischen können
Linq to Query, Criteria und Query Over in meiner Lösung (wenn Sie nur eine dieser Implementierungen benötigen, können Sie sich für Ihre besten Anforderungen entscheiden).

Dazu füge ich vier neue Klassen hinzu, die Specification oder Specification erben

HINWEIS: Die Assembly, in der Sie diese Klassen definieren, benötigt einen Verweis auf NHibernate, da wir Aktionen für Kriterien und ein QueryOver definieren, das in NHibernate zu finden ist

public class CriteriaSpecification<t> : Specification<t>
{
    private Action<icriteria>? _action;
    public Action<icriteria> GetCriteria() => _action ?? throw new NotSupportedException("The criteria has not been specified. Please use UseCriteria() to define the criteria.");
    protected void UseCriteria(Action<icriteria> action) => _action = action;
}

public class CriteriaSpecification<t tresult> : Specification<t tresult>
{
    private Action<icriteria>? _action;
    public Action<icriteria> GetCriteria() => _action ?? throw new NotSupportedException("The criteria has not been specified. Please use UseCriteria() to define the criteria.");
    protected void UseCriteria(Action<icriteria> action) => _action = action;
}

public class QueryOverSpecification<t> : Specification<t>
{
    private Action<iqueryover t>>? _action;
    public Action<iqueryover t>> GetQueryOver() => _action ?? throw new NotSupportedException("The Query over has not been specified. Please use the UseQueryOver() to define the query over.");
    protected void UseQueryOver(Action<iqueryover t>> action) => _action = action;
}

public class QueryOverSpecification<t tresult> : Specification<t tresult>
{
    private Func<iqueryover t>, IQueryOver<t t>>? _action;
    public Func<iqueryover t>, IQueryOver<t t>> GetQueryOver() => _action ??  throw new NotSupportedException("The Query over has not been specified. Please use the UseQueryOver() to define the query over.");
    protected void UseQueryOver(Func<iqueryover t>, IQueryOver<t t>> action) => _action = action;
}
</t></iqueryover></t></iqueryover></t></iqueryover></t></t></iqueryover></iqueryover></iqueryover></t></t></icriteria></icriteria></icriteria></t></t></icriteria></icriteria></icriteria></t></t>

Dann können wir den Mustervergleich in unserem Repository verwenden, um die Art und Weise zu ändern, wie wir Abfragen mit NHibernate durchführen

public IEnumerable<t> List<t>(ISpecification<t> specification) where T : class
{
    return specification switch
    {
        CriteriaSpecification<t> criteriaSpecification => 
            _session.CreateCriteria<t>()
                .Apply(query => criteriaSpecification.GetCriteria().Invoke(query))
                .List<t>(),

        QueryOverSpecification<t> queryOverSpecification => 
            _session.QueryOver<t>()
                .Apply(queryOver => queryOverSpecification.GetQueryOver().Invoke(queryOver))
                .List<t>(),

        _ => _specificationEvaluator.GetQuery(_session.Query<t>().AsQueryable(), specification).ToList()
    };
}

public IEnumerable<tresult> List<t tresult>(ISpecification<t tresult> specification) where T : class
{

    return specification switch
    {
        CriteriaSpecification<t tresult> criteriaSpecification => 
            _session.CreateCriteria<t>()
                .Apply(query => criteriaSpecification.GetCriteria().Invoke(query))
                .List<tresult>(),

        QueryOverSpecification<t tresult> queryOverSpecification =>
            _session.QueryOver<t>()
                .Apply(queryOver => queryOverSpecification.GetQueryOver().Invoke(queryOver))
                .List<tresult>(),

        _ => _specificationEvaluator.GetQuery(_session.Query<t>().AsQueryable(), specification).ToList()
    };
}
</t></tresult></t></t></tresult></t></t></t></t></tresult></t></t></t></t></t></t></t></t></t></t>

Die obige Apply()-Methode ist eine Erweiterungsmethode, die die Abfrage auf eine einzelne Zeile vereinfacht:

public static class QueryExtensions
{
    public static T Apply<t>(this T obj, Action<t> action)
    {
        action(obj);
        return obj;
    }

    public static TResult Apply<t tresult>(this T obj, Func<t tresult> func)
    {
        return func(obj);
    }
}
</t></t></t></t>

Beispiel für eine Kriterienspezifikation

HINWEIS: Die Assembly, in der Sie diese Klassen definieren, benötigt einen Verweis auf NHibernate, da wir Aktionen für Kriterien definieren, die in NHibernate zu finden sind

public class TrackByNameCriteria : CriteriaSpecification<track>
{
    public TrackByNameCriteria(string trackName)
    {
        this.UseCriteria(criteria => criteria.Add(Restrictions.Eq(nameof(Track.Name), trackName)));
    }
}
</track>

Beispiel für eine Abfrage nach einer Spezifikation

HINWEIS: Die Assembly, in der Sie diese Klassen definieren, benötigt einen Verweis auf NHibernate, da wir Aktionen für ein QueryOver definieren, das in NHibernate zu finden ist

public class TrackByNameQueryOver : QueryOverSpecification<track>
{
    public TrackByNameQueryOver(string trackName)
    {
        this.UseQueryOver(queryOver => queryOver.Where(x => x.Name == trackName));
    }
}
</track>

Durch die Erweiterung von Ardalis.Specification für NHibernate erschließen wir die Möglichkeit, Linq to Query, Criteria API und Query Over zu verwenden – alles innerhalb eines einzigen Repository-Musters. Dieser Ansatz bietet eine äußerst anpassungsfähige und leistungsstarke Lösung für NHibernate-Benutzer

Das obige ist der detaillierte Inhalt vonErweitern von Ardalis.Specification für NHibernate mit Linq, Criteria API und Query Over. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
C# gegen C: Lernkurven und EntwicklererfahrungC# gegen C: Lernkurven und EntwicklererfahrungApr 18, 2025 am 12:13 AM

Es gibt signifikante Unterschiede in den Lernkurven von C# und C- und Entwicklererfahrung. 1) Die Lernkurve von C# ist relativ flach und für rasche Entwicklung und Anwendungen auf Unternehmensebene geeignet. 2) Die Lernkurve von C ist steil und für Steuerszenarien mit hoher Leistung und niedrigem Level geeignet.

C# vs. c: Objektorientierte Programmierung und FunktionenC# vs. c: Objektorientierte Programmierung und FunktionenApr 17, 2025 am 12:02 AM

Es gibt signifikante Unterschiede in der Implementierung von C# und C in der objektorientierten Programmierung (OOP). 1) Die Klassendefinition und die Syntax von C# sind prägnanter und unterstützen erweiterte Funktionen wie Linq. 2) C bietet eine feinere granulare Kontrolle, die für die Systemprogrammierung und den hohen Leistungsbedarf geeignet ist. Beide haben ihre eigenen Vorteile, und die Wahl sollte auf dem spezifischen Anwendungsszenario basieren.

Von XML zu C: Datenumwandlung und -manipulationVon XML zu C: Datenumwandlung und -manipulationApr 16, 2025 am 12:08 AM

Das Konvertieren von XML in C und die Durchführung von Datenvorgängen kann in den folgenden Schritten erreicht werden: 1) Parsing XML -Dateien mithilfe der TinyXML2 -Bibliothek, 2) Daten in die Datenstruktur von C mithilfe der C -Standardbibliothek wie STD :: Vector für Datenoperationen in C -Datenstruktur zuzuordnen. Durch diese Schritte können Daten aus XML konvertiert und effizient bearbeitet werden.

C# gegen C: Speicherverwaltung und MüllsammlungC# gegen C: Speicherverwaltung und MüllsammlungApr 15, 2025 am 12:16 AM

C# verwendet den automatischen Müllsammlungsmechanismus, während C die manuelle Speicherverwaltung verwendet. Der Müllkollektor von 1. C#verwaltet automatisch den Speicher, um das Risiko eines Speicherlecks zu verringern, kann jedoch zu einer Leistungsverschlechterung führen. 2.C bietet eine flexible Speicherregelung, die für Anwendungen geeignet ist, die eine feine Verwaltung erfordern, aber mit Vorsicht behandelt werden sollten, um Speicherleckage zu vermeiden.

Jenseits des Hype: Beurteilung der Relevanz von C heute heuteJenseits des Hype: Beurteilung der Relevanz von C heute heuteApr 14, 2025 am 12:01 AM

C hat immer noch wichtige Relevanz für die moderne Programmierung. 1) Hochleistungs- und direkte Hardware-Betriebsfunktionen machen es zur ersten Wahl in den Bereichen Spieleentwicklung, eingebettete Systeme und Hochleistungs-Computing. 2) Reiche Programmierparadigmen und moderne Funktionen wie Smart -Zeiger und Vorlagenprogrammierung verbessern seine Flexibilität und Effizienz. Obwohl die Lernkurve steil ist, machen sie im heutigen Programmierökosystem immer noch wichtig.

Die C -Community: Ressourcen, Unterstützung und EntwicklungDie C -Community: Ressourcen, Unterstützung und EntwicklungApr 13, 2025 am 12:01 AM

C -Lernende und Entwickler können Ressourcen und Unterstützung von Stackoverflow, Reddits R/CPP -Community, Coursera und EDX -Kursen, Open -Source -Projekten zu Github, professionellen Beratungsdiensten und CPPCON erhalten. 1. Stackoverflow gibt Antworten auf technische Fragen. 2. Die R/CPP -Community von Reddit teilt die neuesten Nachrichten; 3.. Coursera und EDX bieten formelle C -Kurse; 4. Open Source -Projekte auf Github wie LLVM und Boost verbessern die Fähigkeiten; 5. Professionelle Beratungsdienste wie Jetbrains und Perforce bieten technische Unterstützung; 6. CPPCON und andere Konferenzen helfen Karrieren

C# vs. c: Wo sich jede Sprache auszeichnetC# vs. c: Wo sich jede Sprache auszeichnetApr 12, 2025 am 12:08 AM

C# eignet sich für Projekte, die eine hohe Entwicklungseffizienz und plattformübergreifende Unterstützung erfordern, während C für Anwendungen geeignet ist, die eine hohe Leistung und die zugrunde liegende Kontrolle erfordern. 1) C# vereinfacht die Entwicklung, bietet Müllsammlung und reichhaltige Klassenbibliotheken, die für Anwendungen auf Unternehmensebene geeignet sind. 2) C ermöglicht den direkten Speicherbetrieb, der für Spielentwicklung und Hochleistungs-Computing geeignet ist.

Die fortgesetzte Verwendung von C: Gründe für seine AusdauerDie fortgesetzte Verwendung von C: Gründe für seine AusdauerApr 11, 2025 am 12:02 AM

C Gründe für die kontinuierliche Verwendung sind seine hohe Leistung, breite Anwendung und sich weiterentwickelnde Eigenschaften. 1) Leistung mit hoher Effizienz. 2) weit verbreitete: Glanz in den Feldern der Spieleentwicklung, eingebettete Systeme usw. 3) Kontinuierliche Entwicklung: Seit seiner Veröffentlichung im Jahr 1983 hat C weiterhin neue Funktionen hinzugefügt, um seine Wettbewerbsfähigkeit aufrechtzuerhalten.

See all articles

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

AI Hentai Generator

AI Hentai Generator

Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

R.E.P.O. Energiekristalle erklärten und was sie tun (gelber Kristall)
1 Monate vorBy尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Beste grafische Einstellungen
1 Monate vorBy尊渡假赌尊渡假赌尊渡假赌
Will R.E.P.O. Crossplay haben?
1 Monate vorBy尊渡假赌尊渡假赌尊渡假赌

Heiße Werkzeuge

WebStorm-Mac-Version

WebStorm-Mac-Version

Nützliche JavaScript-Entwicklungstools

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

Herunterladen der Mac-Version des Atom-Editors

Herunterladen der Mac-Version des Atom-Editors

Der beliebteste Open-Source-Editor

DVWA

DVWA

Damn Vulnerable Web App (DVWA) ist eine PHP/MySQL-Webanwendung, die sehr anfällig ist. Seine Hauptziele bestehen darin, Sicherheitsexperten dabei zu helfen, ihre Fähigkeiten und Tools in einem rechtlichen Umfeld zu testen, Webentwicklern dabei zu helfen, den Prozess der Sicherung von Webanwendungen besser zu verstehen, und Lehrern/Schülern dabei zu helfen, in einer Unterrichtsumgebung Webanwendungen zu lehren/lernen Sicherheit. Das Ziel von DVWA besteht darin, einige der häufigsten Web-Schwachstellen über eine einfache und unkomplizierte Benutzeroberfläche mit unterschiedlichen Schwierigkeitsgraden zu üben. Bitte beachten Sie, dass diese Software

Sicherer Prüfungsbrowser

Sicherer Prüfungsbrowser

Safe Exam Browser ist eine sichere Browserumgebung für die sichere Teilnahme an Online-Prüfungen. Diese Software verwandelt jeden Computer in einen sicheren Arbeitsplatz. Es kontrolliert den Zugriff auf alle Dienstprogramme und verhindert, dass Schüler nicht autorisierte Ressourcen nutzen.