Archiv

Archive for the ‘C#’ Category

ASP.NET MVC 5 Paging selber implementiert

Bei meinem aktuellen Projekt, wollte ich das Paging in einer MVC Applikation selber, ohne Mithilfe der NuGet PagedList for MVC, realisieren.

Zuerst der Reihe nach

  1. Wie wird die erste Seite aufgerufen?
  2. Wie werden dann die folgenden Seiten aufgerufen?

Wie wird die erste Seite aufgerufen?

Zuerst schauen wir uns den HTML Markup des Linkes an, bei welchem wir zur entsprechenden Seite navigieren.


Wie ihr seht ist dass ein ganz normaler Querystring über http

Die Seite wird dann über folgende ActionMethode aufgerufen, wie auch die Seiten die dann folgen. Zum Beispiel Seite 2 etc.

Wie werden dann die folgenden Seiten aufgerufen?

            int toalPageCount = (context.Set<Skater>().Where(skater => skater.IsActive && skater.IsSEV).OrderBy(skater => skater.Lastname).Count() / 10);
            int pageCounter = 0;

            Dictionary<int, List<SkaterDTO>> pages = new Dictionary<int, List<SkaterDTO>>();
            List<SkaterDTO> DTO = new List<SkaterDTO>();                        

            if (selectedPage == 1)
            {
                context.Set<Skater>().OrderBy(skater => skater.Lastname).Where(skater => skater.IsActive && skater.IsSEV).Take(10).ToList().ForEach(skater =>
                {
                    var vm = new SkaterDTO(skater);
                    DTO.Add(vm);
                });
                pages.Add(toalPageCount, DTO.ToList());
                pageCounter++;                    
            }
            else if (selectedPage > 1)
            {
                if (selectedPage == toalPageCount)
                {
                    context.Set<Skater>().OrderByDescending(skater => skater.Lastname).Where(skater => skater.IsActive && skater.IsSEV).Take(10).ToList().ForEach(skater =>
                    {
                        var vm = new SkaterDTO(skater);
                        DTO.Add(vm);
                    });
                }
                else
                {
                    context.Set<Skater>().OrderBy(skater => skater.Lastname).Where(skater => skater.IsActive && skater.IsSEV).Skip(selectedPage * 10).Take(10).ToList().ForEach(skater =>
                    {
                        var vm = new SkaterDTO(skater);
                        DTO.Add(vm);
                    });
                }
                pages.Add(toalPageCount, DTO.ToList());
                pageCounter++;                                        
            }
                       

            return PartialView("Skaters", pages);

Die HTML Seite für die Darstellung des Resultates sieht dann wie folgt aus:

@model System.Collections.Generic.Dictionary<int, System.Collections.Generic.List<EislaufSektionWebUI.DTO.SkaterDTO>>

@{
    ViewBag.Title = "Läufer(innen)";
    Layout = "~/Views/Shared/_LayoutPage.cshtml";
}

@Html.ActionLink("Zurück", "Welcome", "Home")

<table class="table table-responsive table-bordered table-hover">

        <thead>
            <tr>
                <th>
                    Vorname
                </th>
                <th>
                    Name
                </th>
                <th>
                    Bestandene Tests
                </th>
                <th>
                    SEV
                </th>
                <th>
                    Geburtsdatum
                </th>
            </tr>
        </thead>
        <tbody>
            @{               
                var currentPage = int.Parse(HttpContext.Current.Request.QueryString["selectedPage"]);
                foreach (var skater in Model[currentPage-1].ToList())
                {
                    <tr>
                        <td>
                            @skater.Firstname

                        </td>
                        <td>
                            @skater.Lastname
                        </td>
                        <td>
                            @skater.CertificationLevel
                        </td>
                        <td>
                            @skater.IsSEV
                        </td>
                        <td>
                            @skater.DateOfBirth
                        </td>
                    </tr>
                }
            }
        </tbody>
    </table>

<nav>
    <ul class="pagination">
        <li>
            <a href="#" aria-label="Previous">
                <span aria-hidden="true">&laquo;</span>
            </a>
        </li>
        @{
            foreach (var key in Model.Keys)
            {
                var number = key + 1;
                <li>
                    <a href="/Person/GetSkaters?selectedPage=@number">@number</a>
                </li>
            }
        }
        <li>
            <a href="#" aria-label="Next">
                <span aria-hidden="true">&raquo;</span>
            </a>
        </li>
    </ul>
</nav>

Die Twitter Bootstrap „pagination“ Links werden dann wie folgt generiert:

<nav>
    <ul class="pagination">
        <li>
            <a href="#" aria-label="Previous">
                <span aria-hidden="true">&laquo;</span>
            </a>
        </li>
        @{
            foreach (var key in Model.Keys)
            {
                var number = key + 1;
                <li>
                    <a href="/Person/GetSkaters?selectedPage=@number">@number</a>
                </li>
            }
        }
        <li>
            <a href="#" aria-label="Next">
                <span aria-hidden="true">&raquo;</span>
            </a>
        </li>
    </ul>
</nav>

Ich hoffe euch hat der Beitrag gefallen und freue mich über Rückmeldungen oder konstruktive Kritik.

Schritt für Schritt zum eigenen generischen Repository

Das Speichern und Abfragen von Daten wäre mit dem klassischen Repository-Pattern viel zu aufwändig für kleinere Projekte. In Anlehnung an das hier, in Verbindung mit dem Unit Of Work Pattern, verwendete generische Repository, habe ich mir überlegt, dass es doch für kleinere Projekte auch eine Lösung geben sollte, die nicht so viel Aufwand nach sich zieht.

Als Grundlage, oder besser gesagt als Ausgangspunkt habe ich mir diesen Artikel zur Brust genommen.

