Ver código fonte

Weitere Entities und entsprechende Entity-Maps hinzugefügt, sowie diverse Helper-Klassen und Service-Klassen erstellt

Arne Diekmann 8 anos atrás
pai
commit
91ebc9cdfe
38 arquivos alterados com 2398 adições e 60 exclusões
  1. 159 0
      GreenTree.Nachtragsmanagement.Core/CommonHelper.cs
  2. 91 0
      GreenTree.Nachtragsmanagement.Core/ComponentModel/GenericListTypeConverter.cs
  3. 28 0
      GreenTree.Nachtragsmanagement.Core/Configuration/AppendixConfigurationSection.cs
  4. 108 0
      GreenTree.Nachtragsmanagement.Core/Configuration/MailServerElement.cs
  5. 58 0
      GreenTree.Nachtragsmanagement.Core/Data/IRepository.cs
  6. 1 1
      GreenTree.Nachtragsmanagement.Core/Domain/Invoice/Invoice.cs
  7. 21 14
      GreenTree.Nachtragsmanagement.Core/Domain/Misc/MailNotification.cs
  8. 20 0
      GreenTree.Nachtragsmanagement.Core/Domain/User/User.cs
  9. 9 0
      GreenTree.Nachtragsmanagement.Core/GreenTree.Nachtragsmanagement.Core.csproj
  10. 88 0
      GreenTree.Nachtragsmanagement.Core/IWebHelper.cs
  11. 540 0
      GreenTree.Nachtragsmanagement.Core/WebHelper.cs
  12. 12 1
      GreenTree.Nachtragsmanagement.Data/AppendixObjectContext.cs
  13. 235 0
      GreenTree.Nachtragsmanagement.Data/EfRepository.cs
  14. 4 0
      GreenTree.Nachtragsmanagement.Data/GreenTree.Nachtragsmanagement.Data.csproj
  15. 1 2
      GreenTree.Nachtragsmanagement.Data/Mapping/Appendix/AppendixMap.cs
  16. 4 0
      GreenTree.Nachtragsmanagement.Data/Mapping/Invoice/InvoiceMap.cs
  17. 37 0
      GreenTree.Nachtragsmanagement.Data/Mapping/Misc/MailNotificationMap.cs
  18. 22 0
      GreenTree.Nachtragsmanagement.Data/Mapping/Misc/NotificationEventMap.cs
  19. 22 0
      GreenTree.Nachtragsmanagement.Data/Mapping/Misc/NotificationEventTypeMap.cs
  20. 5 0
      GreenTree.Nachtragsmanagement.Data/Mapping/User/UserMap.cs
  21. 161 0
      GreenTree.Nachtragsmanagement.Services/Appendix/AppendixService.cs
  22. 94 0
      GreenTree.Nachtragsmanagement.Services/Appendix/IAppendixService.cs
  23. 26 0
      GreenTree.Nachtragsmanagement.Services/Configuration/ConfigurationService.cs
  24. 17 0
      GreenTree.Nachtragsmanagement.Services/Configuration/IConfigurationService.cs
  25. 13 4
      GreenTree.Nachtragsmanagement.Services/GreenTree.Nachtragsmanagement.Services.csproj
  26. 21 0
      GreenTree.Nachtragsmanagement.Services/Notification/INotificationLogic.cs
  27. 15 0
      GreenTree.Nachtragsmanagement.Services/Notification/INotificationService.cs
  28. 55 0
      GreenTree.Nachtragsmanagement.Services/Site/ISiteService.cs
  29. 99 0
      GreenTree.Nachtragsmanagement.Services/Site/SiteService.cs
  30. 4 4
      GreenTree.Nachtragsmanagement.Services/Test/DbRelationService.cs
  31. 136 0
      GreenTree.Nachtragsmanagement.Services/User/IUserService.cs
  32. 225 0
      GreenTree.Nachtragsmanagement.Services/User/UserService.cs
  33. 31 29
      GreenTree.Nachtragsmanagement.Web.Framework/ApplicationContext.cs
  34. 7 1
      GreenTree.Nachtragsmanagement.Web.Framework/GreenTree.Nachtragsmanagement.Web.Framework.csproj
  35. 13 1
      GreenTree.Nachtragsmanagement.Web/Controllers/HomeController.cs
  36. 1 1
      GreenTree.Nachtragsmanagement.Web/Global.asax.cs
  37. 5 1
      GreenTree.Nachtragsmanagement.Web/GreenTree.Nachtragsmanagement.Web.csproj
  38. 10 1
      GreenTree.Nachtragsmanagement.Web/Web.config

+ 159 - 0
GreenTree.Nachtragsmanagement.Core/CommonHelper.cs

@@ -0,0 +1,159 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Globalization;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using System.Web;
+using GreenTree.Nachtragsmanagement.Core.ComponentModel;
+
+namespace GreenTree.Nachtragsmanagement.Core
+{
+    public class CommonHelper
+    {
+        private static AspNetHostingPermissionLevel? _trustLevel;
+        /// <summary>
+        /// Finds the trust level of the running application (http://blogs.msdn.com/dmitryr/archive/2007/01/23/finding-out-the-current-trust-level-in-asp-net.aspx)
+        /// </summary>
+        /// <returns>The current trust level.</returns>
+        public static AspNetHostingPermissionLevel GetTrustLevel()
+        {
+            if (!_trustLevel.HasValue)
+            {
+                //set minimum
+                _trustLevel = AspNetHostingPermissionLevel.None;
+
+                //determine maximum
+                foreach (AspNetHostingPermissionLevel trustLevel in new[] {
+                                AspNetHostingPermissionLevel.Unrestricted,
+                                AspNetHostingPermissionLevel.High,
+                                AspNetHostingPermissionLevel.Medium,
+                                AspNetHostingPermissionLevel.Low,
+                                AspNetHostingPermissionLevel.Minimal
+                            })
+                {
+                    try
+                    {
+                        new AspNetHostingPermission(trustLevel).Demand();
+                        _trustLevel = trustLevel;
+                        break; //we've set the highest permission we can
+                    }
+                    catch (System.Security.SecurityException)
+                    {
+                        continue;
+                    }
+                }
+            }
+            return _trustLevel.Value;
+        }
+
+        /// <summary>
+        /// Sets a property on an object to a valuae.
+        /// </summary>
+        /// <param name="instance">The object whose property to set.</param>
+        /// <param name="propertyName">The name of the property to set.</param>
+        /// <param name="value">The value to set the property to.</param>
+        public static void SetProperty(object instance, string propertyName, object value)
+        {
+            if (instance == null) throw new ArgumentNullException("instance");
+            if (propertyName == null) throw new ArgumentNullException("propertyName");
+
+            Type instanceType = instance.GetType();
+            PropertyInfo pi = instanceType.GetProperty(propertyName);
+            if (pi == null)
+                throw new Exception(
+                    String.Format("No property '{0}' found on the instance of type '{1}'.", propertyName, instanceType));
+            if (!pi.CanWrite)
+                throw new Exception(
+                    String.Format("The property '{0}' on the instance of type '{1}' does not have a setter.", propertyName, instanceType));
+            if (value != null && !value.GetType().IsAssignableFrom(pi.PropertyType))
+                value = To(value, pi.PropertyType);
+            pi.SetValue(instance, value, new object[0]);
+        }
+
+        public static TypeConverter GetNopCustomTypeConverter(Type type)
+        {
+            //we can't use the following code in order to register our custom type descriptors
+            //TypeDescriptor.AddAttributes(typeof(List<int>), new TypeConverterAttribute(typeof(GenericListTypeConverter<int>)));
+            //so we do it manually here
+
+            if (type == typeof(List<int>))
+                return new GenericListTypeConverter<int>();
+            if (type == typeof(List<decimal>))
+                return new GenericListTypeConverter<decimal>();
+            if (type == typeof(List<string>))
+                return new GenericListTypeConverter<string>();
+
+            return TypeDescriptor.GetConverter(type);
+        }
+
+        /// <summary>
+        /// Converts a value to a destination type.
+        /// </summary>
+        /// <param name="value">The value to convert.</param>
+        /// <param name="destinationType">The type to convert the value to.</param>
+        /// <returns>The converted value.</returns>
+        public static object To(object value, Type destinationType)
+        {
+            return To(value, destinationType, CultureInfo.InvariantCulture);
+        }
+
+        /// <summary>
+        /// Converts a value to a destination type.
+        /// </summary>
+        /// <param name="value">The value to convert.</param>
+        /// <param name="destinationType">The type to convert the value to.</param>
+        /// <param name="culture">Culture</param>
+        /// <returns>The converted value.</returns>
+        public static object To(object value, Type destinationType, CultureInfo culture)
+        {
+            if (value != null)
+            {
+                var sourceType = value.GetType();
+
+                TypeConverter destinationConverter = GetNopCustomTypeConverter(destinationType);
+                TypeConverter sourceConverter = GetNopCustomTypeConverter(sourceType);
+                if (destinationConverter != null && destinationConverter.CanConvertFrom(value.GetType()))
+                    return destinationConverter.ConvertFrom(null, culture, value);
+                if (sourceConverter != null && sourceConverter.CanConvertTo(destinationType))
+                    return sourceConverter.ConvertTo(null, culture, value, destinationType);
+                if (destinationType.IsEnum && value is int)
+                    return Enum.ToObject(destinationType, (int)value);
+                if (!destinationType.IsInstanceOfType(value))
+                    return Convert.ChangeType(value, destinationType, culture);
+            }
+            return value;
+        }
+
+        /// <summary>
+        /// Converts a value to a destination type.
+        /// </summary>
+        /// <param name="value">The value to convert.</param>
+        /// <typeparam name="T">The type to convert the value to.</typeparam>
+        /// <returns>The converted value.</returns>
+        public static T To<T>(object value)
+        {
+            //return (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture);
+            return (T)To(value, typeof(T));
+        }
+
+        /// <summary>
+        /// Convert enum for front-end
+        /// </summary>
+        /// <param name="str">Input string</param>
+        /// <returns>Converted string</returns>
+        public static string ConvertEnum(string str)
+        {
+            string result = string.Empty;
+            char[] letters = str.ToCharArray();
+            foreach (char c in letters)
+                if (c.ToString() != c.ToString().ToLower())
+                    result += " " + c.ToString();
+                else
+                    result += c.ToString();
+            return result;
+        }
+    }
+}

+ 91 - 0
GreenTree.Nachtragsmanagement.Core/ComponentModel/GenericListTypeConverter.cs

