Compare commits

...

2 Commits

Author SHA1 Message Date
56bd434a8f added some extensions, not all working 2022-03-07 20:42:46 -05:00
191aaa20cf additional collection extensions 2022-03-07 20:15:38 -05:00
7 changed files with 499 additions and 15 deletions

View File

@ -1,10 +1,12 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29123.88
# Visual Studio Version 17
VisualStudioVersion = 17.1.32210.238
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BinaryDad.Extensions", "BinaryDad.Extensions\BinaryDad.Extensions.csproj", "{2676B147-0E5A-4161-88B6-6EAFE814B769}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BinaryDad.Web.Extensions", "BinaryDad.Web.Extensions\BinaryDad.Web.Extensions.csproj", "{4ABD9A10-95DA-4843-9EF0-7BF70658311D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -15,6 +17,10 @@ Global
{2676B147-0E5A-4161-88B6-6EAFE814B769}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2676B147-0E5A-4161-88B6-6EAFE814B769}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2676B147-0E5A-4161-88B6-6EAFE814B769}.Release|Any CPU.Build.0 = Release|Any CPU
{4ABD9A10-95DA-4843-9EF0-7BF70658311D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4ABD9A10-95DA-4843-9EF0-7BF70658311D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4ABD9A10-95DA-4843-9EF0-7BF70658311D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4ABD9A10-95DA-4843-9EF0-7BF70658311D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -22,13 +28,4 @@ Global
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8B0AB60B-3317-435D-9945-0EE5492D5141}
EndGlobalSection
GlobalSection(TeamFoundationVersionControl) = preSolution
SccNumberOfProjects = 2
SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}
SccTeamFoundationServer = http://tfs.binarydad.com:8080/tfs/defaultcollection
SccLocalPath0 = .
SccProjectUniqueName1 = BinaryDad.Extensions\\BinaryDad.Extensions.csproj
SccProjectName1 = BinaryDad.Extensions
SccLocalPath1 = BinaryDad.Extensions
EndGlobalSection
EndGlobal

View File