Wie wollen wir vorgehen?

  1. Wir erstellen uns ein ganz triviales Model (Adressverwaltung)
  2. Dann erstellen wir uns Schritt für Schritt das Repository
  3. Am Schluss ziehen wir noch ein Fazit aus dem Artikel
    1. Erstellung des Models

      In Anbetracht, dass nicht zwingend die Korrektheit der Datenmodellierung und der verwendeten Typen im Mittelpunkt steht, erstellen wir uns das Modell wie folgt.

      Das Modell unserer Adressverwaltung

      Wichtig an diesem Punkt ist, dass die Pluralisierung nicht geändert wird (hierzu später mehr bei der Erstellung des Repositories). Die Eigenschaften für die Personen-Entität sind hier represäntativ für alle anderen auch.

      Das Modell unserer Adressverwaltung

      Eine Spezialität ist die komplexe Eigenschaft Persistance, welch die Attribute für

      • CreateDate
      • ModifyDate
      • Creator
      • Modifier

      beherbergt. Schön wäre es ja, wenn alle Attribute bei der entsprechenden Operation des Repositories, Add, Update automatisch aktualisiert wird (dazu später mehr).

      Soweit so gut, wir haben unsere Modell für die 1001 Adressverwaltung erstellt. Wollen wir doch zum nächsten Schritt übergehen, die Erstellung des Repositories.

      Die Repository Erstellung

      Damit wir mit unserem Repository die CRUD Operation unterstützen können brauchen wir folgende Methoden:

      • Rumpf des Repositories
      • Add
      • Remove
      • Update
      • GetObjectById
      • GetObjectByQuery
      • Persistance Attribute automatisch erstellen /aktualisieren
      Rumpf des Repositories

      Damit wir die restlichen Methoden implementieren können müssen wir das Repository wie folgt erstellen:

      namespace GenericRepositoryPattern
      {
          public class Repository<T> : IDisposable where T: EntityObject
          {
              /// <summary>
              /// The context of the database.
              /// </summary>
              private readonly AddressModelContainer context;
              /// <summary>
              /// The related objectSet to the context. Is dependent
              /// of the context.
              /// </summary>
              private readonly ObjectSet<T> objectSet;
              /// <summary>
              /// Constructor to initialize the context and the
              /// objectSet.
              /// </summary>
              /// <param name="context">The context of the model.</param>
              public Repository(AddressModelContainer context)
              {
                  this.context = context;
      
                  if (context != null) 
                  { 
                      this.objectSet = context.CreateObjectSet<T>();
                      this.context.SavingChanges +=new EventHandler(context_SavingChanges);
                  }
              }
      

      (Anmerkung: Die Wiedergabe des Code’s ist gekürzt).
      Wichtig hierbei ist, dass der Context von aussen her dem Konstruktor übergeben werden muss und das ObjectSet vom instantiierten Context das ObjectSet erhält.

      Add Methode

      Damit wir ein neues Objekt zu unserem ObjectContex hinzufügen können müssen wir die Methode wie folgt implementieren.

              /// <summary>
              /// Add's a newly created entity to the
              /// objectSet
              /// </summary>
              /// <param name="entity">The newly created entity.</param>
              public void Add(T entity)
              {
                  this.objectSet.AddObject(entity);            
                  this.context.SaveChanges();                    
              }
      

      Also eine ganz einfache Sache. Als nächstes wenden wir uns der Remove Methode zu.

      Remove Methode

      Das Entfernen eines Objektes ist relativ rasch implementiert:

              public void Remove(T entity)
              {
                  if (entity != null)
                  {
                      this.objectSet.Attach(entity);
                      this.context.DeleteObject(entity);
                      this.context.SaveChanges();
                  }
              }
      

      Also keine Hexerei. Als nächstes wenden wir uns der Update Methode zu und schauen diese genauer an.

      Update Methode

      Die Update Methode zeigt sich in ihrer Ausprägung so:

              public void Update(T entity)
              {
                  this.objectSet.Attach(entity);
                  this.context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);        
                  this.context.SaveChanges();
              }
      

      Damit der Context weiss, dass sich was geändert hat muss die Entität an den Context wieder „Attached“ und im ObjectStateManager der EntityState auf Modified gestellt werden. Dann speichert der Context die Daten aktualisiert.

      GetObjectById

      Damit wir ein Object vom Typ Person erhalten, anhand der übergebenen Id, erstellen wir uns in der Methode einen EntityKey und einem KeyValuePair. Das Attribut dass die Id verkörpert heiss auch „Id“.

              public T GetById(Int32 id)
              {
                  string entitySet = string.Format(@"{0}Set", typeof(T).Name);
                  KeyValuePair<string, object> member = new KeyValuePair<string, object>("Id", id);
                  IEnumerable<KeyValuePair<string, object>> values = new KeyValuePair<string, object>[] { member };
      
                  EntityKey key = new EntityKey(entitySet, values);
      
                  return (T)this.context.GetObjectByKey(key);
              }
      

      Und hier sehen wir auch wieso wir die Pluralisierung PersonSet belassen haben. Damit der Context weiss in welchem Set er suchen soll müssen wir dieses übergeben. Das erreichen wird mit dem kleinen Codeschnipsel

      string entitySet = string.Format(@"{0}Set", typeof(T).Name);
      

      Danach wird der EntityKey erstellt und dem Context zur Abfrage übergeben. Das gefundene Object casten wird dann in den generischen Typ den wir übergeben haben.

      GetObjectByQuery

      Damit wir spezifisch ein Objekt, oder auch mehrere zurück erhalten können wir diese mit dem entsprechenden Methode und einer Func zurück holen. Als Beispiel: Wenn ich eine Person haben möchte bei welchem ich nur den Vornahmen und den Nachnamen haben möchte, dann lege ich beim Aufrufer der Methode folgende Funktion fest:

      Func<Person, bool> functionPerson = person => person.FirstName.ToLower().Equals("hans");
      

      Die kann dann der Methode übergeben werden und diese erstellt dann auch das Resultat anhand der übergebenen Methode.

              public IEnumerable<T> GetObjectByQuery(Func<T, bool> customQuery)
              {
                  if (customQuery == null)
                  {
                      throw new ArgumentNullException("parameter: customquery must be given by caller!");
                  }
      
                  List<T> result = this.objectSet.Where(customQuery).ToList();
                  result.ForEach(this.objectSet.Detach);
                  return result;
              }
      [/csharp]
      <p>Wichtig ist nur, dass die gefundenen Objekte vom Context getrennt werden, damit diese auch ausserhalb der Verwendung des Contextes verwendet werden können.</p>
      <h6>Persistance Attribute aktualisieren</h6>
      <p>Dem Leser wird nicht entgangen sein, dass im Konstruktor der Context mit seinem Ereignis SavingChanges verknüpft worden ist. Hier werden alle relevanten Attribute vom komplexen Property IPersistance aktualisiert oder erstellt. Die Ereignisbehandlung sieht dann so aus:</p>
      
              void context_SavingChanges(object sender, EventArgs e)
              {
                  this.context.ObjectStateManager.GetObjectStateEntries(EntityState.Added).ToList().ForEach(entity => {
                      if (entity != null && entity.Entity != null && entity.Entity.GetType().GetProperty("Persistance") != null)
                      {
                          IPersistance persistance = CreatePersistance();
                          entity.Entity.GetType().GetProperty("Persistance").SetValue(entity.Entity, persistance, null);
                      }
                  });
      
                  this.context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified).ToList().ForEach(entity =>
                  {
                      if (entity != null && entity.Entity != null && entity.Entity.GetType().GetProperty("Persistance") != null)
                      {
                          UpdatePersistance(entity.Entity);
                      }
                  });
              }
      

      Hier wird eigentlich nicht’s anderes gemacht, als das alle Entitäten die den Status Added oder Modified haben das Persistance komplexe Property angepasst. Für das sind dann die zwei Methoden zuständig.

              private IPersistance CreatePersistance()
              {
                  IPersistance persistance = new IPersistance();
                  persistance.CreateDate = DateTime.Now;
                  persistance.ModifyDate = DateTime.Now;
                  persistance.Creator = WindowsIdentity.GetCurrent().Name;
                  persistance.Modifier = persistance.Creator;
                  return persistance;
              }
      

      Wird verwendet wenn eine neue Entität persistiert wird und die nachfolgende wenn eine Entität geändert worden ist.

              private void UpdatePersistance(object entity)
              {
                  if (typeof(T).GetProperty("Persistance") != null)
                  {
                      IPersistance persistance = (IPersistance)typeof(T).GetProperty("Persistance").GetValue(entity, null);
                      if (persistance != null)
                      {
                          persistance.Modifier = WindowsIdentity.GetCurrent().Name;
                          persistance.ModifyDate = DateTime.Now;
                      }
                  }
              }
      

      Zur Übersicht noch der vollständige Code des gesamten Repositories.

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      using System.Data.Objects.DataClasses;
      using GenericRepositoryPattern.Model;
      using System.Data.Objects;
      using System.Data;
      using System.Security.Principal;
      using System.Reflection;
      
      namespace GenericRepositoryPattern
      {
          /// <summary>
          /// Generic repository.
          /// </summary>
          /// <typeparam name="T">For example it could be a model entity Person</typeparam>
          public class Repository<T> : IDisposable where T: EntityObject
          {
              /// <summary>
              /// The context of the database.
              /// </summary>
              private readonly AddressModelContainer context;
              /// <summary>
              /// The related objectSet to the context. Is dependent
              /// of the context.
              /// </summary>
              private readonly ObjectSet<T> objectSet;
              /// <summary>
              /// Constructor to initialize the context and the
              /// objectSet.
              /// </summary>
              /// <param name="context">The context of the model.</param>
              public Repository(AddressModelContainer context)
              {
                  this.context = context;
      
                  if (context != null) 
                  { 
                      this.objectSet = context.CreateObjectSet<T>();
                      this.context.SavingChanges +=new EventHandler(context_SavingChanges);
                  }
              }
              /// <summary>
              /// Get's a specific entity by it's key.
              /// </summary>
              /// <param name="id">The id from the object to retrieve.</param>
              /// <returns>The object if it's present in the db, or NULL if not existent.</returns>
              public T GetById(Int32 id)
              {
                  string entitySet = string.Format(@"{0}Set", typeof(T).Name);
                  KeyValuePair<string, object> member = new KeyValuePair<string, object>("Id", id);
                  IEnumerable<KeyValuePair<string, object>> values = new KeyValuePair<string, object>[] { member };
      
                  EntityKey key = new EntityKey(entitySet, values);
      
                  return (T)this.context.GetObjectByKey(key);
              }
              /// <summary>
              /// Allows the user to query the db with a custom
              /// function to retrieve data from the specific type
              /// of entity.
              /// </summary>
              /// <param name="customQuery">Created outisde by caller</param>
              /// <returns>A queryable of T with the possibility to treat it further.</returns>
              /// <example>
              /// Func<Person, bool> functionPerson = person => person.FirstName.ToLower().Contains("da")
              /// </example>
              public IEnumerable<T> GetObjectByQuery(Func<T, bool> customQuery)
              {
                  if (customQuery == null)
                  {
                      throw new ArgumentNullException("parameter: customquery must be given by caller!");
                  }
      
                  List<T> result = this.objectSet.Where(customQuery).ToList();
                  result.ForEach(this.objectSet.Detach);
                  return result;
              }
      
              /// <summary>
              /// Updates the values of the entity.
              /// </summary>
              /// <param name="entity">Updates a given entity with new values.</param>
              public void Update(T entity)
              {
                  this.objectSet.Attach(entity);
                  this.context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);        
                  this.context.SaveChanges();
              }
              /// <summary>
              /// Add's a newly created entity to the
              /// objectSet
              /// </summary>
              /// <param name="entity">The newly created entity.</param>
              public void Add(T entity)
              {
                  this.objectSet.AddObject(entity);            
                  this.context.SaveChanges();                    
              }
              /// <summary>
              /// Occurs every time an entity is saved or updated.
              /// It goes through the entity properties to update or craete
              /// the persistance attributes that are necessary for saving.
              /// </summary>
              /// <param name="sender">The context</param>
              /// <param name="e">The eventargs from the context.</param>
              void context_SavingChanges(object sender, EventArgs e)
              {
                  this.context.ObjectStateManager.GetObjectStateEntries(EntityState.Added).ToList().ForEach(entity => {
                      if (entity != null && entity.Entity != null && entity.Entity.GetType().GetProperty("Persistance") != null)
                      {
                          IPersistance persistance = CreatePersistance();
                          entity.Entity.GetType().GetProperty("Persistance").SetValue(entity.Entity, persistance, null);
                      }
                  });
      
                  this.context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified).ToList().ForEach(entity =>
                  {
                      if (entity != null && entity.Entity != null && entity.Entity.GetType().GetProperty("Persistance") != null)
                      {
                          UpdatePersistance(entity.Entity);
                      }
                  });
              }
              /// <summary>
              /// Set's the createdate to the datetime
              /// where the creation of the entity
              /// object has been executed.
              /// </summary>
              /// <param name="entity">The entity to create the IPersistance creator and update attributes.</param>
              private IPersistance CreatePersistance()
              {
                  IPersistance persistance = new IPersistance();
                  persistance.CreateDate = DateTime.Now;
                  persistance.ModifyDate = DateTime.Now;
                  persistance.Creator = WindowsIdentity.GetCurrent().Name;
                  persistance.Modifier = persistance.Creator;
                  return persistance;
              }
              /// <summary>
              /// Set's the modifydate to the datetime
              /// where the modification of the entity
              /// object has been executed.
              /// </summary>
              /// <param name="entity">The entity to update the IPersistance modify attributes.</param>
              private void UpdatePersistance(object entity)
              {
                  if (typeof(T).GetProperty("Persistance") != null)
                  {
                      IPersistance persistance = (IPersistance)typeof(T).GetProperty("Persistance").GetValue(entity, null);
                      if (persistance != null)
                      {
                          persistance.Modifier = WindowsIdentity.GetCurrent().Name;
                          persistance.ModifyDate = DateTime.Now;
                      }
                  }
              }
              /// <summary>
              /// Removes an entity from the database.
              /// </summary>
              /// <param name="entity">The entity to remove. Must be loaded prior to remove.</param>
              public void Remove(T entity)
              {
                  if (entity != null)
                  {
                      this.objectSet.Attach(entity);
                      this.context.DeleteObject(entity);
                      this.context.SaveChanges();
                  }
              }
      
              #region IDisposable Members
              /// <summary>
              /// Dispose the context.
              /// </summary>
              public void Dispose()
              {
                  if (this.context != null)
                  {
                      this.context.Dispose();
                  }
              }
      
              #endregion
          }
      }
      

      Fazit

      Mit dem hier vorgestellten Ansatz ist es uns , auch für kleinere Projekte möglich, rasch und einfach CRUD Methoden zur Verfügung zu stellen ohne, dass wir das Unit of Work und das Repositoriy Pattern in ihren grössten Ausprägungen implementieren müssen.

      Wenn Ihnen der Artikel gefallen hat, würde ich mich über einen KICK freuen. Auf Verbesserungsvorschläge und Kritik bin ich natürlich auch offen.

      kick it on dotnet-kicks.de