@@ -0,0 +1,91 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GreenTree.Nachtragsmanagement.Core.ComponentModel
+{
+    public class GenericListTypeConverter<T> : TypeConverter
+    {
+        protected readonly TypeConverter typeConverter;
+
+        public GenericListTypeConverter()
+        {
+            typeConverter = TypeDescriptor.GetConverter(typeof(T));
+            if (typeConverter == null)
+                throw new InvalidOperationException("No type converter exists for type " + typeof(T).FullName);
+        }
+
+        protected virtual string[] GetStringArray(string input)
+        {
+            if (!String.IsNullOrEmpty(input))
+            {
+                var result = input
+                    .Split(',')
+                    .Select(x => x.Trim())
+                    .ToArray();
+                return result;
+            }
+
+            return new string[0];
+        }
+
+        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
+        {
+
+            if (sourceType == typeof(string))
+            {
+                string[] items = GetStringArray(sourceType.ToString());
+                return items.Any();
+            }
+
+            return base.CanConvertFrom(context, sourceType);
+        }
+
+        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
+        {
+            if (value is string)
+            {
+                string[] items = GetStringArray((string)value);
+                var result = new List<T>();
+                Array.ForEach(items, s =>
+                {
+                    object item = typeConverter.ConvertFromInvariantString(s);
+                    if (item != null)
+                    {
+                        result.Add((T)item);
+                    }
+                });
+
+                return result;
+            }
+            return base.ConvertFrom(context, culture, value);
+        }
+
+        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
+        {
+            if (destinationType == typeof(string))
+            {
+                string result = string.Empty;
+                if (value != null)
+                {
+                    //we don't use string.Join() because it doesn't support invariant culture
+                    for (int i = 0; i < ((IList<T>)value).Count; i++)
+                    {
+                        var str1 = Convert.ToString(((IList<T>)value)[i], CultureInfo.InvariantCulture);
+                        result += str1;
+                        //don't add comma after the last element
+                        if (i != ((IList<T>)value).Count - 1)
+                            result += ",";
+                    }
+                }
+                return result;
+            }
+
+            return base.ConvertTo(context, culture, value, destinationType);
+        }
+    }
+}

+ 28 - 0
GreenTree.Nachtragsmanagement.Core/Configuration/AppendixConfigurationSection.cs

@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GreenTree.Nachtragsmanagement.Core.Configuration
+{
+    public class AppendixConfigurationSection : ConfigurationSection
+    {
+        /// <summary>
+        /// Settings for mailserver handling
+        /// </summary>
+        [ConfigurationProperty("mailServer")]
+        public MailServerElement MailServerElement
+        {
+            get
+            {
+                return (MailServerElement)this["mailServer"];
+            }
+            set
+            {
+                this["mailserver"] = value;
+            }
+        }
+    }
+}

+ 108 - 0
GreenTree.Nachtragsmanagement.Core/Configuration/MailServerElement.cs

@@ -0,0 +1,108 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GreenTree.Nachtragsmanagement.Core.Configuration
+{
+    public class MailServerElement : ConfigurationElement
+    {
+        /// <summary>
+        /// SMTP server which sends out the mails
+        /// </summary>
+        [ConfigurationProperty("smtpServer", DefaultValue = "localhost", IsRequired = true)]
+        public string SmtpServer
+        {
+            get
+            {
+                return (string)this["smtpServer"];
+            }
+            set
+            {
+                this["smtpServer"] = value;
+            }
+        }
+
+        /// <summary>
+        /// Server port where SMTP is available
+        /// </summary>
+        [ConfigurationProperty("port", DefaultValue = 25, IsRequired = true)]
+        public int Port
+        {
+            get
+            {
+                return (int)this["port"];
+            }
+            set
+            {
+                this["port"] = value;
+            }
+        }
+
+        /// <summary>
+        /// Username which will be authenticated against SMTP server
+        /// </summary>
+        [ConfigurationProperty("username", DefaultValue = "", IsRequired = false)]
+        public string Username
+        {
+            get
+            {
+                return (string)this["username"];
+            }
+            set
+            {
+                this["username"] = value;
+            }
+        }
+
+        /// <summary>
+        /// Domain in which the users exists
+        /// </summary>
+        [ConfigurationProperty("domain", DefaultValue = "", IsRequired = false)]
+        public string Domain
+        {
+            get
+            {
+                return (string)this["domain"];
+            }
+            set
+            {
+                this["domain"] = value;
+            }
+        }
+
+        /// <summary>
+        /// Password of the authenticated user
+        /// </summary>
+        [ConfigurationProperty("password", DefaultValue = "", IsRequired = false)]
+        public string Password
+        {
+            get
+            {
+                return (string)this["password"];
+            }
+            set
+            {
+                this["password"] = value;
+            }
+        }
+
+        /// <summary>
+        /// Determines if the connection should use SSL/TLS
+        /// </summary>
+        [ConfigurationProperty("useSsl", DefaultValue = false, IsRequired = false)]
+        public bool UseSsl
+        {
+            get
+            {
+                return (bool)this["useSsl"];
+            }
+            set
+            {
+                this["useSsl"] = value;
+            }
+        }
+    }
+}

+ 58 - 0
GreenTree.Nachtragsmanagement.Core/Data/IRepository.cs

@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GreenTree.Nachtragsmanagement.Core.Data
+{
+    public interface IRepository<T> where T : BaseEntity
+    {
+        /// <summary>
+        /// Get entity by identifier
+        /// </summary>
+        /// <param name="id">Identifier</param>
+        /// <returns>Entity</returns>
+        T GetById(object id);
+
+        /// <summary>
+        /// Insert entity
+        /// </summary>
+        /// <param name="entity">Entity</param>
+        void Insert(T entity);
+
+        /// <summary>
+        /// Insert entities
+        /// </summary>
+        /// <param name="entities">Entities</param>
+        void Insert(IEnumerable<T> entities);
+
+        /// <summary>
+        /// Update entity
+        /// </summary>
+        /// <param name="entity">Entity</param>
+        void Update(T entity);
+
+        /// <summary>
+        /// Delete entity
+        /// </summary>
+        /// <param name="entity">Entity</param>
+        void Delete(T entity);
+
+        /// <summary>
+        /// Delete entities
+        /// </summary>
+        /// <param name="entities">Entities</param>
+        void Delete(IEnumerable<T> entities);
+
+        /// <summary>
+        /// Gets a table
+        /// </summary>
+        IQueryable<T> Table { get; }
+
+        /// <summary>
+        /// Gets a table with "no tracking" enabled (EF feature) Use it only when you load record(s) only for read-only operations
+        /// </summary>
+        IQueryable<T> TableNoTracking { get; }
+    }
+}

+ 1 - 1
GreenTree.Nachtragsmanagement.Core/Domain/Invoice/Invoice.cs

@@ -26,7 +26,7 @@ namespace GreenTree.Nachtragsmanagement.Core.Domain.Invoice
         /// <summary>
         /// Id of the related appendix
         /// </summary>
-        public int AppendixId { get; set; }
+        public int? AppendixId { get; set; }
 
         /// <summary>
         /// Appendix related to the invoice

+ 21 - 14
GreenTree.Nachtragsmanagement.Core/Domain/Misc/MailNotification.cs

@@ -10,29 +10,22 @@ namespace GreenTree.Nachtragsmanagement.Core.Domain.Misc
     {
         #region Fields
 
-
+        /// <summary>
+        /// Invoices related to the appendix
+        /// </summary>
+        private ICollection<User.User> _users;
 
         #endregion
 
         /// <summary>
         /// Id of the corresponding NotificationEventType
         /// </summary>
-        public int NotificationEventTypeId { get; set; }
+        public int? NotificationEventTypeId { get; set; }
 
         /// <summary>
         /// Corresponding NotificationEventType
         /// </summary>
-        public NotificationEventType EventType { get; set; }
-
-        /// <summary>
-        /// Id of the corresponding User
-        /// </summary>
-        public int UserId { get; set; }
-
-        /// <summary>
-        /// Corresponding User
-        /// </summary>
-        public User.User User { get; set; }
+        public NotificationEventType NotificationEventType { get; set; }
 
         /// <summary>
         /// The cron expression if the notification will be sent by time
@@ -42,13 +35,18 @@ namespace GreenTree.Nachtragsmanagement.Core.Domain.Misc
         /// <summary>
         /// Id of the corresponding NotificationEvent
         /// </summary>
-        public int NotificationEventId { get; set; }
+        public int? NotificationEventId { get; set; }
 
         /// <summary>
         /// Corresponding NotificationEvent
         /// </summary>
         public NotificationEvent NotificationEvent { get; set; }
 
+        /// <summary>
+        /// The logic by which the notification is generated
+        /// </summary>
+        public string NotificationLogicType { get; set; }
+
         /// <summary>
         /// Determines if data is daily summarized before notificated
         /// </summary>
@@ -63,5 +61,14 @@ namespace GreenTree.Nachtragsmanagement.Core.Domain.Misc
         /// Determines if data is monthly summarized before notificated
         /// </summary>
         public bool IsMonthlySummary { get; set; }
+
+        /// <summary>
+        /// Users assigned to the notification
+        /// </summary>
+        public virtual ICollection<User.User> Users
+        {
+            get { return _users ?? ( _users = new List<User.User>()); }
+            protected set { _users = value; }
+        }
     }
 }

+ 20 - 0
GreenTree.Nachtragsmanagement.Core/Domain/User/User.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
+using GreenTree.Nachtragsmanagement.Core.Domain.Misc;
 
 namespace GreenTree.Nachtragsmanagement.Core.Domain.User
 {
@@ -15,6 +16,11 @@ namespace GreenTree.Nachtragsmanagement.Core.Domain.User
         /// </summary>
         private ICollection<Role> _roles;
 
+        /// <summary>
+        /// Notificationlist
+        /// </summary>
+        private ICollection<MailNotification> _mailNotifications;
+
         #endregion
 
         /// <summary>
@@ -37,6 +43,11 @@ namespace GreenTree.Nachtragsmanagement.Core.Domain.User
         /// </summary>
         public string MailAddress { get; set; }
 
+        /// <summary>
+        /// Password
+        /// </summary>
+        public string Password { get; set; }
+
         /// <summary>
         /// Roles the user have
         /// </summary>
@@ -45,5 +56,14 @@ namespace GreenTree.Nachtragsmanagement.Core.Domain.User
             get { return _roles ?? (_roles = new List<Role>()); }
             protected set { _roles = value; }
         }
+
+        /// <summary>
+        /// MailNotification related to the user
+        /// </summary>
+        public virtual ICollection<MailNotification> MailNotifications
+        {
+            get { return _mailNotifications ?? ( _mailNotifications = new List<MailNotification>()); }
+            protected set { _mailNotifications = value; }
+        }
     }
 }

+ 9 - 0
GreenTree.Nachtragsmanagement.Core/GreenTree.Nachtragsmanagement.Core.csproj

@@ -31,8 +31,10 @@
   </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
+    <Reference Include="System.configuration" />
     <Reference Include="System.Core" />
     <Reference Include="System.Data.Entity" />
+    <Reference Include="System.Web" />
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
     <Reference Include="Microsoft.CSharp" />
@@ -42,6 +44,11 @@
   </ItemGroup>
   <ItemGroup>
     <Compile Include="BaseEntity.cs" />
+    <Compile Include="CommonHelper.cs" />
+    <Compile Include="ComponentModel\GenericListTypeConverter.cs" />
+    <Compile Include="Configuration\AppendixConfigurationSection.cs" />
+    <Compile Include="Configuration\MailServerElement.cs" />
+    <Compile Include="Data\IRepository.cs" />
     <Compile Include="Domain\Appendix\Appendix.cs" />
     <Compile Include="Domain\Appendix\Category.cs" />
     <Compile Include="Domain\Deviation\Deviation.cs" />
@@ -56,7 +63,9 @@
     <Compile Include="Domain\User\Function.cs" />
     <Compile Include="Domain\User\Role.cs" />
     <Compile Include="Domain\User\User.cs" />
+    <Compile Include="IWebHelper.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="WebHelper.cs" />
   </ItemGroup>
   <ItemGroup />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

+ 88 - 0
GreenTree.Nachtragsmanagement.Core/IWebHelper.cs

