2016-04-11 7 views
3

Ich verwende das Beispiel here, um ein Repository-Muster mit Arbeitseinheit zu erstellen.Repository-Muster Nur gewünschte Spalten abrufen

Im Code, gibt es eine generische Methode Get:

public class GenericRepository<TEntity> where TEntity : class 
{ 
    internal AdminDbContext context; 
    internal DbSet<TEntity> dbSet; 

    public GenericRepository(AdminDbContext context) 
    { 
     this.context = context; 
     this.dbSet = context.Set<TEntity>(); 
    } 

    public virtual IEnumerable<TEntity> Get(
     Expression<Func<TEntity, bool>> filter = null, 
     Func<TEntity, TEntity> selector = null, 
     Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, 
     string includeProperties = "") 
    { 
     IQueryable<TEntity> query = dbSet; 

     if (filter != null) 
     { 
      query = query.Where(filter); 
     } 


     foreach (var includeProperty in includeProperties.Split 
      (new char[] { ','}, StringSplitOptions.RemoveEmptyEntries)) 
     { 
      query = query.Include(includeProperty); 
     } 

     if (orderBy != null) 
     { 
      return orderBy(query).ToList(); 
     } 
     else 
     { 
      return query.ToList(); 
     } 
    } 

In diesem Moment, wenn diese Methode aufgerufen wird, werden alle Datensätze aus der Datenbank geholt und Spaltenauswahl wird im Speicher durchgeführt.

Meine Frage ist, wie kann ich hier die Get-Methode erweitern, die mir erlauben würde, Spaltennamen dynamisch an diese Methode zu übergeben, so dass nur die gewünschten Spalten auf Datenbankebene ausgewählt werden?

Ich habe versucht:

  • einen String-Parameter mit dem Komma getrennte Werte der gewünschten Spalten akzeptieren aber, dass ich sie an das Unternehmen nicht zuordnen konnte.
  • Erstellen eines Parameters mit dem gleichen Filtertyp, der einen Fehler ergab.
+1

Mit '.Select()'. – CodeCaster

Antwort

1

Das ist eine lange Antwort, aber hier ist eine Erweiterung Methode, die ich dafür gemacht habe. Ich gebe ein Objekt in diesem Fall zurück, da dies in der WebAPI verwendet wird, um nur json zurückzugeben, und ich brauche keinen bestimmten Typ, aber dies kann leicht angepasst werden, um einen generischen Entitätstyp zurückzugeben.

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Linq; 
using System.Reflection; 
using Newtonsoft.Json.Linq; 

namespace Your.Extensions 
{ 
    public enum PropertyFormat 
    { 
     AsIs, 
     PascalCase, 
     CamelCase 
    } 

    public static class DataShapingExtensions 
    { 
     public static object ToDataShape<ObjectIn>(this ObjectIn objectToShape, string fields, PropertyFormat propertyFormat = PropertyFormat.AsIs) where ObjectIn : class 
     { 
      var listOfFields = new List<string>(); 

      if (!string.IsNullOrWhiteSpace(fields)) 
      { 
       listOfFields = fields.ToLower().Split(',').ToList(); 
      } 

      if (listOfFields.Any()) 
      { 
       var objectToReturn = new JObject(); 

       //==== 
       var enumerable = objectToShape as IEnumerable; 

       if (enumerable != null) 
       { 
        var listOfObjects = new List<JObject>(); 

        foreach (var item in enumerable) 
        { 
         var objectToReturn2 = new JObject(); 

         listOfFields.ForEach(field => 
         { 
          try 
          { 
           var prop = item.GetType() 
            .GetProperty(field, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); 

           var fieldName = prop.Name; 
           var fieldValue = prop.GetValue(item, null); 

           fieldName = GetName(fieldName, propertyFormat); 
           objectToReturn2.Add(new JProperty(fieldName, fieldValue)); 
          } 
          catch (Exception ex) { } 
         }); 

         listOfObjects.Add(objectToReturn2); 
        } 

        return listOfObjects.ConvertAll(o => o); 
       } 
       //==== 

       listOfFields.ForEach(field => 
       { 
        try 
        { 
         var prop = objectToShape.GetType() 
          .GetProperty(field, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); 

         var fieldName = prop.Name; 
         var fieldValue = prop.GetValue(objectToShape, null); 

         fieldName = GetName(fieldName, propertyFormat); 
         objectToReturn.Add(new JProperty(fieldName, fieldValue)); 
        } 
        catch (Exception ex) { } 
       }); 

       return objectToReturn; 
      } 

      return objectToShape; 
     } 

     private static string GetName(string field, PropertyFormat propertyFormat) 
     { 
      switch (propertyFormat) 
      { 
       case PropertyFormat.AsIs: return field; 
       case PropertyFormat.PascalCase: return field.ToPascalCase(); 
       case PropertyFormat.CamelCase: return field.ToCamelCase(); 
       default: return field; 
      } 
     } 
    } 
} 

// Usage

[HttpGet, Route("api/someroute")] 
public async Task<IHttpActionResult> YourMethod(string fields = null) 
{ 
    try 
    { 
     var products = await yourRepo.GetProductsList(); 

     if (fields.HasValue()) 
     { 
       return Ok(products.ToDataShape(fields, PropertyFormat.CamelCase)); 
     } 

     return Ok(products); 
    } 
    catch (Exception) 
    { 
     return InternalServerError(); 
    } 
} 

// Aufruf Route

/api/someroute?fields=productID,productName 

// Ausgabe (json)

{ 
    productID: 1, 
    productName: "Some Name" 
} 
+0

Danke für die Zeit, aber mit dieser Methode werden die Daten im Speicher "geformt", nachdem die Datensätze aus der Datenbank abgerufen wurden, richtig? Ich hoffe, eine Lösung zu finden, die den richtigen SQL-Befehl an die Datenbank sendet. –

+0

@KemalEmin Sie könnten versuchen, erweitern Sie Ihr Repository zu 'IQuerable GetAsQuerable()' und 'Select (Ihre Spaltennamen)' – Eldho

+0

@KemalEmin Ich denke, ich missverstanden, was Sie zu tun versuchen. Als Code Caster in den Kommentaren hingewiesen, die Sie einfach verwenden können. Wählen Sie (als Query), aber dann würden Sie es nicht als den gewünschten Entitätstyp zurückgeben (es sei denn, Sie wollten alle anderen Felder null sein). –