Using EF to query against a compile-time-unknown property - entity-framework-4.1

Is there a way in EntityFramework (and the resulting LINQ) to query against a property of an entity that is not hard-coded?
Let's say, something that can be used for a search function.
public IList<Entity> Search (string propertyName, object value) {
// something that'll do the following
return context.Set<Entity>()
.Where(x => x.propertyName == value)
.ToList()
;
}

How about Property Descriptor?
The following code seems to do what you require:
string propertyName = "Length";
List<string> testList = new List<string>();
testList.Add("String1");
testList.Add("String10");
testList.Add("String100");
testList.Add("String1000");
System.ComponentModel.PropertyDescriptorCollection props = System.ComponentModel.TypeDescriptor.GetProperties(typeof(string));
System.ComponentModel.PropertyDescriptor desc = props.Find(propertyName, false);
IEnumerable<object> obj = from env in testList
select desc.GetValue(env);
foreach (object it in obj)
{
Console.WriteLine(it.ToString());
}

You can build equals expression manually like this
private static Expression<Func<TEntity, bool>> BuildEqualExpression<TEntity>(string propertyName, object value)
{
var param = Expression.Parameter(typeof(TEntity), "x");
var body = Expression.MakeBinary(ExpressionType.Equal,
Expression.Property(param, propertyName), Expression.Constant(value));
return Expression.Lambda<Func<TEntity, bool>>(body, new ParameterExpression[] { param });
}
and then use it in your LINQ query
var expression = BuildEqualExpression<TEntity>(propertyName, value);
return context.Set<TEntity>().Where(expression).ToList();

Related

Disable sorting fields value alphabetically

I am using Spring Boot (v2.1.3 RELEASE) and SpringDoc. I already went through https://springdoc.org/springdoc-properties.html and https://springdoc.org/, but it looks like SpringDoc is automatically sorting the parameters alphabetically. How can we prevent this?
#Operation(summary = "Find Students")
#Parameter(in=ParameterIn.QUERY, name="page", description="Results page you want to retrieve (0..N)", schema=#Schema(defaultValue = 0))
#Parameter(in=ParameterIn.QUERY, name="size", description="Number of records per page.", schema=#Schema(defaultValue =50))
#Parameter(in=ParameterIn.QUERY, name="search_type", description=AppConstants.SEARCH_TYPE, schema=#Schema(allowableValues= {"Starts", "Contains"},defaultValue = "Starts"))
#ApiCountryHeader
#GetMapping(value = "/students")
public ResponseEntity<List<Students>> findStudentss(
#Parameter(description = "") #RequestParam(required = false) String studentCd,
#Parameter(description = "") #RequestParam(required = false) String studentName,
#Parameter(hidden=true) String search_type){
....
....
...
return new ResponseEntity<>(studentts, HttpStatus.OK);
}
Fields are not sorted alphabetically, but the order of declaration is preserved.
You can change the fields order, using the different available customizers:
OpenApiCustomiser: To customize the OpenAPI object
OperationCustomizer: To customize an operation based on the HandlerMethod
For example:
#RestController
public class HelloController {
#GetMapping(value = "/persons")
public String getPerson(String b, String a) {
return null;
}
#Bean
OperationCustomizer operationCustomizer() {
return (Operation operation, HandlerMethod handlerMethod) -> {
if ("getPerson".equals(handlerMethod.getMethod().getName())) {
List<Parameter> parameterList = operation.getParameters();
if (!CollectionUtils.isEmpty(parameterList))
Collections.reverse(parameterList);
}
return operation;
};
}
}

Binding an Enum to a DropDownList in MVC 4? [duplicate]