@@ -0,0 +1,88 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GreenTree.Nachtragsmanagement.Core
+{
+    /// <summary>
+    /// Represents a common helper
+    /// </summary>
+    public partial interface IWebHelper
+    {
+        /// <summary>
+        /// Get URL referrer
+        /// </summary>
+        /// <returns>URL referrer</returns>
+        string GetUrlReferrer();
+
+        /// <summary>
+        /// Get context IP address
+        /// </summary>
+        /// <returns>URL referrer</returns>
+        string GetCurrentIpAddress();
+
+        /// <summary>
+        /// Gets a value indicating whether current connection is secured
+        /// </summary>
+        /// <returns>true - secured, false - not secured</returns>
+        bool IsCurrentConnectionSecured();
+
+        /// <summary>
+        /// Gets server variable by name
+        /// </summary>
+        /// <param name="name">Name</param>
+        /// <returns>Server variable</returns>
+        string ServerVariables(string name);
+
+        /// <summary>
+        /// Maps a virtual path to a physical disk path.
+        /// </summary>
+        /// <param name="path">The path to map. E.g. "~/bin"</param>
+        /// <returns>The physical path. E.g. "c:\inetpub\wwwroot\bin"</returns>
+        string MapPath(string path);
+
+        /// <summary>
+        /// Modifies query string
+        /// </summary>
+        /// <param name="url">Url to modify</param>
+        /// <param name="queryStringModification">Query string modification</param>
+        /// <param name="anchor">Anchor</param>
+        /// <returns>New url</returns>
+        string ModifyQueryString(string url, string queryStringModification, string anchor);
+
+        /// <summary>
+        /// Remove query string from url
+        /// </summary>
+        /// <param name="url">Url to modify</param>
+        /// <param name="queryString">Query string to remove</param>
+        /// <returns>New url</returns>
+        string RemoveQueryString(string url, string queryString);
+
+        /// <summary>
+        /// Gets query string value by name
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="name">Parameter name</param>
+        /// <returns>Query string value</returns>
+        T QueryString<T>(string name);
+
+        /// <summary>
+        /// Restart application domain
+        /// </summary>
+        /// <param name="makeRedirect">A value indicating whether we should made redirection after restart</param>
+        /// <param name="redirectUrl">Redirect URL; empty string if you want to redirect to the current page URL</param>
+        void RestartAppDomain(bool makeRedirect = false, string redirectUrl = "");
+
+        /// <summary>
+        /// Gets a value that indicates whether the client is being redirected to a new location
+        /// </summary>
+        bool IsRequestBeingRedirected { get; }
+
+        /// <summary>
+        /// Gets or sets a value that indicates whether the client is being redirected to a new location using POST
+        /// </summary>
+        bool IsPostBeingDone { get; set; }
+    }
+}

+ 540 - 0
GreenTree.Nachtragsmanagement.Core/WebHelper.cs