WCF DataContracts synchronisieren mit Entity Framework Klassen

Im Rahmen einer Projektarbeit haben wir das Ziel Businessdaten über die WCF zu transportieren. Die Daten sind im Entity Framework gespeichert. Schön wäre es wenn wir diese Entitäten direkt über den Kommunikationskanal schicken könnten. Leider ist dies nicht möglich da ein direktes Versenden der Entitäten, durch Projektrestriktionen nicht erlaubt ist, oder man ganz einfach keine Referenzen auf dem Client auf den Namespace System.Data haben möchte.

Also muss eine Lösung her die einigermassen hilfreich sein kann. Diese Lösung erledigt das Mapping eines einkommenden DatenVertrages mit der entsprechenden Entität.

Vorbereitungen
  • AddressManagement.BusinessModel
  • AddressManagement.PersistanceModel
  • AddressManagement.ServiceLayer

Für das Hosten der WCF können wir verschiedene Möglichkeiten nutzen, die aber in diesem Blogpost nicht abgedeckt werden. Es soll nur die Möglichkeit einer generischen Mapping Methode aufgezeigt werden, damit die Daten vom Entity Framework nicht direkt über den Kommunikationskanal versendet werden müssen, sondern gekapselt werden.

Wir erstellen uns ein Entity-Modell dass wie folgt aussieht: (Dieses erstellen wir im Projekt AddressManagement.Persistance)