This question already has answers here:
How do you create a dropdownlist from an enum in ASP.NET MVC?
(36 answers)
Closed 8 years ago.
I've been finding all over the place that the common way to bind Enums to DropDowns is through helper methods, which seems a bit overbearing for such a seemingly simple task.
What is the best way to bind Enums to DropDownLists in ASP.Net MVC 4?
You can to this:
#Html.DropDownListFor(model => model.Type, Enum.GetNames(typeof(Rewards.Models.PropertyType)).Select(e => new SelectListItem { Text = e }))
I think it is about the only (clean) way, which is a pity, but at least there are a few options out there. I'd recommend having a look at this blog: http://paulthecyclist.com/2013/05/24/enum-dropdown/
Sorry, it's too long to copy here, but the gist is that he created a new HTML helper method for this.
All the source code is available on GitHub.
Enums are supported by the framework since MVC 5.1:
#Html.EnumDropDownListFor(m => m.Palette)
Displayed text can be customized:
public enum Palette
{
[Display(Name = "Black & White")]
BlackAndWhite,
Colour
}
MSDN link: http://www.asp.net/mvc/overview/releases/mvc51-release-notes#Enum
In my Controller:
var feedTypeList = new Dictionary<short, string>();
foreach (var item in Enum.GetValues(typeof(FeedType)))
{
feedTypeList.Add((short)item, Enum.GetName(typeof(FeedType), item));
}
ViewBag.FeedTypeList = new SelectList(feedTypeList, "Key", "Value", feed.FeedType);
In my View:
#Html.DropDownList("FeedType", (SelectList)ViewBag.FeedTypeList)
The solution from PaulTheCyclist is spot on. But I wouldn't use RESX (I'd have to add a new .resx file for each new enum??)
Here is my HtmlHelper Expression:
public static MvcHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TEnum>> expression, object attributes = null)
{
//Get metadata from enum
var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
var enumType = GetNonNullableModelType(metadata);
var values = Enum.GetValues(enumType).Cast<TEnum>();
//Convert enumeration items into SelectListItems
var items =
from value in values
select new SelectListItem
{
Text = value.ToDescription(),
Value = value.ToString(),
Selected = value.Equals(metadata.Model)
};
//Check for nullable value types
if (metadata.IsNullableValueType)
{
var emptyItem = new List<SelectListItem>
{
new SelectListItem {Text = string.Empty, Value = string.Empty}
};
items = emptyItem.Concat(items);
}
//Return the regular DropDownlist helper
return htmlHelper.DropDownListFor(expression, items, attributes);
}
Here is how I declare my enums:
[Flags]
public enum LoanApplicationType
{
[Description("Undefined")]
Undefined = 0,
[Description("Personal Loan")]
PersonalLoan = 1,
[Description("Mortgage Loan")]
MortgageLoan = 2,
[Description("Vehicle Loan")]
VehicleLoan = 4,
[Description("Small Business")]
SmallBusiness = 8,
}
And here is the call from a Razor View:
<div class="control-group span2">
<div class="controls">
#Html.EnumDropDownListFor(m => m.LoanType, new { #class = "span2" })
</div>
</div>
Where #Model.LoanType is an model property of the LoanApplicationType type
UPDATE: Sorry, forgot to include code for the helper function ToDescription()
/// <summary>
/// Returns Description Attribute information for an Enum value
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string ToDescription(this Enum value)
{
if (value == null)
{
return string.Empty;
}
var attributes = (DescriptionAttribute[]) value.GetType().GetField(
Convert.ToString(value)).GetCustomAttributes(typeof (DescriptionAttribute), false);
return attributes.Length > 0 ? attributes[0].Description : Convert.ToString(value);
}
Technically, you don't need a helper method, since Html.DropdownListFor only requires a SelectList or Ienumerable<SelectListItem>. You can just turn your enums into such an output and feed it in that way.
I use a static library method to convert enums into List<SelectListItem> with a few params/options:
public static List<SelectListItem> GetEnumsByType<T>(bool useFriendlyName = false, List<T> exclude = null,
List<T> eachSelected = null, bool useIntValue = true) where T : struct, IConvertible
{
var enumList = from enumItem in EnumUtil.GetEnumValuesFor<T>()
where (exclude == null || !exclude.Contains(enumItem))
select enumItem;
var list = new List<SelectListItem>();
foreach (var item in enumList)
{
var selItem = new SelectListItem();
selItem.Text = (useFriendlyName) ? item.ToFriendlyString() : item.ToString();
selItem.Value = (useIntValue) ? item.To<int>().ToString() : item.ToString();
if (eachSelected != null && eachSelected.Contains(item))
selItem.Selected = true;
list.Add(selItem);
}
return list;
}
public static class EnumUtil
{
public static IEnumerable<T> GetEnumValuesFor<T>()
{
return Enum.GetValues(typeof(T)).Cast<T>();
}
// other stuff in here too...
}
/// <summary>
/// Turns Camelcase or underscore separated phrases into properly spaces phrases
/// "DogWithMustard".ToFriendlyString() == "Dog With Mustard"
/// </summary>
public static string ToFriendlyString(this object o)
{
var s = o.ToString();
s = s.Replace("__", " / ").Replace("_", " ");
char[] origArray = s.ToCharArray();
List<char> newCharList = new List<char>();
for (int i = 0; i < origArray.Count(); i++)
{
if (origArray[i].ToString() == origArray[i].ToString().ToUpper())
{
newCharList.Add(' ');
}
newCharList.Add(origArray[i]);
}
s = new string(newCharList.ToArray()).TrimStart();
return s;
}
Your ViewModel can pass in the options you want. Here's a fairly complex one:
public IEnumerable<SelectListItem> PaymentMethodChoices
{
get
{
var exclusions = new List<Membership.Payment.PaymentMethod> { Membership.Payment.PaymentMethod.Unknown, Membership.Payment.PaymentMethod.Reversal };
var selected = new List<Membership.Payment.PaymentMethod> { this.SelectedPaymentMethod };
return GetEnumsByType<Membership.Payment.PaymentMethod>(useFriendlyName: true, exclude: exclusions, eachSelected: selected);
}
}
So you wire your View's DropDownList against that IEnumerable<SelectListItem> property.
Extending the html helper to do it works well, but if you'd like to be able to change the text of the drop down values based on DisplayAttribute mappings, then you would need to modify it similar to this,
(Do this pre MVC 5.1, it's included in 5.1+)
public static IHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> html, Expression<Func<TModel, TEnum>> expression)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
var enumType = Nullable.GetUnderlyingType(metadata.ModelType) ?? metadata.ModelType;
var enumValues = Enum.GetValues(enumType).Cast<object>();
var items = enumValues.Select(item =>
{
var type = item.GetType();
var member = type.GetMember(item.ToString());
var attribute = member[0].GetCustomAttribute<DisplayAttribute>();
string text = attribute != null ? ((DisplayAttribute)attribute).Name : item.ToString();
string value = ((int)item).ToString();
bool selected = item.Equals(metadata.Model);
return new SelectListItem
{
Text = text,
Value = value,
Selected = selected
};
});
return html.DropDownListFor(expression, items, string.Empty, null);
}