@@ -0,0 +1,540 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Web;
+using System.Web.Hosting;
+
+namespace GreenTree.Nachtragsmanagement.Core
+{
+    public class WebHelper
+    {
+        #region Fields 
+
+        private readonly HttpContextBase _httpContext;
+
+        #endregion
+
+        #region Utilities
+
+        protected virtual Boolean IsRequestAvailable(HttpContextBase httpContext)
+        {
+            if (httpContext == null)
+                return false;
+
+            try
+            {
+                if (httpContext.Request == null)
+                    return false;
+            }
+            catch (HttpException)
+            {
+                return false;
+            }
+
+            return true;
+        }
+        protected virtual bool TryWriteWebConfig()
+        {
+            try
+            {
+                // In medium trust, "UnloadAppDomain" is not supported. Touch web.config
+                // to force an AppDomain restart.
+                File.SetLastWriteTimeUtc(MapPath("~/web.config"), DateTime.UtcNow);
+                return true;
+            }
+            catch
+            {
+                return false;
+            }
+        }
+
+        protected virtual bool TryWriteGlobalAsax()
+        {
+            try
+            {
+                //When a new plugin is dropped in the Plugins folder and is installed into nopCommerce, 
+                //even if the plugin has registered routes for its controllers, 
+                //these routes will not be working as the MVC framework couldn't 
+                //find the new controller types and couldn't instantiate the requested controller. 
+                //That's why you get these nasty errors 
+                //i.e "Controller does not implement IController".
+                //The issue is described here: http://www.nopcommerce.com/boards/t/10969/nop-20-plugin.aspx?p=4#51318
+                //The solution is to touch global.asax file
+                File.SetLastWriteTimeUtc(MapPath("~/global.asax"), DateTime.UtcNow);
+                return true;
+            }
+            catch
+            {
+                return false;
+            }
+        }
+
+        #endregion
+
+        #region Methods
+
+        /// <summary>
+        /// Ctor
+        /// </summary>
+        /// <param name="httpContext">HTTP context</param>
+        public WebHelper(HttpContextBase httpContext)
+        {
+            _httpContext = httpContext;
+        }
+
+        /// <summary>
+        /// Get URL referrer
+        /// </summary>
+        /// <returns>URL referrer</returns>
+        public virtual string GetUrlReferrer()
+        {
+            string referrerUrl = string.Empty;
+
+            //URL referrer is null in some case (for example, in IE 8)
+            if (IsRequestAvailable(_httpContext) && _httpContext.Request.UrlReferrer != null)
+                referrerUrl = _httpContext.Request.UrlReferrer.PathAndQuery;
+
+            return referrerUrl;
+        }
+
+        /// <summary>
+        /// Get context IP address
+        /// </summary>
+        /// <returns>URL referrer</returns>
+        public virtual string GetCurrentIpAddress()
+        {
+            if (!IsRequestAvailable(_httpContext))
+                return string.Empty;
+
+            var result = "";
+            if (_httpContext.Request.Headers != null)
+            {
+                //The X-Forwarded-For (XFF) HTTP header field is a de facto standard
+                //for identifying the originating IP address of a client
+                //connecting to a web server through an HTTP proxy or load balancer.
+                var forwardedHttpHeader = "X-FORWARDED-FOR";
+                if (!String.IsNullOrEmpty(ConfigurationManager.AppSettings["ForwardedHTTPheader"]))
+                {
+                    //but in some cases server use other HTTP header
+                    //in these cases an administrator can specify a custom Forwarded HTTP header
+                    forwardedHttpHeader = ConfigurationManager.AppSettings["ForwardedHTTPheader"];
+                }
+
+                //it's used for identifying the originating IP address of a client connecting to a web server
+                //through an HTTP proxy or load balancer. 
+                string xff = _httpContext.Request.Headers.AllKeys
+                    .Where(x => forwardedHttpHeader.Equals(x, StringComparison.InvariantCultureIgnoreCase))
+                    .Select(k => _httpContext.Request.Headers[k])
+                    .FirstOrDefault();
+
+                //if you want to exclude private IP addresses, then see http://stackoverflow.com/questions/2577496/how-can-i-get-the-clients-ip-address-in-asp-net-mvc
+                if (!String.IsNullOrEmpty(xff))
+                {
+                    string lastIp = xff.Split(new[] { ',' }).FirstOrDefault();
+                    result = lastIp;
+                }
+            }
+
+            if (String.IsNullOrEmpty(result) && _httpContext.Request.UserHostAddress != null)
+            {
+                result = _httpContext.Request.UserHostAddress;
+            }
+
+            //some validation
+            if (result == "::1")
+                result = "127.0.0.1";
+            //remove port
+            if (!String.IsNullOrEmpty(result))
+            {
+                int index = result.IndexOf(":", StringComparison.InvariantCultureIgnoreCase);
+                if (index > 0)
+                    result = result.Substring(0, index);
+            }
+            return result;
+
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether current connection is secured
+        /// </summary>
+        /// <returns>true - secured, false - not secured</returns>
+        public virtual bool IsCurrentConnectionSecured()
+        {
+            bool useSsl = false;
+            if (IsRequestAvailable(_httpContext))
+            {
+                useSsl = _httpContext.Request.IsSecureConnection;
+                //when your hosting uses a load balancer on their server then the Request.IsSecureConnection is never got set to true, use the statement below
+                //just uncomment it
+                //useSSL = _httpContext.Request.ServerVariables["HTTP_CLUSTER_HTTPS"] == "on" ? true : false;
+            }
+
+            return useSsl;
+        }
+
+        /// <summary>
+        /// Gets server variable by name
+        /// </summary>
+        /// <param name="name">Name</param>
+        /// <returns>Server variable</returns>
+        public virtual string ServerVariables(string name)
+        {
+            string result = string.Empty;
+
+            try
+            {
+                if (!IsRequestAvailable(_httpContext))
+                    return result;
+
+                //put this method is try-catch 
+                //as described here http://www.nopcommerce.com/boards/t/21356/multi-store-roadmap-lets-discuss-update-done.aspx?p=6#90196
+                if (_httpContext.Request.ServerVariables[name] != null)
+                {
+                    result = _httpContext.Request.ServerVariables[name];
+                }
+            }
+            catch
+            {
+                result = string.Empty;
+            }
+            return result;
+        }
+
+        /// <summary>
+        /// Returns true if the requested resource is one of the typical resources that needn't be processed by the cms engine.
+        /// </summary>
+        /// <param name="request">HTTP Request</param>
+        /// <returns>True if the request targets a static resource file.</returns>
+        /// <remarks>
+        /// These are the file extensions considered to be static resources:
+        /// .css
+        ///	.gif
+        /// .png 
+        /// .jpg
+        /// .jpeg
+        /// .js
+        /// .axd
+        /// .ashx
+        /// </remarks>
+        public virtual bool IsStaticResource(HttpRequest request)
+        {
+            if (request == null)
+                throw new ArgumentNullException("request");
+
+            string path = request.Path;
+            string extension = VirtualPathUtility.GetExtension(path);
+
+            if (extension == null) return false;
+
+            switch (extension.ToLower())
+            {
+                case ".axd":
+                case ".ashx":
+                case ".bmp":
+                case ".css":
+                case ".gif":
+                case ".htm":
+                case ".html":
+                case ".ico":
+                case ".jpeg":
+                case ".jpg":
+                case ".js":
+                case ".png":
+                case ".rar":
+                case ".zip":
+                    return true;
+            }
+
+            return false;
+        }
+
+        /// <summary>
+        /// Maps a virtual path to a physical disk path.
+        /// </summary>
+        /// <param name="path">The path to map. E.g. "~/bin"</param>
+        /// <returns>The physical path. E.g. "c:\inetpub\wwwroot\bin"</returns>
+        public virtual string MapPath(string path)
+        {
+            if (HostingEnvironment.IsHosted)
+            {
+                //hosted
+                return HostingEnvironment.MapPath(path);
+            }
+
+            //not hosted. For example, run in unit tests
+            string baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
+            path = path.Replace("~/", "").TrimStart('/').Replace('/', '\\');
+            return Path.Combine(baseDirectory, path);
+        }
+
+        /// <summary>
+        /// Modifies query string
+        /// </summary>
+        /// <param name="url">Url to modify</param>
+        /// <param name="queryStringModification">Query string modification</param>
+        /// <param name="anchor">Anchor</param>
+        /// <returns>New url</returns>
+        public virtual string ModifyQueryString(string url, string queryStringModification, string anchor)
+        {
+            if (url == null)
+                url = string.Empty;
+            url = url.ToLowerInvariant();
+
+            if (queryStringModification == null)
+                queryStringModification = string.Empty;
+            queryStringModification = queryStringModification.ToLowerInvariant();
+
+            if (anchor == null)
+                anchor = string.Empty;
+            anchor = anchor.ToLowerInvariant();
+
+
+            string str = string.Empty;
+            string str2 = string.Empty;
+            if (url.Contains("#"))
+            {
+                str2 = url.Substring(url.IndexOf("#") + 1);
+                url = url.Substring(0, url.IndexOf("#"));
+            }
+            if (url.Contains("?"))
+            {
+                str = url.Substring(url.IndexOf("?") + 1);
+                url = url.Substring(0, url.IndexOf("?"));
+            }
+            if (!string.IsNullOrEmpty(queryStringModification))
+            {
+                if (!string.IsNullOrEmpty(str))
+                {
+                    var dictionary = new Dictionary<string, string>();
+                    foreach (string str3 in str.Split(new[] { '&' }))
+                    {
+                        if (!string.IsNullOrEmpty(str3))
+                        {
+                            string[] strArray = str3.Split(new[] { '=' });
+                            if (strArray.Length == 2)
+                            {
+                                if (!dictionary.ContainsKey(strArray[0]))
+                                {
+                                    //do not add value if it already exists
+                                    //two the same query parameters? theoretically it's not possible.
+                                    //but MVC has some ugly implementation for checkboxes and we can have two values
+                                    //find more info here: http://www.mindstorminteractive.com/topics/jquery-fix-asp-net-mvc-checkbox-truefalse-value/
+                                    //we do this validation just to ensure that the first one is not overridden
+                                    dictionary[strArray[0]] = strArray[1];
+                                }
+                            }
+                            else
+                            {
+                                dictionary[str3] = null;
+                            }
+                        }
+                    }
+                    foreach (string str4 in queryStringModification.Split(new[] { '&' }))
+                    {
+                        if (!string.IsNullOrEmpty(str4))
+                        {
+                            string[] strArray2 = str4.Split(new[] { '=' });
+                            if (strArray2.Length == 2)
+                            {
+                                dictionary[strArray2[0]] = strArray2[1];
+                            }
+                            else
+                            {
+                                dictionary[str4] = null;
+                            }
+                        }
+                    }
+                    var builder = new StringBuilder();
+                    foreach (string str5 in dictionary.Keys)
+                    {
+                        if (builder.Length > 0)
+                        {
+                            builder.Append("&");
+                        }
+                        builder.Append(str5);
+                        if (dictionary[str5] != null)
+                        {
+                            builder.Append("=");
+                            builder.Append(dictionary[str5]);
+                        }
+                    }
+                    str = builder.ToString();
+                }
+                else
+                {
+                    str = queryStringModification;
+                }
+            }
+            if (!string.IsNullOrEmpty(anchor))
+            {
+                str2 = anchor;
+            }
+            return (url + (string.IsNullOrEmpty(str) ? "" : ("?" + str)) + (string.IsNullOrEmpty(str2) ? "" : ("#" + str2))).ToLowerInvariant();
+        }
+
+        /// <summary>
+        /// Remove query string from url
+        /// </summary>
+        /// <param name="url">Url to modify</param>
+        /// <param name="queryString">Query string to remove</param>
+        /// <returns>New url</returns>
+        public virtual string RemoveQueryString(string url, string queryString)
+        {
+            if (url == null)
+                url = string.Empty;
+            url = url.ToLowerInvariant();
+
+            if (queryString == null)
+                queryString = string.Empty;
+            queryString = queryString.ToLowerInvariant();
+
+
+            string str = string.Empty;
+            if (url.Contains("?"))
+            {
+                str = url.Substring(url.IndexOf("?") + 1);
+                url = url.Substring(0, url.IndexOf("?"));
+            }
+            if (!string.IsNullOrEmpty(queryString))
+            {
+                if (!string.IsNullOrEmpty(str))
+                {
+                    var dictionary = new Dictionary<string, string>();
+                    foreach (string str3 in str.Split(new[] { '&' }))
+                    {
+                        if (!string.IsNullOrEmpty(str3))
+                        {
+                            string[] strArray = str3.Split(new[] { '=' });
+                            if (strArray.Length == 2)
+                            {
+                                dictionary[strArray[0]] = strArray[1];
+                            }
+                            else
+                            {
+                                dictionary[str3] = null;
+                            }
+                        }
+                    }
+                    dictionary.Remove(queryString);
+
+                    var builder = new StringBuilder();
+                    foreach (string str5 in dictionary.Keys)
+                    {
+                        if (builder.Length > 0)
+                        {
+                            builder.Append("&");
+                        }
+                        builder.Append(str5);
+                        if (dictionary[str5] != null)
+                        {
+                            builder.Append("=");
+                            builder.Append(dictionary[str5]);
+                        }
+                    }
+                    str = builder.ToString();
+                }
+            }
+            return (url + (string.IsNullOrEmpty(str) ? "" : ("?" + str)));
+        }
+
+        /// <summary>
+        /// Gets query string value by name
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="name">Parameter name</param>
+        /// <returns>Query string value</returns>
+        public virtual T QueryString<T>(string name)
+        {
+            string queryParam = null;
+            if (IsRequestAvailable(_httpContext) && _httpContext.Request.QueryString[name] != null)
+                queryParam = _httpContext.Request.QueryString[name];
+
+            if (!String.IsNullOrEmpty(queryParam))
+                return CommonHelper.To<T>(queryParam);
+
+            return default(T);
+        }
+
+        /// <summary>
+        /// Restart application domain
+        /// </summary>
+        /// <param name="makeRedirect">A value indicating whether we should made redirection after restart</param>
+        /// <param name="redirectUrl">Redirect URL; empty string if you want to redirect to the current page URL</param>
+        public virtual void RestartAppDomain(bool makeRedirect = false, string redirectUrl = "")
+        {
+            if (CommonHelper.GetTrustLevel() > AspNetHostingPermissionLevel.Medium)
+            {
+                //full trust
+                HttpRuntime.UnloadAppDomain();
+
+                TryWriteGlobalAsax();
+            }
+            else
+            {
+                //medium trust
+                bool success = TryWriteWebConfig();
+                if (!success)
+                {
+                    throw new Exception("The application needs to be restarted due to a configuration change, but was unable to do so." + Environment.NewLine +
+                        "To prevent this issue in the future, a change to the web server configuration is required:" + Environment.NewLine +
+                        "- run the application in a full trust environment, or" + Environment.NewLine +
+                        "- give the application write access to the 'web.config' file.");
+                }
+
+                success = TryWriteGlobalAsax();
+                if (!success)
+                {
+                    throw new Exception("The application needs to be restarted due to a configuration change, but was unable to do so." + Environment.NewLine +
+                        "To prevent this issue in the future, a change to the web server configuration is required:" + Environment.NewLine +
+                        "- run the application in a full trust environment, or" + Environment.NewLine +
+                        "- give the application write access to the 'Global.asax' file.");
+                }
+            }
+
+            // If setting up extensions/modules requires an AppDomain restart, it's very unlikely the
+            // current request can be processed correctly.  So, we redirect to the same URL, so that the
+            // new request will come to the newly started AppDomain.
+            if (_httpContext != null && makeRedirect)
+            {
+                if (String.IsNullOrEmpty(redirectUrl))
+                    redirectUrl = "~/";
+                _httpContext.Response.Redirect(redirectUrl, true /*endResponse*/);
+            }
+        }
+
+        /// <summary>
+        /// Gets a value that indicates whether the client is being redirected to a new location
+        /// </summary>
+        public virtual bool IsRequestBeingRedirected
+        {
+            get
+            {
+                var response = _httpContext.Response;
+                return response.IsRequestBeingRedirected;
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets a value that indicates whether the client is being redirected to a new location using POST
+        /// </summary>
+        public virtual bool IsPostBeingDone
+        {
+            get
+            {
+                if (_httpContext.Items["nop.IsPOSTBeingDone"] == null)
+                    return false;
+                return Convert.ToBoolean(_httpContext.Items["nop.IsPOSTBeingDone"]);
+            }
+            set
+            {
+                _httpContext.Items["nop.IsPOSTBeingDone"] = value;
+            }
+        }
+
+        #endregion
+    }
+}

+ 12 - 1
GreenTree.Nachtragsmanagement.Data/AppendixObjectContext.cs

@@ -11,10 +11,12 @@ using GreenTree.Nachtragsmanagement.Core;
 using GreenTree.Nachtragsmanagement.Core.Domain.Appendix;
 using GreenTree.Nachtragsmanagement.Core.Domain.Deviation;
 using GreenTree.Nachtragsmanagement.Core.Domain.Invoice;
+using GreenTree.Nachtragsmanagement.Core.Domain.Misc;
 using GreenTree.Nachtragsmanagement.Core.Domain.Site;
 using GreenTree.Nachtragsmanagement.Core.Domain.User;
 using GreenTree.Nachtragsmanagement.Data.Mapping.Appendix;
 using GreenTree.Nachtragsmanagement.Data.Mapping.Deviation;
+using GreenTree.Nachtragsmanagement.Data.Mapping.Misc;
 using GreenTree.Nachtragsmanagement.Data.Mapping.Site;
 using GreenTree.Nachtragsmanagement.Data.Mapping.User;
 
@@ -50,6 +52,9 @@ namespace GreenTree.Nachtragsmanagement.Data
             var deviationSet = Set<Deviation>();
             var categorySet = Set<Category>();
             var appendixSet = Set<Appendix>();
+            var mailNotificationSet = Set<MailNotification>();
+            var notificationEventSet = Set<NotificationEvent>();
+            var notificationEventTypeSet = Set<NotificationEventType>();
 
             _dbSets.AddRange(
                 new object[] 
@@ -64,7 +69,10 @@ namespace GreenTree.Nachtragsmanagement.Data
                     disturbanceSet,
                     deviationSet,
                     categorySet,
-                    appendixSet
+                    appendixSet,
+                    mailNotificationSet,
+                    notificationEventSet,
+                    notificationEventTypeSet
                 }
             );
 
@@ -93,6 +101,9 @@ namespace GreenTree.Nachtragsmanagement.Data
             modelBuilder.Configurations.Add(new DeviationMap());
             modelBuilder.Configurations.Add(new CategoryMap());
             modelBuilder.Configurations.Add(new AppendixMap());
+            modelBuilder.Configurations.Add(new MailNotificationMap());
+            modelBuilder.Configurations.Add(new NotificationEventMap());
+            modelBuilder.Configurations.Add(new NotificationEventTypeMap());
 
             base.OnModelCreating(modelBuilder);
         }

+ 235 - 0
GreenTree.Nachtragsmanagement.Data/EfRepository.cs

@@ -0,0 +1,235 @@
+using System;
+using System.Collections.Generic;
+using System.Data.Entity;
+using System.Data.Entity.Validation;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using GreenTree.Nachtragsmanagement.Core;
+using GreenTree.Nachtragsmanagement.Core.Data;
+
+namespace GreenTree.Nachtragsmanagement.Data
+{
+    public class EfRepository<T> : IRepository<T> where T : BaseEntity
+    {
+        #region Fields
+
+        private readonly IDbContext _context;
+        private IDbSet<T> _entities;
+
+        #endregion
+
+        #region Ctor
+
+        /// <summary>
+        /// Ctor
+        /// </summary>
+        /// <param name="context">Object context</param>
+        public EfRepository(IDbContext context)
+        {
+            _context = context;
+        }
+
+        #endregion
+
+        #region Methods
+
+        /// <summary>
+        /// Get entity by identifier
+        /// </summary>
+        /// <param name="id">Identifier</param>
+        /// <returns>Entity</returns>
+        public virtual T GetById(object id)
+        {
+            //see some suggested performance optimization (not tested)
+            //http://stackoverflow.com/questions/11686225/dbset-find-method-ridiculously-slow-compared-to-singleordefault-on-id/11688189#comment34876113_11688189
+            return Entities.Find(id);
+        }
+
+        /// <summary>
+        /// Insert entity
+        /// </summary>
+        /// <param name="entity">Entity</param>
+        public virtual void Insert(T entity)
+        {
+            try
+            {
+                if (entity == null)
+                    throw new ArgumentNullException("entity");
+
+                Entities.Add(entity);
+
+                _context.SaveChanges();
+            }
+            catch (DbEntityValidationException dbEx)
+            {
+                var msg = string.Empty;
+
+                foreach (var validationErrors in dbEx.EntityValidationErrors)
+                    foreach (var validationError in validationErrors.ValidationErrors)
+                        msg += string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage) + Environment.NewLine;
+
+                var fail = new Exception(msg, dbEx);
+                //Debug.WriteLine(fail.Message, fail);
+                throw fail;
+            }
+        }
+
+        /// <summary>
+        /// Insert entities
+        /// </summary>
+        /// <param name="entities">Entities</param>
+        public virtual void Insert(IEnumerable<T> entities)
+        {
+            try
+            {
+                if (entities == null)
+                    throw new ArgumentNullException("entities");
+
+                foreach (var entity in entities)
+                    Entities.Add(entity);
+
+                _context.SaveChanges();
+            }
+            catch (DbEntityValidationException dbEx)
+            {
+                var msg = string.Empty;
+
+                foreach (var validationErrors in dbEx.EntityValidationErrors)
+                    foreach (var validationError in validationErrors.ValidationErrors)
+                        msg += string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage) + Environment.NewLine;
+
+                var fail = new Exception(msg, dbEx);
+                //Debug.WriteLine(fail.Message, fail);
+                throw fail;
+            }
+        }
+
+        /// <summary>
+        /// Update entity
+        /// </summary>
+        /// <param name="entity">Entity</param>
+        public virtual void Update(T entity)
+        {
+            try
+            {
+                if (entity == null)
+                    throw new ArgumentNullException("entity");
+
+                _context.SaveChanges();
+            }
+            catch (DbEntityValidationException dbEx)
+            {
+                var msg = string.Empty;
+
+                foreach (var validationErrors in dbEx.EntityValidationErrors)
+                    foreach (var validationError in validationErrors.ValidationErrors)
+                        msg += Environment.NewLine + string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
+
+                var fail = new Exception(msg, dbEx);
+                //Debug.WriteLine(fail.Message, fail);
+                throw fail;
+            }
+        }
+
+        /// <summary>
+        /// Delete entity
+        /// </summary>
+        /// <param name="entity">Entity</param>
+        public virtual void Delete(T entity)
+        {
+            try
+            {
+                if (entity == null)
+                    throw new ArgumentNullException("entity");
+
+                Entities.Remove(entity);
+
+                _context.SaveChanges();
+            }
+            catch (DbEntityValidationException dbEx)
+            {
+                var msg = string.Empty;
+
+                foreach (var validationErrors in dbEx.EntityValidationErrors)
+                    foreach (var validationError in validationErrors.ValidationErrors)
+                        msg += Environment.NewLine + string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
+
+                var fail = new Exception(msg, dbEx);
+                //Debug.WriteLine(fail.Message, fail);
+                throw fail;
+            }
+        }
+
+        /// <summary>
+        /// Delete entities
+        /// </summary>
+        /// <param name="entities">Entities</param>
+        public virtual void Delete(IEnumerable<T> entities)
+        {
+            try
+            {
+                if (entities == null)
+                    throw new ArgumentNullException("entities");
+
+                foreach (var entity in entities)
+                    Entities.Remove(entity);
+
+                _context.SaveChanges();
+            }
+            catch (DbEntityValidationException dbEx)
+            {
+                var msg = string.Empty;
+
+                foreach (var validationErrors in dbEx.EntityValidationErrors)
+                    foreach (var validationError in validationErrors.ValidationErrors)
+                        msg += Environment.NewLine + string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
+
+                var fail = new Exception(msg, dbEx);
+                //Debug.WriteLine(fail.Message, fail);
+                throw fail;
+            }
+        }
+
+        #endregion
+
+        #region Properties
+
+        /// <summary>
+        /// Gets a table
+        /// </summary>
+        public virtual IQueryable<T> Table
+        {
+            get
+            {
+                return Entities;
+            }
+        }
+
+        /// <summary>
+        /// Gets a table with "no tracking" enabled (EF feature) Use it only when you load record(s) only for read-only operations
+        /// </summary>
+        public virtual IQueryable<T> TableNoTracking
+        {
+            get
+            {
+                return Entities.AsNoTracking();
+            }
+        }
+
+        /// <summary>
+        /// Entities
+        /// </summary>
+        protected virtual IDbSet<T> Entities
+        {
+            get
+            {
+                if (_entities == null)
+                    _entities = _context.Get<T>();
+                return _entities;
+            }
+        }
+
+        #endregion
+    }
+}