Das Entity Data Design Model

Anschliessend erstellen wir uns unseres eigentliche Businessklassenmodell welches wir im Projekt AddressManagement.BusinessModel anlegen.

Das Businessklassen Model

Wichtig hierbei ist, dass die Beziehungen, Phones, Addresses genau gleich heissen wie im Entity Model. Auch alle anderen Eigenschaften müssen die gleiche Schreibweise haben, da sonst im Mapper keine Übereinstimmung gefunden wird.

Die Businessklassen sollen dann mit der WCF übertragen werden. Hierzu werden die Klassen mit den entsprechenden Attributen versehen.

Die Klasse Person

namespace AddressManagement.BusinessModel
{
    [DataContract]
    [KnownType(typeof(Address))]
    [KnownType(typeof(Phone))]
    public class Person : IPersistance
    {        
        #region IPersistance Members
        [DataMember]
        public Int32 Id { get; set; }
        #endregion
        [DataMember]
        public string FirstName { get; set; }
        [DataMember]
        public string LastName { get; set; }
        [DataMember]
        public IEnumerable<IPersistance> Addresses { get; set; }
        [DataMember]
        public IEnumerable<IPersistance> Phones { get; set; }
    }
}

Die Klasse Phone

namespace AddressManagement.BusinessModel
{
    [DataContract(IsReference = true)]
    [KnownType(typeof(ObjectType))]
    public class Phone : IPersistance
    {
        #region IPersistance Members
        [DataMember]
        public Int32 Id { get; set; }
        #endregion
        [DataMember]
        public string SubScriberNumber { get; set; }
        [DataMember]
        public ObjectType PhoneType { get; set; }
    }
}

Die Klasse Address

namespace AddressManagement.BusinessModel
{
    [DataContract(IsReference = true)]
    [KnownType(typeof(ObjectType))]
    public class Address : IPersistance
    {
        #region IPersistance Members
        [DataMember]
        public Int32 Id { get; set; }
        #endregion
        [DataMember]
        public string StreetName { get; set; }
        [DataMember]
        public Int16 StreetNumber { get; set; }
        [DataMember]
        public Int16 PostalCode { get; set; }
        [DataMember]
        public string CityName { get; set; }
        [DataMember]
        public ObjectType AddressType { get; set; }
    }
    [DataContract]
    public enum ObjectType
    {
        [EnumMember]
        Private,
        [EnumMember]
        Business
    }
}

Der Mapper/Synchronisierer

Damit wir nun beide Welten miteinander abgleichen können ist der Mapper zu erstellen, welche ich Schritt für Schritt erläutere. Wir erstellen uns als erstes eine Statische Klasse mit dem Namen DataContractToEntityMapper oder DataContractAndEntitySynchronizer und erstellen als erstes folgende Methode:

        /// <summary>
        /// Maps a given datacontract to the appropriate entity.
        /// Check's if the datacontract is new or must be updated
        /// and return the newly persisted contract back or it's updated
        /// values, if it was already persisted before.
        /// </summary>
        /// <typeparam name="T">Type of the datacontract</typeparam>
        /// <typeparam name="U">Type of the entity</typeparam>
        /// <param name="dataContract">The datacontract to interact with.</param>
        /// <returns>A new or updated datacontract from the database.</returns>
        public static IPersistance SynchronizeDataContractAndEntity<T, U>(T dataContract)
            where T : IPersistance
            where U : EntityObject
        {
            IPersistance persistance = Activator.CreateInstance<T>();
            // 1. a) Determine if the datacontract is already persisted.
            if (dataContract.Id == 0)
            {
                // 1. b) Map the newly created entity to the datacontract.                
                PersistDataContract<T, U>(dataContract);
            }
            else
            // 2.) Map an existing entity to it's datacontract pendant
            {
                U persistedEntity = Activator.CreateInstance<U>();
                using (AddressRepository<U> repository = new AddressRepository<U>(new AddressManagementPersistanceModelContainer()))
                {
                    persistedEntity = repository.GetByKey<U>(dataContract.Id);
                    persistance = UpdateProperties<T, U>(dataContract, persistedEntity);
                }
            }
            return persistance;
        }

Diese Methode führ folgende Aktionen durch:

  1. Existiert der eingetroffene DataContract schon in der Datenbank?
  2. Ist die Id 0, dann muss eine neue Entität angelegt werden
  3. Ist die Id > 0 ist der Contract bereits in der Datenbank vorhanden und muss mit den vorliegenden Werten aktualisiert werden.
  4. Zum Schluss, nachdem die Entity und der DataContract miteinander synchronisiert worden sind, wird ein aktualisierter DataContract zurück gegeben.

Als nächster Schritt erstellen wir uns eine Methode die für den Abgleich der Property-Werte zuständig ist. Diese unterscheidet ob es sich bei der Eigenschaft um eine Collection oder um eine einfache Eigenschaft handelt und führt dementsprechend die Aktualisierung aus:

        /// <summary>
        /// Updates the properties from a datacontract.
        /// </summary>
        /// <typeparam name="T">The type of datacontract</typeparam>
        /// <typeparam name="U">The type of the entity</typeparam>
        /// <param name="dataContract">The datacontract to update the values.</param>
        /// <param name="entity">The entity that is persisted and owns the actual values.</param>
        /// <returns>Updated datacontract.</returns>
        private static IPersistance UpdateProperties<T, U>(T dataContract, U entity)
            where T : IPersistance
            where U : EntityObject
        {
            IPersistance persistance = (IPersistance)CreateInstance<T, U>(dataContract, entity);
            entity.GetType().GetProperties().ToList().ForEach(entityPropertyInfo =>
            {
                string propertyName = entityPropertyInfo.Name;
                PropertyInfo dataContractPropertyInfo = persistance.GetType().GetProperty(propertyName);
 
                if (entityPropertyInfo != null && !entityPropertyInfo.PropertyType.Name.Contains(typeof(EntityCollection<U>).Name))
                {
                    var entityPropertyValue = entityPropertyInfo.GetValue(entity, null);
                    if (entityPropertyValue != null && dataContractPropertyInfo != null && !dataContractPropertyInfo.PropertyType.IsEnum)
                    {                        
                        dataContractPropertyInfo.SetValue(persistance, entityPropertyValue, BindingFlags.Default, null, null, null);
                    }
                }
                else if (entityPropertyInfo != null && dataContract.GetType().GetProperty(entityPropertyInfo.Name) != null)
                {
                    var ienumerableValues = (IEnumerable)HandleRelatedEnds<T>(dataContract, (IEnumerable)entityPropertyInfo.GetValue(entity, null));
                    persistance.GetType().GetProperty(entityPropertyInfo.Name).SetValue(persistance, ienumerableValues, BindingFlags.Default, null, null, null);
                }
            });
            return persistance;
        }

