I have a data model with a dozen properties, say value1, value2, value3 (actually they have meaningful names but that's not important here).
In my display, I need to do the following for each value:
#if (Model.Value1 >= 2)
{
<div class="col-sm-6 test-box-item">
<h5>#Html.DisplayNameFor(_ => Model.Value1)</h5>
<div>#Model.Value1</div>
</div>
}
This for a dozen of them.
I wanted to create a #function or #helper and simplify the code to something like:
DisplayValue(_ => Model.Value1)
DisplayValue(_ => Model.Value2)
...
but couldn't figure out how to do that.
Any idea? I guess I need a function that accepts an Expression but I really don't know how to code that.
check this helper
public static class HmtlExtensions
{
public static HtmlString DisplayValue<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression)
{
if (expression == null)
throw (new ArgumentNullException("expression"));
var result = new StringBuilder();
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
// get value
var _value = metadata.Model.ToString();
int value = 0;
if (int.TryParse(_value, out value) && value >= 2)
{
result.Append("<div class=\"col-sm-6 test-box-item\"><h5>");
result.Append(helper.DisplayNameFor(expression));
result.Append("</h5><div>");
result.Append(_value);
result.Append("</div></div>");
}
return MvcHtmlString.Create(result.ToString());
}
}
and in your view use it like
#Html.DisplayValue(x => x.Value1)
#Html.DisplayValue(x => x.Value2)
#Html.DisplayValue(x => x.Value3)
Related
I try to call a scalar function stored in my DB.
Here is my code:
public class PronosticDbContext : DbContext
{
public PronosticDbContext(DbContextOptions<PronosticDbContext> options) : base(options)
{
}
[DbFunction(FunctionName = "GetPlayerPointsForDay", Schema = "dbo")]
public static int GetPlayerPointsForDay(int dayId, int userId) => throw new NotSupportedException();
}
The call :
private IQueryable<PlayerRankingData> GetPlayerRanking(int? dayId)
{
return Context.Days.Where(x => x.Id == dayId.Value).Select(x => new PlayerRankingData
{
// Some properties
Users = x.Season.Users.Select(us => new PlayerRankingData.User
{
// Other properties
Last = PronosticDbContext.GetPlayerPointsForDay(x.Id, us.User.Id),
// Others again
}).OrderByDescending(u => u.Points).ThenByDescending(u => u.Last).ThenBy(u => u.Login)
});
}
I don't understand why but I always go to the NotSupportedException, my scalar function is never called. Why did I miss ?
I also tried like this, but same result...
modelBuilder
.HasDbFunction(typeof(PronosticDbContext).GetMethod(nameof(GetPlayerPointsForDay)))
.HasSchema("dbo")
.HasName(nameof(GetPlayerPointsForDay));
Ok, I got it.
The thing is you can't call the scalar function directly (I don't know why), it must be encapsulate in a linq query, like this :
Last = Context.Users.Select(u => PronosticDbContext.GetPlayerPointsForDay(x.Id, us.User.Id)).FirstOrDefault()
And that's all. No need to declaration in the DbContext, this is enough :
[DbFunction]
public static int? GetPlayerPointsForDay(int dayId, int userId) => throw new NotSupportedException();
See the following code:
var source = Peoples.AsQueryable();
IQueryable<People> p;
p = source.Where( x => false);
p = p.Concat(source.Where( x => x.FirstName == "DAVID"));
p = p.Concat(source.Where(x => x.City_id == 3));
p.Dump(); //I use linqpad but it does not matter now
this code work, and linq2sql product one sql-statement (with three qouery and UNION ALL).
now the same code, ostensibly exactly the same procedure, in another variation, throw NotSupportedException:
var q = new IQueryableUnderstand<People>(Peoples.AsQueryable());
q.AddToList(x => x.City_id == 3);
q.AddToList(x => x.FirstName == "Dave");
q.Results().Dump();
IQueryableUnderstand Class:
class IQueryableUnderstand<T>
{
IQueryable<T> _source;
IQueryable<T> combin;
public IQueryableUnderstand(IQueryable<T> source)
{
_source = source;
combin = _source.Where(s => false);
}
public void AddToList(Func<T, bool> predicate) => combin = combin.Concat(_source.Where(predicate));
public IQueryable<T> Results() => combin;
}
LINQ To SQL exception: Local sequence cannot be used in LINQ to SQL implementation of query operators except the Contains operator
I feel stupid, what's the difference?
Your AddToList() method accepts a Func<T, bool> predicate. The parameter type is incorrect. If applied to your query, it will convert it to a Linq-To-Objects query. IQueryable<TSource> expects expressions, not delegates.
Change your method signature:
public void AddToList(Expression<Func<T, bool>> predicate) =>
combin = combin.Concat(_source.Where(predicate));
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();
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.
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;
}