+ 4 - 0
GreenTree.Nachtragsmanagement.Data/GreenTree.Nachtragsmanagement.Data.csproj

@@ -48,6 +48,7 @@
   </ItemGroup>
   <ItemGroup>
     <Compile Include="AppendixObjectContext.cs" />
+    <Compile Include="EfRepository.cs" />
     <Compile Include="IDbContext.cs" />
     <Compile Include="Mapping\Appendix\AppendixMap.cs" />
     <Compile Include="Mapping\Appendix\CategoryMap.cs" />
@@ -56,6 +57,9 @@
     <Compile Include="Mapping\Deviation\KindMap.cs" />
     <Compile Include="Mapping\Deviation\StatusMap.cs" />
     <Compile Include="Mapping\Invoice\InvoiceMap.cs" />
+    <Compile Include="Mapping\Misc\MailNotificationMap.cs" />
+    <Compile Include="Mapping\Misc\NotificationEventMap.cs" />
+    <Compile Include="Mapping\Misc\NotificationEventTypeMap.cs" />
     <Compile Include="Mapping\Site\SiteMap.cs" />
     <Compile Include="Mapping\User\FunctionMap.cs" />
     <Compile Include="Mapping\User\RoleMap.cs" />

+ 1 - 2
GreenTree.Nachtragsmanagement.Data/Mapping/Appendix/AppendixMap.cs

@@ -40,8 +40,7 @@ namespace GreenTree.Nachtragsmanagement.Data.Mapping.Appendix
                 .WithOptional();
 
             HasMany(s => s.Invoices)
-                .WithMany()
-                .Map(a => a.ToTable("AppendixInvoices"));
+                .WithOptional();
         }
     }
 }

+ 4 - 0
GreenTree.Nachtragsmanagement.Data/Mapping/Invoice/InvoiceMap.cs

@@ -18,6 +18,10 @@ namespace GreenTree.Nachtragsmanagement.Data.Mapping.Deviation
             Property(f => f.CustomNumber);
             Property(f => f.Date);
             Property(f => f.Value);
+
+            HasOptional(i => i.Appendix)
+                .WithMany(a => a.Invoices)
+                .HasForeignKey(i => i.AppendixId);
         }
     }
 }

+ 37 - 0
GreenTree.Nachtragsmanagement.Data/Mapping/Misc/MailNotificationMap.cs

@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Data.Entity.ModelConfiguration;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using GreenTree.Nachtragsmanagement.Core.Domain.Misc;
+
+namespace GreenTree.Nachtragsmanagement.Data.Mapping.Misc
+{
+    public class MailNotificationMap : EntityTypeConfiguration<MailNotification>
+    {
+        public MailNotificationMap()
+        {
+            ToTable("MailNotification");
+
+            HasKey(m => m.Id);
+
+            Property(m => m.CronExpression);
+            Property(m => m.IsDailySummary);
+            Property(m => m.IsWeeklySummary);
+            Property(m => m.IsMonthlySummary);
+
+            HasOptional(m => m.NotificationEvent)
+                .WithMany()
+                .HasForeignKey(m => m.NotificationEventId);
+
+            HasOptional(m => m.NotificationEventType)
+                .WithMany()
+                .HasForeignKey(m => m.NotificationEventTypeId);
+
+            HasMany(m => m.Users)
+                .WithMany(u => u.MailNotifications)
+                .Map(m => m.ToTable("UserMailNotifications"));
+        }
+    }
+}

+ 22 - 0
GreenTree.Nachtragsmanagement.Data/Mapping/Misc/NotificationEventMap.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Data.Entity.ModelConfiguration;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using GreenTree.Nachtragsmanagement.Core.Domain.Misc;
+
+namespace GreenTree.Nachtragsmanagement.Data.Mapping.Misc
+{
+    public class NotificationEventMap : EntityTypeConfiguration<NotificationEvent>
+    {
+        public NotificationEventMap()
+        {
+            ToTable("NotificationEvent");
+
+            HasKey(n => n.Id);
+
+            Property(n => n.Description);
+        }
+    }
+}

+ 22 - 0
GreenTree.Nachtragsmanagement.Data/Mapping/Misc/NotificationEventTypeMap.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Data.Entity.ModelConfiguration;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using GreenTree.Nachtragsmanagement.Core.Domain.Misc;
+
+namespace GreenTree.Nachtragsmanagement.Data.Mapping.Misc
+{
+    public class NotificationEventTypeMap : EntityTypeConfiguration<NotificationEventType>
+    {
+        public NotificationEventTypeMap()
+        {
+            ToTable("NotificationEventType");
+
+            HasKey(n => n.Id);
+
+            Property(n => n.Description);
+        }
+    }
+}

+ 5 - 0
GreenTree.Nachtragsmanagement.Data/Mapping/User/UserMap.cs

@@ -19,10 +19,15 @@ namespace GreenTree.Nachtragsmanagement.Data.Mapping.User
             Property(u => u.Forename);
             Property(u => u.Lastname);
             Property(u => u.MailAddress);
+            Property(u => u.Password);
 
             HasMany(c => c.Roles)
                 .WithMany()
                 .Map(m => m.ToTable("UserRoles"));
+
+            HasMany(c => c.MailNotifications)
+                .WithMany()
+                .Map(m => m.ToTable("UserMailNotifications"));
         }
     }
 }

+ 161 - 0
GreenTree.Nachtragsmanagement.Services/Appendix/AppendixService.cs