Der erste Teil aktualisiert alle flachen Eigenschaften des DatenContracts. Der zweite Teil kümmert sich um die Collections.

        /// <summary>
        /// Handles the collections an creates for the datacontract
        /// appropriate items to add them to the list.
        /// </summary>
        /// <typeparam name="T">Type of datacontract</typeparam>
        /// <param name="dataContract">The specific contract where the list belongs to.</param>
        /// <param name="entityCollectionToDuplicate">The entitycollection to map to the datacontract.</param>
        /// <returns>A List of IPersistance objects (BusinessModel Objects).</returns>
        private static List<IPersistance> HandleRelatedEnds<T>(T dataContract, IEnumerable entityCollectionToDuplicate) where T : IPersistance
        {
            List<IPersistance> persistances = new List<IPersistance>();
            IEnumerator it = entityCollectionToDuplicate.GetEnumerator();
 
            while (it.MoveNext())
            {
                EntityObject entity = ((EntityObject)it.Current);
                IPersistance ipersistance = CreateInstance<T, EntityObject>(dataContract, entity);
                ipersistance = UpdateProperties<T, EntityObject>(dataContract, entity);
                persistances.Add(ipersistance);
            }
 
            return persistances;
        }

Damit die Items erstellt werden können braucht es eine weitere Hilfsmethode die uns entsprechende Instanzen zurück gibt.

        /// <summary>
        /// This method creates a specific instance from the Businessmodell Assembly
        /// given by the entity type.
        /// </summary>
        /// <typeparam name="T">The IPersistanceObject to create</typeparam>
        /// <typeparam name="U">The entity that contains the values</typeparam>
        /// <param name="dataContract">The datacontract to add /remove or update</param>
        /// <param name="entity">The entity that was created /updated depending on the passed datacontract.</param>
        /// <returns></returns>
        private static IPersistance CreateInstance<T, U>(T dataContract, U entity) where T : IPersistance
        {
            IPersistance persistance;
            string typeOfEntity = entity.GetType().Name;
            Assembly assembly = Assembly.GetAssembly(typeof(IPersistance));
            Type type = assembly.GetTypes().FirstOrDefault(assemblyType => assemblyType.Name.ToLower().Equals(typeOfEntity.ToLower()));
            persistance = (IPersistance)assembly.CreateInstance(type.FullName);
 
            return persistance;
        }

[/csharp]
<p>Der Funktionsumfang der oben beschriebenen Methode ist einfach. Nimm den Entity-Namen und suche in der BusinessModel Library nach dem entsprechenden Typ und instanziiere einen solchen und gib diesen dann zurück, damit er in der aufrufenden Methode in die Liste eingefügt werden kann.
Eine weitere Methode brauchen wir, damit wir einen DataContract, der über keinen Wert in der Id verfügt, persistieren können. (Das Repository ist nicht Teil dieses).
</p>

        /// <summary>
        /// A newly passed datacontract with it's properties
        /// will be persisted in the entity framework.
        /// </summary>
        /// <typeparam name="T">DataContractType</typeparam>
        /// <typeparam name="U">EntityType (must macht with the datacontract type)</typeparam>
        /// <param name="dataContract">The datacontract to persist.</param>
        private static void PersistDataContract<T, U>(T dataContract) where T : IPersistance where U : EntityObject
        {
            U entityToPersist = Activator.CreateInstance<U>();
            entityToPersist.GetType().GetProperties().ToList().ForEach(dataContractPropertyInfo =>
            {
                string propertyName = dataContractPropertyInfo.Name;
                PropertyInfo entityPropertyInfo = dataContract.GetType().GetProperty(propertyName);
 
                if (entityPropertyInfo != null && entityPropertyInfo.PropertyType != (typeof(RelatedEnd)))
                {
                    var entityPropertyValue = entityPropertyInfo.GetValue(dataContract, null);
                    if (entityPropertyValue != null)
                    {
                        dataContractPropertyInfo.SetValue(entityToPersist, entityPropertyValue, BindingFlags.Default, null, null, null);
                    }
                }
            });
 
            using (AddressRepository<U> repository = new AddressRepository<U>(new AddressManagementPersistanceModelContainer()))
            {
                repository.Add(entityToPersist);
                repository.SaveChanges();
            }
        }

Mit dieser letzten Methode haben wir den Mapper erstellt. Nun ist es uns möglich von einem Sender ein Objekt der Businessklassen zu erstellen und müssen uns keine Sorgen machen, ob dies bereits persistiert worden ist oder nicht. Wir erhalten als dann die aktualisierten oder neuen Werte von der Datenbank als Resultat zurück und können somit auf dem Client weiter arbeiten.

Fazit

Der Mapper hat nicht den Anspruch 100% aller Fälle abzudecken, er versucht zu zeigen wie man eine grösstmögliche Modularisierung anstreben kann, ohne dass auf dem Client Server DLL’s refernziert werden müssen. Und natürlich ein nicht zu unterschätzender Punkt ist der Aufwand einmal für die WCF Seite und einmal für die Persistance-Seite ein Modell zu führen.

Sollte Ihnen der Artikel gefallen haben, würde ich mich über einen kick freuen. Gerne beantworte ich auch Fragen und Kritik.

kick it on dotnet-kicks.de

Wie unterscheide ich zwei Web-Applikationen?

Folgende Ausgangssituation:

– Wir haben einen Persistency-Layer mit NHibernate

– Wir hatten ein WebFrontEnd

– Wir werden ein zweites WebFrontEnd haben

und beide WebFrontEnds sollen das gleiche Modul für die Registrierung des Nhibernate Modules verwenden.

Damit die beiden Applikationen unterschieden und auch die Registrierung der Assemblies funktioniert reicht ein Eintrag in der Web.Config der jeweiligen Applikation.

Diese habe ich für die erste WebApplikation so gewählt:

    <appSettings>
<add key="WebType" value="WebApplicationOne"/>
</appSettings></pre>

Und für die zweite WebApplikation so:

<appSettings>
<add key="WebType" value="WebApplicationTwo"/>
</appSettings>

Damit dann der Unterschied im Init-Module geschehen kann wurde der Init Code wie folgt geändert. Vorher

// Initialize NHibernate application-wide for the Buraut assemblies HibernateUtil hibernateUtil = new Buraut.Persistency.Sql.NHibernate.HibernateUtil();
hibernateUtil.RegisterAssembly("Buraut.Ais"); hibernateUtil.RegisterAssembly("Buraut.Ais.WebFrontEnd.Implementation");
hibernateUtil.RegisterAssembly("Buraut.Soa");

Nachher