@ -7,11 +7,18 @@ using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web;
namespace BinaryDad.Extensions
{
public static class CollectionExtensions
{
/// <summary>
/// Adds an item to the <see cref="ICollection{T}"/>, replacing it if it already exists
/// </summary>
/// <typeparam name="TItem"></typeparam>
/// <param name="items"></param>
/// <param name="item"></param>
public static void AddReplace<TItem, TProperty>(this ICollection<TItem> items, TItem item, Func<TItem, TProperty> matchingProperty) where TItem : class where TProperty : IComparable
{
var existing = items.FirstOrDefault(i => matchingProperty(i).Equals(matchingProperty(item)));
@ -39,6 +46,21 @@ namespace BinaryDad.Extensions
}
}
/// <summary>
/// Groups a set of items into batches of a given batch size
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items"></param>
/// <param name="batchSize"></param>
/// <returns></returns>
public static IEnumerable<ICollection<T>> Batch<T>(this IEnumerable<T> items, int batchSize)
{
return items.Select((Item, Index) => new { Item, Index })
.GroupBy(x => x.Index / batchSize)
.Select(x => x.Select(t => t.Item).ToList())
.ToList();
}
/// <summary>
/// Returns a distinct list of elements using the first-matched item on a specific property
/// </summary>
@ -69,6 +91,16 @@ namespace BinaryDad.Extensions
#region ToDataTable
/// <summary>
/// Converts a typed collection into a <see cref="DataTable"/>. This method excludes the column if <see cref="NotMappedAttribute"/> is bound to a property.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="collection"></param>
/// <param name="useColumnAttributeName">Specifies whether the data column should use the name from <see cref="ColumnAttribute"/></param>, if bound to a property.
/// <param name="tableName"></param>
/// <returns></returns>
public static DataTable ToDataTable(this IEnumerable collection, bool useColumnAttributeName = false, string tableName = null) => ToDataTable(collection, null, useColumnAttributeName, tableName);
/// <summary>
/// Converts a typed collection into a <see cref="DataTable"/>. This method excludes the column if <see cref="NotMappedAttribute"/> is bound to a property.
/// </summary>
@ -78,7 +110,19 @@ namespace BinaryDad.Extensions
/// <param name="useColumnAttributeName">Specifies whether the data column should use the name from <see cref="ColumnAttribute"/></param>, if bound to a property.
/// <param name="tableName"></param>
/// <returns></returns>
public static DataTable ToDataTable(this IEnumerable collection, Func<PropertyInfo, string> columnNameModifier, bool useColumnAttributeName = false, string tableName = null)
public static DataTable ToDataTable(this IEnumerable collection, Func<PropertyInfo, string> columnNameModifier, bool useColumnAttributeName = false, string tableName = null) => ToDataTable(collection, columnNameModifier, null, useColumnAttributeName, tableName);
/// <summary>
/// Converts a typed collection into a <see cref="DataTable"/>. This method excludes the column if <see cref="NotMappedAttribute"/> is bound to a property.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="collection"></param>
/// <param name="columnNameModifier">If a non-null string is returned, the column name is overridden</param>
/// <param name="columnIncludeModifier">If a condition is supplied, only columns matching the condition will be included</param>
/// <param name="useColumnAttributeName">Specifies whether the data column should use the name from <see cref="ColumnAttribute"/></param>, if bound to a property.
/// <param name="tableName"></param>
/// <returns></returns>
public static DataTable ToDataTable(this IEnumerable collection, Func<PropertyInfo, string> columnNameModifier, Func<PropertyInfo, bool> columnIncludeModifier, bool useColumnAttributeName = false, string tableName = null)
{
if (collection == null)
{
@ -116,10 +160,25 @@ namespace BinaryDad.Extensions
var propertyInfo = type
.GetProperties()
.Where(p => !p.HasCustomAttribute<NotMappedAttribute>())
.Where(p =>
{
// automatically ignore [NotMapped] attribute properties
if (p.HasCustomAttribute<NotMappedAttribute>())
{
return false;
}
// allow for custom column inclusion
if (columnIncludeModifier != null)
{
return columnIncludeModifier.Invoke(p);
}
return true;
})
.Select(p =>
{
string columnName = p.Name;
var columnName = p.Name;
string columnTypeName = null;
// set column name to be either the property name
@ -206,6 +265,13 @@ namespace BinaryDad.Extensions
#region Join
/// <summary>
/// Serializes a collection of objects (wrapper for <see cref="string.Join{T}(string, IEnumerable{T})"/>)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items"></param>
/// <param name="separator"></param>
/// <returns></returns>
public static string Join<T>(this IEnumerable<T> items, string separator = "") where T : IComparable
{
if (items == null)
@ -221,6 +287,15 @@ namespace BinaryDad.Extensions
return string.Join(separator, items.ToArray());
}
/// <summary>
/// Serializes a collection of objects with a custom property selector (wrapper for <see cref="string.Join{T}(string, IEnumerable{T})"/>)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TSelector"></typeparam>
/// <param name="items"></param>
/// <param name="selector"></param>
/// <param name="separator"></param>
/// <returns></returns>
public static string Join<T, TSelector>(this IEnumerable<T> items, Func<T, TSelector> selector, string separator = "") where TSelector : IComparable => Join(items.Select(selector), separator);
/// <summary>
@ -537,7 +612,48 @@ namespace BinaryDad.Extensions
}
}
public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(this IEnumerable<KeyValuePair<TKey, TSource>> source, IEqualityComparer<TKey> comparer = null) => source.ToDictionary(k => k.Key, v => v.Value, comparer);
public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(this IEnumerable<KeyValuePair<TKey, TSource>> source) => source.ToDictionary(k => k.Key, v => v.Value);
public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(this IEnumerable<KeyValuePair<TKey, TSource>> source, IEqualityComparer<TKey> comparer) => source.ToDictionary(k => k.Key, v => v.Value, comparer);
public static void Add<TKey, TValue>(this IDictionary<TKey, TValue> source, KeyValuePair<TKey, TValue> value) => source.Add(value.Key, value.Value);
public static T To<T>(this IDictionary<string, object> source) where T : class, new()
{
var instance = new T();
var properties = typeof(T).GetProperties();
foreach (var property in properties)
{
var propertyName = property.Name;
if (source.ContainsKey(propertyName))
{
property.SetValue(instance, source[propertyName]);
}
}
return instance;
}
/// <summary>
/// Creates a query string using a dictionary
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
public static string ToQueryString<T>(this IEnumerable<KeyValuePair<string, T>> source) where T : IConvertible
{
// allows us to get a HttpValueCollection which provides proper serialiation/encoding
var collection = HttpUtility.ParseQueryString(string.Empty);
foreach (var pair in source)
{
collection.Add(pair.Key, pair.Value?.ToString());
}
return collection.ToString();
}
#region Replace

View File

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNet.Mvc" Version="5.2.7" />
<PackageReference Include="Microsoft.AspNetCore.Routing.Abstractions" Version="2.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BinaryDad.Extensions\BinaryDad.Extensions.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,19 @@
using System.Web.Mvc;
namespace Nava.Web.UI
{
public static class ContextExtensions
{
public static T GetViewModel<T>(this ActionExecutedContext context) where T : class
{
var result = context.Result as ViewResultBase;
if (result != null)
{
return result.Model as T;
}
return default;
}
}
}

View File

@ -0,0 +1,35 @@
using System.Collections.Generic;
using System.Collections.Specialized;
namespace Nava.Web.UI
{
public static class RequestExtensions
{
/// <summary>
/// Retrieves the submitted action (string value "submit-action") value from the request. Typically, this is the name of the <button /> and with a required name of "submit-action".
/// NOTE: if IE9 support is not needed, consider using the "formaction" attribute on the button element.
/// </summary>
/// <param name="collection"></param>
/// <param name="action">Name of submitted action value</param>
/// <returns></returns>
public static bool HasSubmitAction(this NameValueCollection collection, string action)
{
var submittedAction = collection["submit-action"];
return submittedAction != null && submittedAction.Equals(action, System.StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// Retrieves form data in a key/value pair collection
/// </summary>
/// <param name="collection"></param>
/// <returns></returns>
public static IEnumerable<KeyValuePair<string, string>> GetParameters(this NameValueCollection collection)
{
foreach (string k in collection.Keys)
{
yield return new KeyValuePair<string, string>(k, collection[k]);
}
}
}
}

View File

@ -0,0 +1,23 @@
using Microsoft.AspNetCore.Routing;
namespace Nava.Web.UI
{
public static class RouteExtensions
{
public static RouteValueDictionary AddCreate(this RouteValueDictionary routeValues, string key, object data)
{
// even if we have no routes, create a route value dictionary with the clientId as a value
if (routeValues == null)
{
routeValues = new RouteValueDictionary();
}
if (!routeValues.ContainsKey(key))
{
routeValues.Add(key, data);
}
return routeValues;
}
}
}

View File

@ -0,0 +1,278 @@
using BinaryDad.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Web.WebPages.Html;
namespace Nava.Web.UI
{
public static class ViewExtensions
{
private const string DateFormat = "{0:M/d/yyyy}";
#region HtmlHelper
//public static object GetRouteValue(this HtmlHelper html, string key) => html.ViewContext.RouteData.Values[key];
#region TextBoxFor<>
public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes, bool enabled) => htmlHelper.TextBoxFor(expression, htmlAttributes.GetPropertyValues(), enabled);
public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes, bool enabled)
{
if (!enabled)
{
htmlAttributes.Add("disabled", "disabled");
}
return htmlHelper.TextBoxFor(expression, htmlAttributes);
}
#endregion
#region TextAreaFor<>
public static MvcHtmlString TextAreaFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes, bool enabled) => htmlHelper.TextAreaFor(expression, htmlAttributes.GetPropertyValues(), enabled);
public static MvcHtmlString TextAreaFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes, bool enabled)
{
if (!enabled)
{
htmlAttributes.Add("disabled", "disabled");
}
return htmlHelper.TextAreaFor(expression, htmlAttributes);
}
#endregion
#region DropDownListFor<>
public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, string optionLabel, object htmlAttributes, bool enabled)
{
var properties = htmlAttributes.GetPropertyValues();
if (!enabled)
{
properties.Add("disabled", "disabled");
}
return htmlHelper.DropDownListFor(expression, selectList, optionLabel, properties);
}
#endregion
#region DropDownList
public static MvcHtmlString DropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList, object htmlAttributes, bool enabled) => htmlHelper.DropDownList(name, selectList, null, htmlAttributes, enabled);
public static MvcHtmlString DropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList, string optionLabel, object htmlAttributes, bool enabled)
{
var properties = htmlAttributes.GetPropertyValues();
if (!enabled)
{
properties.Add("disabled", "disabled");
}
return htmlHelper.DropDownList(name, selectList, optionLabel, properties);
}
#endregion
#region CheckBox
public static MvcHtmlString CheckBox(this HtmlHelper htmlHelper, string name, bool isChecked, object htmlAttributes, bool enabled)
{
var properties = htmlAttributes.GetPropertyValues();
if (!enabled)
{
properties.Add("disabled", "disabled");
}
return htmlHelper.CheckBox(name, isChecked, properties);
}
#endregion
#region DateBoxFor<>
public static MvcHtmlString DateBoxFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression) => html.DateBoxFor(expression, null);
public static MvcHtmlString DateBoxFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, bool enabled) => html.DateBoxFor(expression, null, enabled);
public static MvcHtmlString DateBoxFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, object htmlAttributes) => html.DateBoxFor(expression, htmlAttributes, true);
public static MvcHtmlString DateBoxFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, object htmlAttributes, bool enabled)
{
var properties = htmlAttributes.GetPropertyValues() ?? new Dictionary<string, object>();
UpdateDateBoxProperties(properties, enabled);
return html.TextBoxFor(expression, DateFormat, properties);
}
public static MvcHtmlString DateBox(this HtmlHelper html, string name, object value) => html.DateBox(name, value, null);
public static MvcHtmlString DateBox(this HtmlHelper html, string name, object value, object htmlAttributes) => html.DateBox(name, value, htmlAttributes, true);
public static MvcHtmlString DateBox(this HtmlHelper html, string name, object value, object htmlAttributes, bool enabled)
{
var properties = htmlAttributes.GetPropertyValues() ?? new Dictionary<string, object>();
UpdateDateBoxProperties(properties, enabled);
return html.TextBox(name, value, DateFormat, properties);
}
#endregion
#region EnumDropDownListFor<>
public static MvcHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression, string optionLabel, object htmlAttributes, bool useDescription)
{
// shouldnt happen here, but just in case
if (!useDescription)
{
return htmlHelper.EnumDropDownListFor(expression, optionLabel, htmlAttributes);
}
var type = typeof(TEnum);
if (type.IsNullable())
{
type = Nullable.GetUnderlyingType(type);
}
var values = Enum.GetValues(type)
.Cast<TEnum>()
.Select(v => new
{
Value = Convert.ToInt32(v),
Text = (v as Enum).GetDescription()
})
.ToList();
var list = new SelectList(values, "Value", "Text");
return htmlHelper.DropDownListFor(expression, list, optionLabel, htmlAttributes);
}
#endregion
#region ModalActionLink
public static MvcHtmlString ModalActionLink(this HtmlHelper html, string linkText, string actionName, object routeValues, string dataTarget) => html.ModalActionLink(linkText, actionName, routeValues, dataTarget, null);
public static MvcHtmlString ModalActionLink(this HtmlHelper html, string linkText, string actionName, object routeValues, string dataTarget, object htmlAttributes) => html.ModalActionLink(linkText, actionName, null, routeValues, dataTarget, htmlAttributes);
public static MvcHtmlString ModalActionLink(this HtmlHelper html, string linkText, string actionName, string controllerName, object routeValues, string dataTarget) => html.ModalActionLink(linkText, actionName, controllerName, routeValues, dataTarget, null);
public static MvcHtmlString ModalActionLink(this HtmlHelper html, string linkText, string actionName, string controllerName, object routeValues, string dataTarget, object htmlAttributes) => html.ModalActionLink(linkText, actionName, controllerName, routeValues, dataTarget, htmlAttributes, true);
public static MvcHtmlString ModalActionLink(this HtmlHelper html, string linkText, string actionName, string controllerName, object routeValues, string dataTarget, object htmlAttributes, bool enabled)
{
var properties = htmlAttributes.GetPropertyValues() ?? new Dictionary<string, object>();
MergeProperties(properties, GetModalProperties(dataTarget));
if (!enabled)
{
properties.Add("disabled", "disabled");
}
return html.ActionLink(linkText, actionName, controllerName, new RouteValueDictionary(routeValues), properties);
}
#endregion
#region ModalLink
public static MvcHtmlString ModalLink(this HtmlHelper html, string linkText, string url, string dataTarget, object htmlAttributes)
{
var properties = htmlAttributes.GetPropertyValues() ?? new Dictionary<string, object>();
var builder = new TagBuilder("a");
builder.MergeAttributes(properties);
builder.MergeAttributes(GetModalProperties(dataTarget).ToDictionary());
builder.MergeAttribute("href", url);
builder.SetInnerText(linkText);
return MvcHtmlString.Create(builder.ToString());
}
#endregion
#endregion
#region UrlHelper
public static string Action(this UrlHelper url, string actionName, string controllerName, object routeValues, string returnUrl, string returnTitle)
{
var values = new RouteValueDictionary(routeValues)
.AddCreate("returnUrl", returnUrl)
.AddCreate("returnTitle", returnTitle);
return url.Action(actionName, controllerName, values);
}
#endregion
#region ViewData
public static T Get<T>(this ViewDataDictionary viewData, string key) where T : class => viewData[key] as T;
#endregion
#region Private methods
private static IEnumerable<KeyValuePair<string, object>> GetModalProperties(string dataTarget)
{
return new Dictionary<string, object>
{
{ "data-toggle", "modal" },
{ "data-target", dataTarget }
};
}
private static void MergeProperties(IDictionary<string, object> source, IEnumerable<KeyValuePair<string, object>> merge)
{
if (source != null && merge != null)
{
merge.ForEach(m =>
{
if (source.ContainsKey(m.Key))
{
source[m.Key] += String.Format(" {0}", m.Value.ToString());
}
else
{
source.Add(m.Key, m.Value);
}
});
}
}
private static void UpdateDateBoxProperties(IDictionary<string, object> properties, bool enabled)
{
MergeProperties(properties, new Dictionary<string, object>
{
{ "class", "datepicker form-control enrollmentdate edit-not-allowed" },
{ "data-provide", "datepicker" },
{ "data-date-format", "m/d/yyyy" },
{ "readonly", "readonly" } // this also hides the keyboard in iOS
});
if (!enabled)
{
properties.Add("disabled", "disabled");
}
}
#endregion
}
}