@@ -0,0 +1,161 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using GreenTree.Nachtragsmanagement.Core.Data;
+using GreenTree.Nachtragsmanagement.Core.Domain.Appendix;
+
+namespace GreenTree.Nachtragsmanagement.Services.Appendix
+{
+    public class AppendixService : IAppendixService
+    {
+        #region Fields
+
+        private readonly IRepository<Core.Domain.Appendix.Appendix> _appendixRepository;
+        private readonly IRepository<Category> _categoryRepository;
+
+        #endregion
+
+        #region Ctor
+
+        /// <summary>
+        /// Initializes a new instance of the AppendixService class
+        /// </summary>
+        public AppendixService(
+            IRepository<Core.Domain.Appendix.Appendix> appendixRepository,
+            IRepository<Category> categoryRepository)
+        {
+            _appendixRepository = appendixRepository;
+            _categoryRepository = categoryRepository;
+        }
+
+        #endregion
+
+        #region Appendix
+
+        /// <summary>
+        /// Gets all appendices
+        /// </summary>
+        public IList<Core.Domain.Appendix.Appendix> GetAllAppendices()
+        {
+            return _appendixRepository.Table.ToList();
+        }
+
+        /// <summary>
+        /// Gets a appendix by specified Id
+        /// </summary>
+        /// <param name="id">Appendix identifier.</param>
+        public Core.Domain.Appendix.Appendix GetAppendixById(int id)
+        {
+            return _appendixRepository.GetById(id);
+        }
+
+        /// <summary>
+        /// Gets all appendices to the specified ids
+        /// </summary>
+        public IList<Core.Domain.Appendix.Appendix> GetAppendicesByIds(int[] ids)
+        {
+            return _appendixRepository.Table
+                .Where(u => ids.Contains(u.Id))
+                .ToList();
+        }
+
+        /// <summary>
+        /// Gets a appendix by specified custom number
+        /// </summary>
+        /// <param name="id">Customer number.</param>
+        public Core.Domain.Appendix.Appendix GetAppendixByCustomNumber(int customNumber)
+        {
+            return _appendixRepository
+                .Table.FirstOrDefault(u => u.CustomNumber == customNumber);
+        }
+
+        /// <summary>
+        /// Insert a appendix
+        /// </summary>
+        /// <param name="appendix">Appendix.</param>
+        public void InsertAppendix(Core.Domain.Appendix.Appendix appendix)
+        {
+            _appendixRepository.Insert(appendix);
+        }
+
+        /// <summary>
+        /// Update a appendix
+        /// </summary>
+        /// <param name="appendix">Appendix.</param>
+        public void UpdateAppendix(Core.Domain.Appendix.Appendix appendix)
+        {
+            _appendixRepository.Update(appendix);
+        }
+
+        /// <summary>
+        /// Delete a appendix
+        /// </summary>
+        /// <param name="appendix">Appendix.</param>
+        public void DeleteAppendix(Core.Domain.Appendix.Appendix appendix)
+        {
+            _appendixRepository.Delete(appendix);
+        }
+
+        #endregion
+
+        #region Category
+
+        /// <summary>
+        /// Gets all categories
+        /// </summary>
+        public IList<Category> GetAllCategories()
+        {
+            return _categoryRepository.Table.ToList();
+        }
+
+        /// <summary>
+        /// Gets a category by specified Id
+        /// </summary>
+        /// <param name="id">Category identifier.</param>
+        public Category GetCategoryById(int id)
+        {
+            return _categoryRepository.GetById(id);
+        }
+
+        /// <summary>
+        /// Gets all categories to the specified ids
+        /// </summary>
+        public IList<Category> GetCategoriesByIds(int[] ids)
+        {
+            return _categoryRepository.Table
+                .Where(r => ids.Contains(r.Id))
+                .ToList();
+        }
+
+        /// <summary>
+        /// Insert a appendix
+        /// </summary>
+        /// <param name="category">Category.</param>
+        public void InsertCategory(Category category)
+        {
+            _categoryRepository.Insert(category);
+        }
+
+        /// <summary>
+        /// Update a category
+        /// </summary>
+        /// <param name="category">Category.</param>
+        public void UpdateCategory(Category category)
+        {
+            _categoryRepository.Update(category);
+        }
+
+        /// <summary>
+        /// Delete a category
+        /// </summary>
+        /// <param name="category">Category.</param>
+        public void DeleteCategory(Category category)
+        {
+            _categoryRepository.Delete(category);
+        }
+
+        #endregion
+    }
+}

+ 94 - 0
GreenTree.Nachtragsmanagement.Services/Appendix/IAppendixService.cs

@@ -0,0 +1,94 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using GreenTree.Nachtragsmanagement.Core.Domain.Appendix;
+
+namespace GreenTree.Nachtragsmanagement.Services.Appendix
+{
+    public interface IAppendixService
+    {
+        #region Appendix
+
+        /// <summary>
+        /// Gets all appendices
+        /// </summary>
+        IList<Core.Domain.Appendix.Appendix> GetAllAppendices();
+
+        /// <summary>
+        /// Gets a appendix by specified Id
+        /// </summary>
+        /// <param name="id">Appendix identifier.</param>
+        Core.Domain.Appendix.Appendix GetAppendixById(int id);
+
+        /// <summary>
+        /// Gets all appendices to the specified ids
+        /// </summary>
+        IList<Core.Domain.Appendix.Appendix> GetAppendicesByIds(int[] ids);
+
+        /// <summary>
+        /// Gets a appendix by specified customer number
+        /// </summary>
+        /// <param name="id">Customer number.</param>
+        Core.Domain.Appendix.Appendix GetAppendixByCustomNumber(int customNumber);
+
+        /// <summary>
+        /// Insert a appendix
+        /// </summary>
+        /// <param name="appendix">Appendix.</param>
+        void InsertAppendix(Core.Domain.Appendix.Appendix appendix);
+
+        /// <summary>
+        /// Update a appendix
+        /// </summary>
+        /// <param name="appendix">Appendix.</param>
+        void UpdateAppendix(Core.Domain.Appendix.Appendix appendix);
+
+        /// <summary>
+        /// Delete a appendix
+        /// </summary>
+        /// <param name="appendix">Appendix.</param>
+        void DeleteAppendix(Core.Domain.Appendix.Appendix appendix);
+
+        #endregion
+
+        #region Category
+
+        /// <summary>
+        /// Gets all categories
+        /// </summary>
+        IList<Category> GetAllCategories();
+
+        /// <summary>
+        /// Gets a category by specified Id
+        /// </summary>
+        /// <param name="id">Category identifier.</param>
+        Category GetCategoryById(int id);
+
+        /// <summary>
+        /// Gets all categories to the specified ids
+        /// </summary>
+        IList<Category> GetCategoriesByIds(int[] ids);
+
+        /// <summary>
+        /// Insert a category
+        /// </summary>
+        /// <param name="category">Category.</param>
+        void InsertCategory(Category category);
+
+        /// <summary>
+        /// Update a category
+        /// </summary>
+        /// <param name="category">Category.</param>
+        void UpdateCategory(Category category);
+
+        /// <summary>
+        /// Delete a category
+        /// </summary>
+        /// <param name="category">Category.</param>
+        void DeleteCategory(Category category);
+
+        #endregion
+    }
+}

+ 26 - 0
GreenTree.Nachtragsmanagement.Services/Configuration/ConfigurationService.cs

@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Web.Configuration;
+using GreenTree.Nachtragsmanagement.Core.Configuration;
+
+namespace GreenTree.Nachtragsmanagement.Services.Configuration
+{
+    public class ConfigurationService : IConfigurationService
+    {
+        /// <summary>
+        /// Reads the current configuration from the global config
+        /// </summary>
+        public AppendixConfigurationSection GetCurrentConfiguration()
+        {
+            var config = WebConfigurationManager.OpenWebConfiguration("~/");
+            var sectionGroup = config.GetSectionGroup("appendixSectionGroup");
+            var section = sectionGroup.Sections["appendixConfigSection"];
+
+            return (AppendixConfigurationSection)section;
+        }
+    }
+}

+ 17 - 0
GreenTree.Nachtragsmanagement.Services/Configuration/IConfigurationService.cs

@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using GreenTree.Nachtragsmanagement.Core.Configuration;
+
+namespace GreenTree.Nachtragsmanagement.Services.Configuration
+{
+    public interface IConfigurationService
+    {
+        /// <summary>
+        /// Reads the current configuration from the global config
+        /// </summary>
+        AppendixConfigurationSection GetCurrentConfiguration();
+    }
+}

+ 13 - 4
GreenTree.Nachtragsmanagement.Services/GreenTree.Nachtragsmanagement.Services.csproj

@@ -38,7 +38,9 @@
       <HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
     </Reference>
     <Reference Include="System" />
+    <Reference Include="System.configuration" />
     <Reference Include="System.Core" />
+    <Reference Include="System.Web" />
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
     <Reference Include="Microsoft.CSharp" />
@@ -47,12 +49,22 @@
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="Appendix\AppendixService.cs" />
+    <Compile Include="Appendix\IAppendixService.cs" />
+    <Compile Include="Configuration\ConfigurationService.cs" />
+    <Compile Include="Configuration\IConfigurationService.cs" />
     <Compile Include="DbContext\DbContextService.cs" />
     <Compile Include="DbContext\IDbContextService.cs" />
+    <Compile Include="Notification\INotificationLogic.cs" />
+    <Compile Include="Notification\INotificationService.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Site\ISiteService.cs" />
+    <Compile Include="Site\SiteService.cs" />
     <Compile Include="Test\DbRelationFormat.cs" />
     <Compile Include="Test\DbRelationService.cs" />
     <Compile Include="Test\IDbRelationService.cs" />
+    <Compile Include="User\IUserService.cs" />
+    <Compile Include="User\UserService.cs" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\GreenTree.Nachtragsmanagement.Core\GreenTree.Nachtragsmanagement.Core.csproj">
@@ -67,9 +79,6 @@
   <ItemGroup>
     <None Include="packages.config" />
   </ItemGroup>
-  <ItemGroup>
-    <Folder Include="Common\" />
-    <Folder Include="User\" />
-  </ItemGroup>
+  <ItemGroup />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
 </Project>

+ 21 - 0
GreenTree.Nachtragsmanagement.Services/Notification/INotificationLogic.cs

@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GreenTree.Nachtragsmanagement.Services.Notification
+{
+    public interface INotificationLogic
+    {
+        /// <summary>
+        /// Id of the notification plugin
+        /// </summary>
+        Guid Id { get; set; }
+
+        /// <summary>
+        /// Name of the notification plugin
+        /// </summary>
+        string Name { get; set; }
+    }
+}

+ 15 - 0
GreenTree.Nachtragsmanagement.Services/Notification/INotificationService.cs

@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using GreenTree.Nachtragsmanagement.Core.Domain.Misc;
+
+namespace GreenTree.Nachtragsmanagement.Services.Notification
+{
+    public interface INotificationService
+    {
+
+        string GenerateMailBody(MailNotification mailNotification);
+    }
+}

+ 55 - 0
GreenTree.Nachtragsmanagement.Services/Site/ISiteService.cs

@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GreenTree.Nachtragsmanagement.Services.Site
+{
+    public interface ISiteService
+    {
+        #region Site
+
+        /// <summary>
+        /// Gets all sites
+        /// </summary>
+        IList<Core.Domain.Site.Site> GetAllSites();
+
+        /// <summary>
+        /// Gets a site by specified Id
+        /// </summary>
+        /// <param name="id">Site identifier.</param>
+        Core.Domain.Site.Site GetSiteById(int id);
+
+        /// <summary>
+        /// Gets all sites to the specified ids
+        /// </summary>
+        IList<Core.Domain.Site.Site> GetSitesByIds(int[] ids);
+
+        /// <summary>
+        /// Gets a site by specified customer number
+        /// </summary>
+        /// <param name="id">Customer number.</param>
+        Core.Domain.Site.Site GetSiteByCustomNumber(int customNumber);
+
+        /// <summary>
+        /// Insert a site
+        /// </summary>
+        /// <param name="site">Site.</param>
+        void InsertSite(Core.Domain.Site.Site site);
+
+        /// <summary>
+        /// Update a site
+        /// </summary>
+        /// <param name="site">Site.</param>
+        void UpdateSite(Core.Domain.Site.Site site);
+
+        /// <summary>
+        /// Delete a site
+        /// </summary>
+        /// <param name="site">Site.</param>
+        void DeleteSite(Core.Domain.Site.Site site);
+
+        #endregion
+    }
+}