// Initialize NHibernate application-wide for the Buraut assemblies HibernateUtil hibernateUtil = new Buraut.Persistency.Sql.NHibernate.HibernateUtil();
if (System.Web.Configuration.WebConfigurationManager.AppSettings["WebType"].Equals(WebApplicationOne)) {
hibernateUtil.RegisterAssembly("Buraut.Ais.KpManager");
hibernateUtil.RegisterAssembly("Buraut.Ais"); hibernateUtil.RegisterAssembly("Buraut.Soa");
} else if (System.Web.Configuration.WebConfigurationManager.AppSettings["WebType"].Equals(WebApplicationTwo)) {
hibernateUtil.RegisterAssembly("Buraut.Ais"); hibernateUtil.RegisterAssembly("Buraut.Ais.WebFrontEnd.Implementation");
hibernateUtil.RegisterAssembly("Buraut.Soa"); }

Für Fragen und Kritik bin ich immer offen und würde mich über eine Rückmeldung freuen.

DotNetKicks-DE Image

Sekundenanzahl berechnen auf Tage, Stunden, Minuten und Sekunden

28. Oktober 2010 4 Kommentare

Es ist ja immer wieder schön etwas zu lernen, so habe ich dass heute während dem Unterricht auf jedenfall gesehen. Am Morgen Algorithmen und Datenstrukturen, unabhängig ob jetzt in .NET oder JAVA. Selten weiss man noch wie die Konstrukte aufgebaut sind und was sich dahinter versteckt. Heute Nachmittag bin ich dann ein wenig ins Grübeln gekommen, an einer eigentlich sehr trivialen Aufgabe, die wie folgt formuliert ist:

Ein Satellit funkt Zeitspannen als “Anzahl Sekunden” Zur Erde. Schreiben Sie ein Programm das dies berechnet wobei gilt:

d = Anzahl Tage

h = Anzahl Stunden im Bereich 0 bis 23

m = Anzahl Minuten im Bereich 0 bis 59

s = Anzahl Sekudnen im Bereich von 0 bis 59

C# Int32 wie auch Java Int32 Datentypen stellen Ganzzahlen dar, sodass man sich bei der Lösung darauf beschränken kann, dass Komma-Stellen einfach abgeschnitten werden. Hier die Lösung zum Problem:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Samples.SoftwareDevelopment05.InCSharp
{
    class Program
    {        
        private const int totalSecondsAday = 86400;
        private const int totalSecondsAnHour = 3600;
        private const int totalSecondsAMinute = 60;

        /// <summary>
        /// Mains the specified args.
        /// </summary>
        /// <param name="args">The args.</param>
        static void Main(string[] args)
        {
            Console.Write("Schreiben sie einen Wert für die Anzahl Sekunden: ");
            int input = 0;
            int.TryParse(Console.ReadLine(), out input);
            CalculateSateliteTime(input).ToList().ForEach(i => Console.Write(" " + i.ToString()));

            // Nachtrag von René
            Console.WriteLine(TimeSpan.FromSeconds(input));

            Console.ReadKey(true);
        }

        /// <summary>
        /// Calculates the satelite time.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <returns></returns>
        private static int[] CalculateSateliteTime(int input)
        {
            int seconds = 0;
            int minutes = 0;
            int hours = 0;
            int days = 0;

            days = input / totalSecondsAday;
            input -= days * totalSecondsAday;
            hours = input / totalSecondsAnHour;
            input -= hours * totalSecondsAnHour;
            minutes = input / totalSecondsAMinute;
            input -= minutes * totalSecondsAMinute;
            seconds = input;

            return new int[] { days, hours, minutes, seconds };
        }
    }
}

Für Anregungen und Kritik bin ich immer offen und hoffe doch auf die eine oder andere Rückmeldung.

DotNetKicks-DE Image
Kategorien:C#

Eine kleine RssReader DLL

Nach langem Überlegen habe ich mich entschlossen einen kleinen RSSReader zu machen. Ich finde es immer sehr mühsam die RSS-Feeds zu abonnieren. Nun gut, die Klasse SyndicationFeed bietet alle relevanten Funktionen, damit ein RssFeed dargestellt werden kann.

Mein Grundüberlegungen für die DLL sind:

1. Ich möchte die Feed-URL’s nicht eingeben. Die sollen bereits vorhanden sein

2. Die DLL soll dann in allen Technologien verwendet werden können (ASP.NET WebForms, ASP.NET-MVC, WPF und Silverlight 4 laufen soll.

Also fangen wir damit an. Zuerst habe ich mir ein DLL Projekt erstellt mit der folgenden Struktur:

image

In der Settings.settings-Datei habe ich die NewsFeeds und deren URL’s eingetragen. Die Überlegung warum ein Dictionary<string, List<SyndicationFeed>>() erstellt worde ist, ist die dass pro Key ein Fenster erstellt wird und dann die Liste des Key’s die News anzeigt. Die RssFeedHelper Klasse sieht so aus:

   1: using System;

   2: using System.Collections.Generic;

   3: using System.Linq;

   4: using System.Text;

   5: using System.ServiceModel.Syndication;

   6: using Schaedler.RssReader.Data.Provider.Properties;

   7: using System.Configuration;

   8: using System.Collections;

   9: using System.Net;

  10: using System.Xml;

  11: using System.IO;

  12: using System.ComponentModel;

  13:  

  14: namespace Schaedler.RssReader.Data.Provider.Helper

  15: {

  16:     public class RssFeedHelper

  17:     {

  18:         /// <summary>

  19:         /// Dieses Property ist ein Temporärer Vergleich,

  20:         /// damit geprüft werden kann, ob alle Settings

  21:         /// durchgearbeitet worden sind.

  22:         /// </summary>

  23:         private int propertyCounter = 0;

  24:  

  25:         /// <summary>

  26:         /// Die Collection der Settings die im RssFeedReader

  27:         /// vorhanden sind.

  28:         /// </summary>

  29:         private SettingsProperty currentSelectedSettingsProperty = null;

  30:         /// <summary>

  31:         /// Iterator zum Durchlaufen aller Settings die erfasst worden sind,

  32:         /// damit diese dann abgearbeitet werden können.

  33:         /// </summary>

  34:         private IEnumerator settingsPrperties = null;

  35:  

  36:         /// <summary>

  37:         /// Feld für die das dazugehörige Property.

  38:         /// </summary>

  39:         /// <seealso cref="SyndicationDictionary" Beschreibung/>

  40:         private IDictionary<string, IList<SyndicationItem>> _syndicationDicationary = null;

  41:  

  42:         /// <summary>

  43:         /// Jedes Property in den Settings erzeugt einen Schlüssel der im Dictionary

  44:         /// gespeichert wird. Unter diesem Schlüssel sind dann alle FeedItems des

  45:         /// jeweiligen RssFeeds in einer Liste gespeichert.

  46:         /// </summary>

  47:         public IDictionary<string, IList<SyndicationItem>> SyndicationDictionary

  48:         {

  49:             get

  50:             {

  51:                 return _syndicationDicationary;

  52:             }

  53:             set

  54:             {

  55:                 _syndicationDicationary = value;             

  56:             }

  57:         }

  58:  

  59:         /// <summary>

  60:         /// Wird auf true gesetzt, wenn der WebClient seinen Download

  61:         /// beendet hat und das Feld propertycounter die gleiche Anzahl aufweist

  62:         /// wie die SettingsPropertyCollection.

  63:         /// </summary>

  64:         public bool IsAllDownloaded { get; private set; }

  65:  

  66:         /// <summary>

  67:         /// Konstruktor der die felder

  68:         /// - settingsProperties

  69:         /// und den WebClient konfiguriert.

  70:         /// </summary>

  71:         public RssFeedHelper()

  72:         {

  73:             settingsPrperties = Settings.Default.Properties.GetEnumerator();

  74:             while (settingsPrperties.MoveNext())

  75:             {

  76:                 SettingsProperty property = (SettingsProperty)settingsPrperties.Current;

  77:                 currentSelectedSettingsProperty = property;

  78:                 

  79:                 WebClient client = new WebClient();

  80:                 Uri downloadUri = new Uri(property.DefaultValue.ToString());

  81:                 client.DownloadStringAsync(downloadUri);

  82:                 client.DownloadStringCompleted +=new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);                

  83:             }      

  84:         }

  85:  

  86:         /// <summary>

  87:         /// Stellt Daten für das DownloadStringCompleted-Ereignis bereit.

  88:         /// </summary>

  89:         /// <param name="sender">Der Webclient</param>

  90:         /// <param name="e">Die EventArgs zum WebClient</param>

  91:         void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)

  92:         {

  93:             if (_syndicationDicationary == null)

  94:             {

  95:                 _syndicationDicationary = new Dictionary<string, IList<SyndicationItem>>();

  96:             }

  97:  

  98:             IList<SyndicationItem> syndicationItems = SyndicationFeed.Load(XmlReader.Create(new StringReader(e.Result))).Items.ToList<SyndicationItem>();

  99:             _syndicationDicationary.Add(currentSelectedSettingsProperty.Name, syndicationItems);

 100:  

 101:             propertyCounter += 1;

 102:  

 103:             if (propertyCounter == Settings.Default.Properties.Count)

 104:             {

 105:                 IsAllDownloaded = true;

 106:             }

 107:         }

 108:     }

 109: }