ActionScript - Determine If Value is Class Constant

i'd like to throw an argument error if a particular function doesn't work without a passed value that also happens to be a public constant of the class containing the function.
is there anyway to determine if a class owns a public constant instead of having to iterate thru all of them?
something like this:
public static const HALIFAX:String = "halifax";
public static const MONTREAL:String = "montreal";
public static const TORONTO:String = "toronto";
private var cityProperty:String;
public function set city(value:String):void
{
if (!this.hasConstant(value))
throw new ArgumentError("set city value is not applicable.");
cityProperty = value;
}
public function get city():Strig
{
return cityProperty;
}
currently, for this functionality i have to write the city setter function like this:
public function set city(value:String):void
{
if (value != HALIFAX && value != MONTREAL && value != TORONTO)
throw new ArgumentError("set city value is not applicable.");
cityProperty = value;
}
is this the only way to accomplish this task?
Yes, if you use reflections:
private var type:Class;
private var description:XML;
private function hasConstant (str : String ) : Boolean
{
if (description == null)
{
type = getDefinitionByName (getQualifiedClassName (this)) as Class;
description = describeType (type);
}
for each ( var constant:XML in description.constant)
{
if (type[constant.#name] == str) return true;
}
return false;
}
Note that for this to work, all constants must always be String objects declared public static const.
I was looking for an answer to this question myself and found it annoying that hasOwnProperty() did not work for static properties. Turns out though, that if you cast your class to a Class object, it does work.
Here's an example:
public final class DisplayMode
{
public static const one: String = "one";
public static const two: String = "two";
public static const three: String = "three";
public static function isValid(aDisplayMode: String): Boolean {
return Class(DisplayMode).hasOwnProperty(aDisplayMode);
}
}
I owe this solution to jimmy5804 from this discussion, so hats off to him.
You should be able to use bracket notation to do this. For example:
var foo:Sprite = new Sprite();
foo.rotation = 20;
trace( foo["x"], foo["rotation"]); // traces "0 20"
or more specific to your case:
var bar:String = "rotation";
trace( foo[bar] ); // traces "20"
The only thing you have to look out for here, is that the bracket accessor will throw a ReferenceError if you ask for an object property that isn't there, such as:
trace ( foo["cat"] ); // throws ReferenceError
But it will not throw if you are asking for a static property:
trace ( Sprite["cat"] ); // traces "undefined"
So in your case you might try:
if ( this[value] == undefined ) {
throw new ArgumentError("set city value is not applicable.");
}
EDIT:
Sorry, I was confusing the const's names with their values.
For this to work on your problem you would have to make the String value the same as the const's name, so for example:
public static const HALIFAX:String = "HALIFAX";
then you could use the query as described above and it would give you the desired result.

Performing dynamic sorts on EF4 data

I'm attempting to perform dynamic sorting of data that I'm putting into grids into our MVC UI. Since MVC is abstracted from everything else via WCF, I've created a couple utility classes and extensions to help with this. The two most important things (slightly simplified) are as follows:
public static IQueryable<TModel> ApplySortOptions<TModel, TProperty>(this IQueryable<TModel> collection, IEnumerable<ISortOption<TModel, TProperty>> sortOptions) where TModel : class
{
var sortedSortOptions = (from o in sortOptions
orderby o.Priority ascending
select o).ToList();
var results = collection;
foreach (var option in sortedSortOptions)
{
var currentOption = option;
var propertyName = currentOption.Property.MemberWithoutInstance();
var isAscending = currentOption.IsAscending;
if (isAscending)
{
results = from r in results
orderby propertyName ascending
select r;
}
else
{
results = from r in results
orderby propertyName descending
select r;
}
}
return results;
}
public interface ISortOption<TModel, TProperty> where TModel : class
{
Expression<Func<TModel, TProperty>> Property { get; set; }
bool IsAscending { get; set; }
int Priority { get; set; }
}
I've not given you the implementation for MemberWithoutInstance() but just trust me in that it returns the name of the property as a string. :-)
Following is an example of how I would consume this (using a non-interesting, basic implementation of ISortOption<TModel, TProperty>):
var query = from b in CurrentContext.Businesses
select b;
var sortOptions = new List<ISortOption<Business, object>>
{
new SortOption<Business, object>
{
Property = (x => x.Name),
IsAscending = true,
Priority = 0
}
};
var results = query.ApplySortOptions(sortOptions);
As I discovered with this question, the problem is specific to my orderby propertyName ascending and orderby propertyName descending lines (everything else works great as far as I can tell). How can I do this in a dynamic/generic way that works properly?
You should really look at using Dynamic LINQ for this. In fact, you may opt to simply list the properties by name instead of using an expression, making it somewhat easier to construct.
public static IQueryable<T> ApplySortOptions<T, TModel, TProperty>(this IQueryable<T> collection, IEnumerable<ISortOption<TModel, TProperty>> sortOptions) where TModel : class
{
var results = collection;
foreach (var option in sortOptions.OrderBy( o => o.Priority ))
{
var currentOption = option;
var propertyName = currentOption.Property.MemberWithoutInstance();
var isAscending = currentOption.IsAscending;
results = results.OrderBy( string.Format( "{0}{1}", propertyName, !isAscending ? " desc" : null ) );
}
return results;
}
While I think #tvanfosson's solution will function perfectly, I'm also looking into this possibility:
/// <summary>
/// This extension method is used to help us apply ISortOptions to an IQueryable.
/// </summary>
/// <param name="collection">This is the IQueryable you wish to apply the ISortOptions to.</param>
/// <param name="sortOptions">These are the ISortOptions you wish to have applied. You must specify at least one ISortOption (otherwise, don't call this method).</param>
/// <returns>This returns an IQueryable object.</returns>
/// <remarks>This extension method should honor deferred execution on the IQueryable that is passed in.</remarks>
public static IOrderedQueryable<TModel> ApplySortOptions<TModel, TProperty>(this IQueryable<TModel> collection, IEnumerable<ISortOption<TModel, TProperty>> sortOptions) where TModel : class
{
Debug.Assert(sortOptions != null, "ApplySortOptions cannot accept a null sortOptions input.");
Debug.Assert(sortOptions.Count() > 0, "At least one sort order must be specified to ApplySortOptions' sortOptions input.");
var firstSortOption = sortOptions.OrderBy(o => o.Priority).First();
var propertyName = firstSortOption.Property.MemberWithoutInstance();
var isAscending = firstSortOption.IsAscending;
// Perform the first sort action
var results = isAscending ? collection.OrderBy(propertyName) : collection.OrderByDescending(propertyName);
// Loop through all of the rest ISortOptions
foreach (var sortOption in sortOptions.OrderBy(o => o.Priority).Skip(1))
{
// Make a copy of this or our deferred execution will bite us later.
var currentOption = sortOption;
propertyName = currentOption.Property.MemberWithoutInstance();
isAscending = currentOption.IsAscending;
// Perform the additional orderings.
results = isAscending ? results.ThenBy(propertyName) : results.ThenByDescending(propertyName);
}
return results;
}
using the code from this question's answer:
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
{
return ApplyOrder(source, property, "OrderBy");
}
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
{
return ApplyOrder(source, property, "OrderByDescending");
}
public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder(source, property, "ThenBy");
}
public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder(source, property, "ThenByDescending");
}
private static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName)
{
var props = property.Split('.');
var type = typeof (T);
var arg = Expression.Parameter(type, "x");
Expression expr = arg;
foreach (var prop in props)
{
// use reflection (not ComponentModel) to mirror LINQ
var pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
var delegateType = typeof (Func<,>).MakeGenericType(typeof (T), type);
var lambda = Expression.Lambda(delegateType, expr, arg);
var result = typeof (Queryable).GetMethods().Single(
method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(typeof (T), type)
.Invoke(null, new object[] {source, lambda});
return (IOrderedQueryable<T>) result;
}

No supported translation to SQL

We have this code:
private IList<InfoRequest> GetBy(Func<InformationRequest, string> func, string searchby)
{
var requests = _dc.InformationRequests
.Where(x => func.Invoke(x).Contains(searchby))
.OrderBy(y => y.RequestDate);
return Mapper.Map<InformationRequest[], InfoRequest[]>(requests.ToArray());
}
It continues to throw the no supported translation to SQL error. Any ideas on the problem or how to resolve it?
I would take the advice found in this link, Convert your func to an expression and that will result in a differnt overloaded .Where method getting called.
private IList<InfoRequest> GetBy(Expression<Func<InformationRequest, string>> exp, string searchby)
{
var requests = _dc.InformationRequests
.Where(x => exp(x).Contains(searchby))
I ended up with this:
private static Expression<Func<T, bool>> StartsWith<T>(Func<string, string> func)
{
var searchBy = func.Method.GetParameters()[0].Name;
var search = Expression.Constant(func(null), typeof(string));
var searchByParam = Expression.Parameter(typeof(T), searchBy);
var searchByExp = Expression.Property(searchByParam, searchBy);
var methodInfo = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });//, typeof(StringComparison)});
var containsExpression = Expression.Call(searchByExp, methodInfo, search);
return Expression.Lambda<Func<T, bool>>(containsExpression, searchByParam);
}
If you want more details, I blogged it here: http://derans.blogspot.com/2010/05/building-l2s-expression-with-net-35.html