+ 99 - 0
GreenTree.Nachtragsmanagement.Services/Site/SiteService.cs

@@ -0,0 +1,99 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using GreenTree.Nachtragsmanagement.Core.Data;
+
+namespace GreenTree.Nachtragsmanagement.Services.Site
+{
+    public class SiteService : ISiteService
+    {
+        #region Fields
+
+        private readonly IRepository<Core.Domain.Site.Site> _siteRepository;
+
+        #endregion
+
+        #region Ctor
+
+        /// <summary>
+        /// Initializes a new instance of the SiteService class
+        /// </summary>
+        public SiteService(
+            IRepository<Core.Domain.Site.Site> siteRepository)
+        {
+            _siteRepository = siteRepository;
+        }
+
+        #endregion
+
+        #region Site
+
+        /// <summary>
+        /// Gets all sites
+        /// </summary>
+        public IList<Core.Domain.Site.Site> GetAllSites()
+        {
+            return _siteRepository.Table.ToList();
+        }
+
+        /// <summary>
+        /// Gets a site by specified Id
+        /// </summary>
+        /// <param name="id">Site identifier.</param>
+        public Core.Domain.Site.Site GetSiteById(int id)
+        {
+            return _siteRepository.GetById(id);
+        }
+
+        /// <summary>
+        /// Gets all sites to the specified ids
+        /// </summary>
+        public IList<Core.Domain.Site.Site> GetSitesByIds(int[] ids)
+        {
+            return _siteRepository.Table
+                .Where(u => ids.Contains(u.Id))
+                .ToList();
+        }
+
+        /// <summary>
+        /// Gets a site by specified customer number
+        /// </summary>
+        /// <param name="id">Customer number.</param>
+        public Core.Domain.Site.Site GetSiteByCustomNumber(int customNumber)
+        {
+            return _siteRepository
+                .Table.FirstOrDefault(u => u.CustomNumber == customNumber);
+        }
+
+        /// <summary>
+        /// Insert a site
+        /// </summary>
+        /// <param name="site">Site.</param>
+        public void InsertSite(Core.Domain.Site.Site site)
+        {
+            _siteRepository.Insert(site);
+        }
+
+        /// <summary>
+        /// Update a site
+        /// </summary>
+        /// <param name="site">Site.</param>
+        public void UpdateSite(Core.Domain.Site.Site site)
+        {
+            _siteRepository.Update(site);
+        }
+
+        /// <summary>
+        /// Delete a site
+        /// </summary>
+        /// <param name="site">Site.</param>
+        public void DeleteSite(Core.Domain.Site.Site site)
+        {
+            _siteRepository.Delete(site);
+        }
+
+        #endregion
+    }
+}

+ 4 - 4
GreenTree.Nachtragsmanagement.Services/Test/DbRelationService.cs

@@ -38,10 +38,10 @@ namespace GreenTree.Nachtragsmanagement.Services.Test
             var result = new List<string>();
             var dbContext = (AppendixObjectContext)_idbContext;
 
-            var userList = dbContext.Get<User>().ToList();
-            var deviationList = dbContext.Get<Deviation>().ToList();
-            var siteList = dbContext.Get<Site>().ToList();
-            var appendixList = dbContext.Get<Appendix>().ToList();
+            var userList = dbContext.Get<Core.Domain.User.User>().ToList();
+            var deviationList = dbContext.Get<Core.Domain.Deviation.Deviation>().ToList();
+            var siteList = dbContext.Get<Core.Domain.Site.Site>().ToList();
+            var appendixList = dbContext.Get<Core.Domain.Appendix.Appendix>().ToList();
 
             if (format == DbRelationFormat.Json)
             {

+ 136 - 0
GreenTree.Nachtragsmanagement.Services/User/IUserService.cs

@@ -0,0 +1,136 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using GreenTree.Nachtragsmanagement.Core.Domain.User;
+
+namespace GreenTree.Nachtragsmanagement.Services.User
+{
+    /// <summary>
+    /// User service interface
+    /// </summary>
+    public interface IUserService
+    {
+        #region User
+
+        /// <summary>
+        /// Gets all users
+        /// </summary>
+        IList<Core.Domain.User.User> GetAllUsers();
+
+        /// <summary>
+        /// Gets a user by specified Id
+        /// </summary>
+        /// <param name="id">User identifier.</param>
+        Core.Domain.User.User GetUserById(int id);
+
+        /// <summary>
+        /// Gets all users to the specified ids
+        /// </summary>
+        IList<Core.Domain.User.User> GetUsersByIds(int[] ids);
+
+        /// <summary>
+        /// Gets a user by specified customer number
+        /// </summary>
+        /// <param name="id">Customer number.</param>
+        Core.Domain.User.User GetUserByCustomNumber(int customNumber);
+
+        /// <summary>
+        /// Insert a user
+        /// </summary>
+        /// <param name="user">User.</param>
+        void InsertUser(Core.Domain.User.User user);
+
+        /// <summary>
+        /// Update a user
+        /// </summary>
+        /// <param name="user">User.</param>
+        void UpdateUser(Core.Domain.User.User user);
+
+        /// <summary>
+        /// Delete a user
+        /// </summary>
+        /// <param name="user">User.</param>
+        void DeleteUser(Core.Domain.User.User user);
+
+        #endregion
+
+        #region Role
+
+        /// <summary>
+        /// Gets all roles
+        /// </summary>
+        IList<Role> GetAllRoles();
+
+        /// <summary>
+        /// Gets a user by specified Id
+        /// </summary>
+        /// <param name="id">Role identifier.</param>
+        Role GetRoleById(int id);
+
+        /// <summary>
+        /// Gets all roles to the specified ids
+        /// </summary>
+        IList<Role> GetRolesByIds(int[] ids);
+
+        /// <summary>
+        /// Insert a user
+        /// </summary>
+        /// <param name="role">Role.</param>
+        void InsertRole(Role role);
+
+        /// <summary>
+        /// Update a role
+        /// </summary>
+        /// <param name="role">Role.</param>
+        void UpdateRole(Role role);
+
+        /// <summary>
+        /// Delete a role
+        /// </summary>
+        /// <param name="role">Role.</param>
+        void DeleteRole(Role role);
+
+        #endregion
+
+        #region Function
+
+        /// <summary>
+        /// Gets all functions
+        /// </summary>
+        IList<Function> GetAllFunctions();
+
+        /// <summary>
+        /// Gets a function by specified Id
+        /// </summary>
+        /// <param name="id">Function identifier.</param>
+        Function GetFunctionById(int id);
+
+        /// <summary>
+        /// Gets all functions to the specified ids
+        /// </summary>
+        IList<Function> GetFunctionsByIds(int[] ids);
+
+        /// <summary>
+        /// Insert a function
+        /// </summary>
+        /// <param name="function">Function.</param>
+        void InsertFunction(Function function);
+
+        /// <summary>
+        /// Update a function
+        /// </summary>
+        /// <param name="function">Function.</param>
+        void UpdateFunction(Function function);
+
+        /// <summary>
+        /// Delete a function
+        /// </summary>
+        /// <param name="function">Function.</param>
+        void DeleteFunction(Function function);
+
+        #endregion
+    }
+}

+ 225 - 0
GreenTree.Nachtragsmanagement.Services/User/UserService.cs

@@ -0,0 +1,225 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using GreenTree.Nachtragsmanagement.Core.Data;
+using GreenTree.Nachtragsmanagement.Core.Domain.User;
+
+namespace GreenTree.Nachtragsmanagement.Services.User
+{
+    /// <summary>
+    /// User service
+    /// </summary>
+    public class UserService : IUserService
+    {
+        #region Fields
+
+        private readonly IRepository<Core.Domain.User.User> _userRepository;
+        private readonly IRepository<Role> _roleRepository;
+        private readonly IRepository<Function> _functionRepository;
+
+        #endregion
+
+        #region Ctor
+
+        /// <summary>
+        /// Initializes a new instance of the UserService class
+        /// </summary>
+        public UserService(
+            IRepository<Core.Domain.User.User> userRepository,
+            IRepository<Role> roleRepository,
+            IRepository<Function> functionRepository)
+        {
+            _userRepository = userRepository;
+            _roleRepository = roleRepository;
+            _functionRepository = functionRepository;
+        }
+
+        #endregion
+
+        #region User
+
+        /// <summary>
+        /// Gets all users
+        /// </summary>
+        public IList<Core.Domain.User.User> GetAllUsers()
+        {
+            return _userRepository.Table.ToList();
+        }
+
+        /// <summary>
+        /// Gets a user by specified Id
+        /// </summary>
+        /// <param name="id">User identifier.</param>
+        public Core.Domain.User.User GetUserById(int id)
+        {
+            return _userRepository.GetById(id);
+        }
+
+        /// <summary>
+        /// Gets all users to the specified ids
+        /// </summary>
+        public IList<Core.Domain.User.User> GetUsersByIds(int[] ids)
+        {
+            return _userRepository.Table
+                .Where(u => ids.Contains(u.Id))
+                .ToList();
+        }
+
+        /// <summary>
+        /// Gets a user by specified custom number
+        /// </summary>
+        /// <param name="id">Customer number.</param>
+        public Core.Domain.User.User GetUserByCustomNumber(int customNumber)
+        {
+            return _userRepository
+                .Table.FirstOrDefault(u => u.CustomId == customNumber);
+        }
+
+        /// <summary>
+        /// Insert a user
+        /// </summary>
+        /// <param name="user">User.</param>
+        public void InsertUser(Core.Domain.User.User user)
+        {
+            _userRepository.Insert(user);
+        }
+
+        /// <summary>
+        /// Update a user
+        /// </summary>
+        /// <param name="user">User.</param>
+        public void UpdateUser(Core.Domain.User.User user)
+        {
+            _userRepository.Update(user);
+        }
+
+        /// <summary>
+        /// Delete a user
+        /// </summary>
+        /// <param name="user">User.</param>
+        public void DeleteUser(Core.Domain.User.User user)
+        {
+            _userRepository.Delete(user);
+        }
+
+        #endregion
+
+        #region Role
+
+        /// <summary>
+        /// Gets all roles
+        /// </summary>
+        public IList<Role> GetAllRoles()
+        {
+            return _roleRepository.Table.ToList();
+        }
+
+        /// <summary>
+        /// Gets a role by specified Id
+        /// </summary>
+        /// <param name="id">Role identifier.</param>
+        public Role GetRoleById(int id)
+        {
+            return _roleRepository.GetById(id);
+        }
+
+        /// <summary>
+        /// Gets all roles to the specified ids
+        /// </summary>
+        public IList<Role> GetRolesByIds(int[] ids)
+        {
+            return _roleRepository.Table
+                .Where(r => ids.Contains(r.Id))
+                .ToList();
+        }
+
+        /// <summary>
+        /// Insert a user
+        /// </summary>
+        /// <param name="role">Role.</param>
+        public void InsertRole(Role role)
+        {
+            _roleRepository.Insert(role);
+        }
+
+        /// <summary>
+        /// Update a role
+        /// </summary>
+        /// <param name="role">Role.</param>
+        public void UpdateRole(Role role)
+        {
+            _roleRepository.Update(role);
+        }
+
+        /// <summary>
+        /// Delete a role
+        /// </summary>
+        /// <param name="role">Role.</param>
+        public void DeleteRole(Role role)
+        {
+            _roleRepository.Delete(role);
+        }
+
+        #endregion
+
+        #region Function
+
+        /// <summary>
+        /// Gets all functions
+        /// </summary>
+        public IList<Function> GetAllFunctions()
+        {
+            return _functionRepository.Table.ToList();
+        }
+
+        /// <summary>
+        /// Gets a function by specified Id
+        /// </summary>
+        /// <param name="id">Function identifier.</param>
+        public Function GetFunctionById(int id)
+        {
+            return _functionRepository.GetById(id);
+        }
+
+        /// <summary>
+        /// Gets all functions to the specified ids
+        /// </summary>
+        public IList<Function> GetFunctionsByIds(int[] ids)
+        {
+            return _functionRepository.Table
+                .Where(f => ids.Contains(f.Id))
+                .ToList();
+        }
+
+        /// <summary>
+        /// Insert a function
+        /// </summary>
+        /// <param name="function">Function.</param>
+        public void InsertFunction(Function function)
+        {
+            _functionRepository.Insert(function);
+        }
+
+        /// <summary>
+        /// Update a function
+        /// </summary>
+        /// <param name="function">Function.</param>
+        public void UpdateFunction(Function function)
+        {
+            _functionRepository.Update(function);
+        }
+
+        /// <summary>
+        /// Delete a function
+        /// </summary>
+        /// <param name="function">Function.</param>
+        public void DeleteFunction(Function function)
+        {
+            _functionRepository.Delete(function);
+        }
+
+        #endregion
+    }
+}

+ 31 - 29
GreenTree.Nachtragsmanagement.Web.Framework/ApplicationContext.cs

@@ -1,15 +1,23 @@
 using System;
 using System.Collections.Generic;
+using System.Configuration;
 using System.Linq;
 using System.Reflection;
 using System.Text;
 using System.Threading.Tasks;
+using System.Web;
+using System.Web.Configuration;
 using System.Web.Mvc;
 using Autofac;
 using Autofac.Integration.Mvc;
+using GreenTree.Nachtragsmanagement.Core.Configuration;
+using GreenTree.Nachtragsmanagement.Core.Data;
 using GreenTree.Nachtragsmanagement.Data;
+using GreenTree.Nachtragsmanagement.Services.Configuration;
 using GreenTree.Nachtragsmanagement.Services.DbContext;
+using GreenTree.Nachtragsmanagement.Services.Site;
 using GreenTree.Nachtragsmanagement.Services.Test;
+using GreenTree.Nachtragsmanagement.Services.User;
 
 namespace GreenTree.Nachtragsmanagement.Web.Framework
 {
@@ -47,20 +55,6 @@ namespace GreenTree.Nachtragsmanagement.Web.Framework
         /// </summary>
         private static IContainer _appContainer;
 
-        /// <summary>
-        /// Returns the application wide controller container builder
-        /// </summary>
-        private static IContainer _controllerContainer;
-
-        #endregion
-
-        #region Services
-
-        /// <summary>
-        /// Returns the generel DbContextService
-        /// </summary>
-        public DbContextService DbContextService { get; set; }
-
         #endregion
 
         #region Ctor
@@ -70,7 +64,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Framework
         /// </summary>
         public ApplicationContext()
         {
-            DbContextService = new DbContextService(_appContainer.Resolve<IDbContext>());
+
         }
 
         #endregion
@@ -78,29 +72,37 @@ namespace GreenTree.Nachtragsmanagement.Web.Framework
         #region Initialization
 
         /// <summary>
-        /// Registers all service ressources needed by the ApplicationContext
+        /// Registers all application ressources
         /// </summary>
-        public static void InitServices()
+        public static void InitApplication()
         {
-            var appBuilder = new ContainerBuilder();
+            var builder = new ContainerBuilder();
+
+            var connection = WebConfigurationManager.ConnectionStrings["DefaultConnection"];
+
+            // Register object instances
+            builder.RegisterInstance(new AppendixObjectContext(connection.ConnectionString)).As<IDbContext>().SingleInstance();
+
+            // Register generic data repositorys
+            builder.RegisterGeneric(typeof(EfRepository<>)).As(typeof(IRepository<>));
 
-            appBuilder
-                .RegisterInstance(new AppendixObjectContext("Server=localhost\\SQLEXPRESS;Database=Nachtragsmanagement;User Id=nachtragdat;Password=nachtragdat;"))
-                .As<IDbContext>()
-                .SingleInstance();
+            // Register service types
+            builder.RegisterType<ConfigurationService>().As<IConfigurationService>();
+            builder.RegisterType<DbRelationService>().As<IDbRelationService>();
+            builder.RegisterType<UserService>().As<IUserService>();
+            builder.RegisterType<SiteService>().As<ISiteService>();
 
-            appBuilder
-                .RegisterType<DbRelationService>()
-                .As<IDbRelationService>();
+            // Register controllers
+            builder.RegisterControllers(Assembly.GetCallingAssembly());
 
-            appBuilder
-                .RegisterControllers(Assembly.GetCallingAssembly());
+            // Register modules
+            builder.RegisterModule(new AutofacWebTypesModule());
 
-            _appContainer = appBuilder.Build();
+            _appContainer = builder.Build();
 
             DependencyResolver.SetResolver(new AutofacDependencyResolver(_appContainer));
         }
 
         #endregion
     }
-}
+}