Das Aufrufende Programm ist eine Simple Konsolen-Applikation. Hier noch der Code dazu:

   1: using System;

   2: using System.Collections.Generic;

   3: using System.Linq;

   4: using System.Text;

   5: using Schaedler.RssReader.Data.Provider.Helper;

   6: using Schaedler.RssReader.Data.Provider;

   7: using System.Collections;

   8: using System.ServiceModel.Syndication;

   9: using System.Threading;

  10:  

  11: namespace Schaedler.RssReader.ConsoleUI

  12: {

  13:     class Program

  14:     {

  15:         /// <summary>

  16:         /// Statische, einmalige Initialisierung des RssFeedHelpers.

  17:         /// </summary>

  18:         private static RssFeedHelper helper = new RssFeedHelper();

  19:  

  20:         /// <summary>

  21:         /// Standard Einsprungspunkt für ein ConsolenProgramm.

  22:         /// </summary>

  23:         /// <param name="args"></param>

  24:         static void Main(string[] args)

  25:         {

  26:             Console.BackgroundColor = ConsoleColor.DarkBlue;

  27:             Console.ForegroundColor = ConsoleColor.White;

  28:             Console.WindowWidth = 200;

  29:             Console.WindowHeight = 95;

  30:             GetNews(helper);

  31:             Console.ReadKey(true);

  32:         }

  33:  

  34:         /// <summary>

  35:         /// Holt die News, die im SettingsFile der RssReader.Data.Provider.dll liegen

  36:         /// und gibt diese aus.

  37:         /// </summary>

  38:         /// <param name="helper">Eine Instanz des RssFeedHelpers</param>

  39:         private static void GetNews(RssFeedHelper helper)

  40:         {

  41:             if (helper.IsAllDownloaded)

  42:             {

  43:                 foreach (KeyValuePair<string, IList<SyndicationItem>> item in helper.SyndicationDictionary)

  44:                 {

  45:                     Console.WriteLine(item.Key);

  46:                     item.Value.ToList<SyndicationItem>().ForEach(i => Console.Write(i.Title.Text + Environment.NewLine + i.Summary.Text + Environment.NewLine + 

  47:                                                                                     i.PublishDate + Environment.NewLine + "--------------------------------"));

  48:                 }

  49:             }

  50:             else

  51:             {

  52:                 // Der Timer muss gesetzt werden, da das Consolen Programm

  53:                 // nicht so komfortable mit einer ObservableCollection umgehen kann.

  54:                 Thread.Sleep(new TimeSpan(0, 0, 30));

  55:                 GetNews(helper);

  56:             }

  57:         }

  58:     }

  59: }

Bis jetzt sind nur die TopNews von MSDN hinzugefügt. Für die weiteren Beispiele werde ich zusätzliche News hinzufügen.