+ 7 - 1
GreenTree.Nachtragsmanagement.Web.Framework/GreenTree.Nachtragsmanagement.Web.Framework.csproj

@@ -41,7 +41,9 @@
       <Private>True</Private>
     </Reference>
     <Reference Include="System" />
+    <Reference Include="System.configuration" />
     <Reference Include="System.Core" />
+    <Reference Include="System.Web" />
     <Reference Include="System.Web.Helpers, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
       <HintPath>..\packages\Microsoft.AspNet.WebPages.3.1.0\lib\net45\System.Web.Helpers.dll</HintPath>
     </Reference>
@@ -73,12 +75,16 @@
     <Compile Include="Singleton.cs" />
   </ItemGroup>
   <ItemGroup>
-    <Folder Include="NewFolder1\" />
+    <Folder Include="Configuration\" />
   </ItemGroup>
   <ItemGroup>
     <None Include="packages.config" />
   </ItemGroup>
   <ItemGroup>
+    <ProjectReference Include="..\GreenTree.Nachtragsmanagement.Core\GreenTree.Nachtragsmanagement.Core.csproj">
+      <Project>{0b80c4a0-cb8f-423c-8af4-92b489c238dd}</Project>
+      <Name>GreenTree.Nachtragsmanagement.Core</Name>
+    </ProjectReference>
     <ProjectReference Include="..\GreenTree.Nachtragsmanagement.Data\GreenTree.Nachtragsmanagement.Data.csproj">
       <Project>{0c45ecbc-6ad6-4eb1-89bb-f05a3f0fda13}</Project>
       <Name>GreenTree.Nachtragsmanagement.Data</Name>

+ 13 - 1
GreenTree.Nachtragsmanagement.Web/Controllers/HomeController.cs

@@ -3,7 +3,9 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Web;
 using System.Web.Mvc;
+using GreenTree.Nachtragsmanagement.Services.Configuration;
 using GreenTree.Nachtragsmanagement.Services.Test;
+using GreenTree.Nachtragsmanagement.Services.User;
 using GreenTree.Nachtragsmanagement.Web.Framework;
 using GreenTree.Nachtragsmanagement.Web.Models.Test;
 using GreenTree.Nachtragsmanagement.Web.Models.User;
@@ -14,16 +16,24 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
     public class HomeController : Controller
     {
         private readonly IDbRelationService _dbRelationService;
+        private readonly IConfigurationService _configurationService;
+        private readonly IUserService _userService;
 
         public HomeController(
-            IDbRelationService dbRelationService)
+            IDbRelationService dbRelationService,
+            IConfigurationService configurationService,
+            IUserService userService)
         {
             _dbRelationService = dbRelationService;
+            _configurationService = configurationService;
+            _userService = userService;
         }
 
         // GET: Home
         public ActionResult Index()
         {
+            var users = _userService.GetAllUsers();
+
             var relations = _dbRelationService.GetRelations(3, DbRelationFormat.Json);
 
             var model = new DbRelationModel
@@ -34,6 +44,8 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
                 AppendixJson = relations[3]
             };
 
+            var configSection = _configurationService.GetCurrentConfiguration();
+
             return View("~/Views/Home/Index.cshtml", model);
         }
     }

+ 1 - 1
GreenTree.Nachtragsmanagement.Web/Global.asax.cs

@@ -26,7 +26,7 @@ namespace GreenTree.Nachtragsmanagement.Web
             
             ModelBinders.Binders.DefaultBinder = new DevExpress.Web.Mvc.DevExpressEditorsBinder();
 
-            ApplicationContext.InitServices();
+            ApplicationContext.InitApplication();
 
             DevExpress.Web.ASPxWebControl.CallbackError += Application_Error;
         }

+ 5 - 1
GreenTree.Nachtragsmanagement.Web/GreenTree.Nachtragsmanagement.Web.csproj

@@ -173,14 +173,18 @@
     <Content Include="Views\Home\Index.cshtml" />
   </ItemGroup>
   <ItemGroup>
-    <Content Include="Web.config" />
+    <Content Include="Web.config">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="Views\Shared\_Layout.cshtml" />
     <Content Include="Views\_ViewStart.cshtml" />
     <None Include="Web.Debug.config">
       <DependentUpon>Web.config</DependentUpon>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </None>
     <None Include="Web.Release.config">
       <DependentUpon>Web.config</DependentUpon>
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </None>
   </ItemGroup>
   <ItemGroup>

+ 10 - 1
GreenTree.Nachtragsmanagement.Web/Web.config

@@ -12,9 +12,12 @@
       <section name="errors" type="DevExpress.Web.ErrorsConfigurationSection, DevExpress.Web.v17.1, Version=17.1.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" requirePermission="false" />
       <section name="resources" type="DevExpress.Web.ResourcesConfigurationSection, DevExpress.Web.v17.1, Version=17.1.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" requirePermission="false" />
     </sectionGroup>
+    <sectionGroup name="appendixSectionGroup">
+      <section name="appendixConfigSection" type="GreenTree.Nachtragsmanagement.Core.Configuration.AppendixConfigurationSection, GreenTree.Nachtragsmanagement.Core" />
+    </sectionGroup>
   </configSections>
   <connectionStrings>
-    <add name="DefaultConnection" connectionString="data source=(localdb)\mssqllocaldb;initial catalog=aspnet-GreenTree.Nachtragsmanagement.Web-20170723101623;integrated security=SSPI" providerName="System.Data.SqlClient" />
+    <add name="DefaultConnection" connectionString="Server=localhost\SQLEXPRESS;Database=Nachtragsmanagement;User Id=nachtragdat;Password=nachtragdat;" providerName="System.Data.SqlClient" />
   </connectionStrings>
   <appSettings>
     <add key="webpages:Version" value="3.0.0.0" />
@@ -24,6 +27,12 @@
     <add key="UnobtrusiveJavaScriptEnabled" value="true" />
     <add key="vs:EnableBrowserLink" value="false" />
   </appSettings>
+  <appendixSectionGroup>
+    <appendixConfigSection>
+      <mailServer smtpServer="localhost" port="25" username="" domain="" password="" useSsl="false">
+      </mailServer>
+    </appendixConfigSection>
+  </appendixSectionGroup>
   <system.web>
     <compilation debug="true" targetFramework="4.5.2">
       <assemblies>