var insertLocation = document.getElementById(‚dnkContainer‘);
var encodedTitle = encodeURI(document.title).replace(‚#‘, ‚%23‘);
if (insertLocation) {
var currentPageUrl = document.URL + „&title=“ + encodedTitle;
var dotnetkicksLink = document.createElement(‚a‘);
dotnetkicksLink.href = ‚http://dotnet-kicks.de/kick/?url=&#8216; + currentPageUrl;
var dotnetkicksImg = document.createElement(‚IMG‘);
dotnetkicksImg.src = ‚http://dotnet-kicks.de/Services/Images/KickItImageGenerator.ashx?url=&#8216; + currentPageUrl;
dotnetkicksImg.border = 0;
dotnetkicksLink.appendChild(dotnetkicksImg);
insertLocation.appendChild(dotnetkicksLink);
}

DotNetKicks-DE Image
Kategorien:C#

Was ist schneller… Foreach, LAMBDA Expressions, oder LINQ?

9. September 2009 4 Kommentare

Ich habe mich schon lange gefragt, was wohl schneller ist. Bei uns in der Firma werden immer Werte als IList<T> zurück gegeben. Ziemlich mühsam, wenn man diese immer mit while, foreach oder for durch gehen muss bis man das Gesucht gefunden hat. Deshalb habe ich mich dafür interessiert was denn schneller ist.

Zuerst habe ich mir zwei Klassen erstell (Person und PersonList beides Dummy Klassen)

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5:  
   6: namespace PerformanceComparing.Data.DataHelpers {
   7:     /// <summary>
   8:     /// Class for Dummy object person.
   9:     /// </summary>
  10:     public class Person {
  11:  
  12:         /// <summary>
  13:         /// Gets or sets the first name.
  14:         /// </summary>
  15:         /// <value>The first name.</value>
  16:         public string FirstName { get; set; }
  17:  
  18:         /// <summary>
  19:         /// Gets or sets the last name.
  20:         /// </summary>
  21:         /// <value>The last name.</value>
  22:         public string LastName { get; set; }
  23:  
  24:         /// <summary>
  25:         /// Gets or sets the age.
  26:         /// </summary>
  27:         /// <value>The age.</value>
  28:         public int Age { get; set; }
  29:     }
  30: }

Hier noch die PersonList Klasse

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5:  
   6: namespace PerformanceComparing.Data.DataHelpers {
   7:     /// <summary>
   8:     /// List class that holds a defined count
   9:     /// of objects (indicated outside).
  10:     /// </summary>
  11:     public class PersonList : List<Person> {
  12:  
  13:         /// <summary>
  14:         /// Firstname array.
  15:         /// </summary>
  16:         string[] _firstnames = new string[] { "Hans", "Ueli", "Reto", "Peter", "Hansruedi", "Roland" };
  17:         /// <summary>
  18:         /// LastName array
  19:         /// </summary>
  20:         string[] _lastnames = new string[] { "Müller", "Meier", "Hugentobler", "Rechner", "Landolt" };
  21:  
  22:  
  23:         /// <summary>
  24:         /// Initializes a new instance of the <see cref="PersonList"/> class.
  25:         /// </summary>
  26:         /// <param name="count">The count.</param>
  27:         public PersonList(int count) {
  28:             Random randomFirstNames = new Random();
  29:             Random randomLastNames = new Random();
  30:             Random randomAges = new Random();
  31:  
  32:             for(int i = 0; i < count; i++) {
  33:                 Person person = new Person();
  34:                 person.FirstName = _firstnames[randomFirstNames.Next(0, _firstnames.Length)];
  35:                 person.LastName = _lastnames[randomLastNames.Next(0, _lastnames.Length)];
  36:                 person.Age = randomAges.Next(0, 130);
  37:                 this.Add(person);
  38:             }
  39:         }
  40:     }
  41: }

Danach habe ich mir eine PerformanceComparer Klasse erstellt, welche eine Stopwatch (System.Diagnostics Namespace) verwendet und die Resultate an die Properties weiter gibt.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using PerformanceComparing.Data.DataHelpers;

namespace PerformanceComparing.Data.Utils {

    /// <summary>
    /// Enum for indicating for which
    /// performance count the instance will
    /// be used.
    /// </summary>
    public enum ComparerEnum {
        /// <summary>
        /// Case LINQ
        /// </summary>
        LINQ,
        /// <summary>
        /// Case LAMBDA
        /// </summary>
        LAMBDA,
        /// <summary>
        /// Case foreach
        /// </summary>
        FOREACH        
    }

    /// <summary>
    /// Class for initialising a PerformanceComparer
    /// </summary>
    public class PerformanceComparer {

        /// <summary>
        /// Filled once at inititialisation of the class.
        /// </summary>
        private PersonList _personList = null;

        /// <summary>
        /// Gets or sets the start.
        /// </summary>
        /// <value>The start.</value>
        public DateTime Start { get; private set;}
        /// <summary>
        /// Gets or sets the end.
        /// </summary>
        /// <value>The end.</value>
        public DateTime End {get; private set;}
        /// <summary>
        /// Gets or sets the stop watch.
        /// </summary>
        /// <value>The stop watch.</value>
        public Stopwatch StopWatch { get; private set; }


        /// <summary>
        /// Initializes a new instance of the <see cref="PerformanceComparer"/> class.
        /// With the a comparer Enum and a generated personlist (outside generated).
        /// </summary>
        /// <param name="comparer">The comparer.</param>
        /// <param name="personenList">The personen list.</param>
        public PerformanceComparer(ComparerEnum comparer, PersonList personenList) {
            _personList = personenList;
            ExecuteQuery(comparer);
        }

        /// <summary>
        /// Executes the query.
        /// </summary>
        /// <param name="comparerenum">The comparerenum.</param>
        private void ExecuteQuery(ComparerEnum comparerenum){
            Stopwatch stopwatch = new Stopwatch();

            switch(comparerenum) {
                case ComparerEnum.LINQ:
                    Start = DateTime.Now;
                    stopwatch.Start();
                    var Linq_Result = (from entry in _personList where entry.FirstName.Equals("Hans") select entry);
                    stopwatch.Stop();
                    StopWatch = stopwatch;
                    End = DateTime.Now;
                    break;
                case ComparerEnum.LAMBDA:
                    Start = DateTime.Now;
                    stopwatch.Start();
                    var Lambda_result = _personList.Where(p => p.FirstName.Equals("Hans")).Select(p => p);
                    stopwatch.Stop();
                    StopWatch = stopwatch;
                    End = DateTime.Now;
                    break;
                case ComparerEnum.FOREACH:
                    Start = DateTime.Now;
                    stopwatch.Start();
                    IList<Person> Foreach_Result = new List<Person>();

                    foreach(var item in _personList) {
                        if(item.FirstName.Equals("Hans")) {
                            Foreach_Result.Add(item);
                        }
                    }
                    stopwatch.Stop();
                    StopWatch = stopwatch;
                    End = DateTime.Now;
                    break;
            }
        }
    }
}

Das ganze habe ich in einem WPF-GUI (noch nicht mit MVVM-Pattern umgesetzt, da ich dieses noch ein wenig zu abstrakt finde) gebastelt, der sehr simpel aussieht. Hier nur der Ausschnitt des Click Events für einen Button:

   1: private void Button_Click(object sender, RoutedEventArgs e) {
   2:             Button button = null;
   3:             PersonList personList = new PersonList(2000000);
   4:             PerformanceComparer comparerLinq = new PerformanceComparer(ComparerEnum.LINQ, personList);
   5:             PerformanceComparer comparerLambda = new PerformanceComparer(ComparerEnum.LAMBDA, personList);
   6:             PerformanceComparer comparerForeach = new PerformanceComparer(ComparerEnum.FOREACH, personList);
   7:  
   8:             if(sender != null) {
   9:                 button = (Button)sender;
  10:             }
  11:  
  12:             switch(button.Name) {
  13:                 case "LINQ_Button":
  14:                     LINQ_StartTime_TextBlock.Text = comparerLinq.Start.ToLongTimeString();
  15:                     LINQ_EndTime_TextBlock.Text = comparerLinq.End.ToLongTimeString();
  16:                     LINQ_ElapsedTime_TextBlock.Text = comparerLinq.StopWatch.Elapsed.Ticks.ToString();
  17:                     break;
  18:                 case "LAMBDA_Button":
  19:                     LAMBDA_StartTime_TextBlock.Text = comparerLambda.Start.ToLongTimeString();
  20:                     LAMBDA_EndTime_TextBlock.Text = comparerLambda.End.ToLongTimeString();
  21:                     LAMBDA_ElapsedTime_TextBlock.Text = comparerLambda.StopWatch.Elapsed.Ticks.ToString();
  22:                     break;
  23:                 case "Foreach_Button":
  24:                     Foreach_StartTime_TextBlock.Text = comparerForeach.Start.ToLongTimeString();
  25:                     Foreach_EndTime_TextBlock.Text = comparerForeach.End.ToLongTimeString();
  26:                     Foreach_ElapsedTime_TextBlock.Text = comparerForeach.StopWatch.Elapsed.Ticks.ToString();
  27:                     break;
  28:             }
  29:  
  30:         }

Das Biespielprojekt kann hier heruntergeladen werden. Für Fragen, Kritik und natürlich Verbesserungsvorschlägen bin ich sehr dankbar.

Kategorien:C#