Working with EPiServer Find and trying to build a generic facet funcionality for it to simplify managing which facet should be enabled. I would like to construct two generic methods, one for adding active filters to perform the hits search and one to perform the available facet filters remaining.
The first method will perform the following (specific code for brand filter):
var brandFilter = client.BuildFilter<FashionProduct>();
foreach (var facet in SelectedGroup.Facets.Where(x => x.Selected))
{
brandFilter = brandFilter.Or(x => x.Brand.Match(facet.Key));
}
query = query.Filter(brandFilter);
I would like to be able to call it in a generic way so I could base the available facets on some simple list of strings or objects. Like this:
query = AddFilterFacet<FashionProduct>(query, "Brand", SelectedGroup.Facets)
So the method would take the type of object to filter on, the query to append the filters on, the name of the property to filter on and the list of values to add.
The second method is similar but relates more to perform the following:
facetQuery = facetQuery.TermsFacetFor(x => x.Brand)
...
var brandFacets = facetResult.TermsFacetFor(x => x.Brand).Terms;
Is it possible to build this kind of functionality? The biggest questionmark I have is how to translate the "Brand" input string to be the Brand Property in x => x.Brand
private void AddFilterFacet<T>(IClient client, ref ITypeSearch<T> query, string propertyName, List<FacetOption> facets)
{
var filter = client.BuildFilter<T>();
foreach (var facet in facets)
{
filter = filter.Or(x => x.????.Match(facet.Key));
}
query = query.Filter(filter);
}
The .Or method takes a
System.Linq.Expressions.Expression<Func<T, Find.Api.Querying.Filter>>
so perhaps something can be used to make a proper generic call to it
It's definitely possible to create generic lambda expressions, it's just not easy and requires a lot of reflection code.
I haven't done it in a while, but maybe if you look at the code i created for something similar a while ago (Generic lambda expressions) it'll help. I'm sure someone with fresher experience will help you out here soon enough.
Decimal precision attribute <-- take a look a this answer witch has code to genereate modelBuilder.Entity<CLASS>().Property(OBJECT=> OBJECT.PROPERTY).HasPrecision(12, 10) automatically from an attribute in a class
Related
Trying to write a piece of code that is dynamic in that it can accept any number of possible model definitions.
Current hard code is:
var items = _context.Events.ToList();
foreach (var item in items)
{
(...)
}
What I would like to do is to make the _context.Events.ToList(); be more like _context.{variable that holds model name}.ToList();
Something like:
var modelName = "Table1"
var items = _context.modelName.ToList();
foreach (var item in items)
{
(...)
}
I thought about declaring items as a generic variable, that way it was available to the entire method even if set inside an if or switch, but no idea on what to declare it as.
Is something like this possible?
Try this :
var table = (ITable)context.GetType()
.GetProperty(modelName)
.GetValue(context, null);
I hope be helpful :)
Entity Framework has a generic Set<TEntity> accessor, but the type must be known at compile-time:
var foo = _context.Set<Foo>();
If you have the type as a string variable, though, your options are extremely limited. You can technically use reflection to get at the right DbSet, but you're going to lose the generic IQueryable<TEntity> interface, and you'll be stuck with the much more limited IQueryable interface, and by "much more limited", I mean you basically can't do anything but materialize the set.
If you want to type it via a string variable, but still have at least some querying ability, you'll need to employ a base class that's shared between all the entity types you'd want to use in this way. For example, if you have different "event" types, and you can make them all inherit from Event, then you could do something like:
MethodInfo method = typeof(Queryable).GetMethod("OfType");
MethodInfo generic = method.MakeGenericMethod(new Type[] { eventType });
var set = (IQueryable<Event>)generic.Invoke(null, new object[] { _context.Events });
If your eventType was "Film", for example, that would effectively give you the same queryset as something like _context.Set<Film>() (where Film would be a derived class of Event). You could then utilize your normal LINQ query functionality like Where, Select, etc. on set.
I sum myself to the hapless lot that fumbles with custom methods in LINQ to EF queries. I've skimmed the web trying to detect a pattern to what makes a custom method LINQ-friendly, and while every source says that the method must be translatable into a T-SQL query, the applications seem very diverse. So, I'll post my code here and hopefully a generous SO denizen can tell me what I'm doing wrong and why.
The Code
public IEnumerable<WordIndexModel> GetWordIndex(int transid)
{
return (from trindex in context.transIndexes
let trueWord = IsWord(trindex)
join trans in context.Transcripts on trindex.transLineUID equals trans.UID
group new { trindex, trans } by new { TrueWord = trueWord, trindex.transID } into grouped
orderby grouped.Key.word
where grouped.Key.transID == transid
select new WordIndexModel
{
Word = TrueWord,
Instances = grouped.Select(test => test.trans).Distinct()
});
}
public string IsWord(transIndex trindex)
{
Match m = Regex.Match(trindex.word, #"^[a-z]+(\w*[-]*)*",
RegexOptions.IgnoreCase);
return m.Value;
}
With the above code I access a table, transIndex that is essentially a word index of culled from various user documents. The problem is that not all entries are actually words. Nubers, and even underscore lines, such as, ___________,, are saved as well.
The Problem
I'd like to keep only the words that my custom method IsWord returns (at the present time I have not actually developed the parsing mechanism). But as the IsWord function shows it will return a string.
So, using let I introduce my custom method into the query and use it as a grouping parameter, the is selectable into my object. Upon execution I get the omninous:
LINQ to Entities does not recognize the method
'System.String IsWord(transIndex)' method, and this
method cannot be translated into a store expression."
I also need to make sure that only records that match the IsWord condition are returned.
Any ideas?
It is saying it does not understand your IsWord method in terms of how to translate it to SQL.
Frankly it does not do much anyway, why not replace it with
return (from trindex in context.transIndexes
let trueWord = trindex.word
join trans in context.Transcripts on trindex.transLineUID equals trans.UID
group new { trindex, trans } by new { TrueWord = trueWord, trindex.transID } into grouped
orderby grouped.Key.word
where grouped.Key.transID == transid
select new WordIndexModel
{
Word = TrueWord,
Instances = grouped.Select(test => test.trans).Distinct()
});
What methods can EF translate into SQL, i can't give you a list, but it can never translate a straight forward method you have written. But their are some built in ones that it understands, like MyArray.Contains(x) for example, it can turn this into something like
...
WHERE Field IN (ArrItem1,ArrItem2,ArrItem3)
If you want to write a linq compatible method then you need to create an expresion tree that EF can understand and turn into SQL.
This is where things star to bend my mind a little but this article may help http://blogs.msdn.com/b/csharpfaq/archive/2009/09/14/generating-dynamic-methods-with-expression-trees-in-visual-studio-2010.aspx.
If the percentage of bad records in return is not large, you could consider enumerate the result set first, and then apply the processing / filtering?
var query = (from trindex in context.transIndexes
...
select new WordIndexModel
{
Word,
Instances = grouped.Select(test => test.trans).Distinct()
});
var result = query.ToList().Where(word => IsTrueWord(word));
return result;
If the number of records is too high to enumerate, consider doing the check in a view or stored procedure. That will help with speed and keep the code clean.
But of course, using stored procedures has disadvatages of reusability and maintainbility (because of no refactoring tools).
Also, check out another answer which seems to be similar to this one: https://stackoverflow.com/a/10485624/3481183
We are using Xamarin with SQLiteNet as ORM.
In our data layer class we have the method below.
filter = ri => ri.ItemVersioniId == itemVersionId;
The method is getting the records matching the Id. If the lambda expression is hardcoded, instead of using the "filter" parameter it is much faster... even though it is the same logic.
We would to be able to pass the filter as a parameter but still get a good performance. Any advise?
public virtual List<ResourceItem> GetResourceItems (string itemVersionId, Func<ResourceItem,bool> filter ){
//var t = db.Table<ResourceItem> ().Where (ri => ri.ItemVersionId == itemVersionId); --* this line is 10 times faster
var t = db.Table<ResourceItem> ().Where (filter); --* this line is 10 times slower
return new List<ResourceItem> (t);
}
I'm not sure because it is xamarin specific, but i suggest to use Expression instead of Func.
Expression<Func<ResourceItem,bool>> filter =
ri => ri.ItemVersioniId == itemVersionId;
public virtual List<ResourceItem> GetResourceItems
(string itemVersionId, Expression<Func<ResourceItem,bool>> filter )
{
return db.Table<ResourceItem> ().Where (filter).ToList();
}
I would advise hard coding it. Here is why, but first let me qualify this by saying I am speculating - I have no experience with SQLiteNet - this based on some general, rudimentary knowledge on how LINQ Prodivers work.
When you have it hard coded the lambda expression is converted to SQL at compile time. When you set it to a delegate, it could be a LINQ to Objects query, there is no way to know at compile time that your LINQ Provider can convert that to a SQL statement. Instead this work occurs at runtime and as a result the performance suffers greatly.
I'm using EF 4.1 and I'm trying to enumerate a company list for a grid. I have two options in the current project: select all companies from the DbContext (Entities) and load them into an object from a non-anonymous type (let's say EmpresaGrid) or select all companies into anonymous type objects with the same structure like Empresa (which is the entity I'm selecting from).
The first option (creating a model class for that) would require a little more work, but can be, eventually, more readable. Still, I'm not sure about that. The second option is what I'm using right now.
So, first question: it's better to create a model class only for displaying data or use anonymous type? Doing a direct select is out of question: a SELECT * is too big and that might make everything damn slow (I guess). So selection into another type creates a custom query with only the needed fields.
Using the second option (anonymous type), I have this code (simplified version):
public static IEnumerable<object> Grid()
{
Entities db = new Entities();
var empresas = db.Empresas
.Select(e => new
{
Cgc = e.Cgc, // PK
(...)
Address = new
{
AddressLine = e.EnderecoSede.AddressLine,
(...)
}
},
Contato = e.Contato,
(...)
})
.ToList();
return empresas;
}
The anonymous type I'm creating has around 40 lines of code, so it's kinda big, but it recreates part of the Empresa class struct (since the grid is waiting for a Empresa object). Anyway, I have a problem with the data format. For example, I would like to format the Cgc property using a custom string format. I have a public method for this, FormataCgc. This method receives a string and returns it formatted using some internal conditions.
So, my problem is how to that. For example, I have tried this:
var empresas = db.Empresas
.Select(e => new
{
Cgc = FormataCgc(e.Cgc),
}
But that doesn't work because FormataCgc cannot be translated into SQL (and I don't want to convert it). I also tried this:
var empresas = db.Empresas
.Select(e => new
{
(...)
}
.ToList();
foreach (var e in empresas) {
e.Cgc = FormataCgc(e.Cgc);
}
But it cannot be done since anonymous types have only read-only properties.
So, my second question is: how exactly can I do that? I need to change the data after selecting it, but using anonymous types? I've done a little research, and the best thing I've found was this: Calling a custom method in LINQ query. In that solution, Ladislav suggested doing a second select from the IEnumerable, but since the grid is excepting Empresa I cannot do that (I need to change or add properties, not encapsulate them).
I'm not sure if I was clear enough, but feel free to ask any questions. Also, the grid I'm currently using is a Telerik ASP.NET MVC Grid, which receives a IEnumerable (where T is a class) as model data and them iterates each object, doing its magic.
Since you're already converting this into an IEnumerable<T>, you can do the custom formatting as you stream the results in the client. Do your db.Select, and then convert to the appropriate format afterwards, ie:
var empresas = db.Empresas
.Select(e => new
{
(...)
})
.ToList();
foreach (var e in empresas) {
yield return new {
Cgc = FormataCgc(e.Cgc),
// Copy other properties here, as needed...
};
}
That being said, I'd personally recommend making a custom class, and not return an anonymous type. Your conversion would then be:
foreach (var e in empresas) {
yield return new YourClass(FormataCgc(e.Cgc), ...); // Construct as needed
}
This will dramatically improve the usability of this method, as you will have proper, named access to your properties from the caller of the method.
I think the solution to both of your questions is to create a model class. Sure it is a little bit more work up front, but it will allow you greater flexibility in the long run. Your custom model class can then handle the formatting for you.
public class EmpresaGridModel
{
public string Cgc { get; set; }
public string CgcFormatted
{
return FormataCgc(this.Cgc);
}
//properties for the other fields will have to be created as well obviously
}
Your telerik grid can then bind directly to the CgcFormatted property
I'm having some trouble figuring out the best way to do this, and I would appreciate any help.
Basically, I'm setting up a filter that allows the user to look at a history of audit items associated with an arbitrary "filter" of usernames.
The datasource is a SQL Server data base, so I'm taking the IQueryable "source" (either a direct table reference from the db context object, or perhaps an IQueryable that's resulted from additional queries), applying the WHERE filter, and then returning the resultant IQueryable object....but I'm a little stumped as to how to perform OR using this approach.
I've considered going the route of Expressions because I know how to OR those, but I haven't been able to figure out quite how to do that with a "Contains" type evaluation, so I'm currently using a UNION, but I'm afraid this might have negative impact on performance, and I'm wondering if it may not give me exactly what I need if other filters (in addition to user name filtering shown here) are added in an arbirary order.
Here is my sample code:
public override IQueryable<X> ApplyFilter<X>(IQueryable<X> source)
{
// Take allowed values...
List<string> searchStrings = new List<string>();
// <SNIP> (This just populates my list of search strings)
IQueryable<X> oReturn = null;
// Step through each iteration, and perform a 'LIKE %value%' query
string[] searchArray = searchStrings.ToArray();
for (int i = 0; i < searchArray.Length; i++)
{
string value = searchArray[i];
if (i == 0)
// For first step, perform direct WHERE
oReturn = source.Where(x => x.Username.Contains(value));
else
// For additional steps, perform UNION on WHERE
oReturn = oReturn.Union(source.Where(x => x.Username.Contains(value)));
}
return oReturn ?? source;
}
This feels like the wrong way to do things, but it does seem to work, so my question is first, is there a better way to do this? Also, is there a way to do a 'Contains' or 'Like' with Expressions?
(Editted to correct my code: In rolling back to working state in order to post it, I apparently didn't roll back quite far enough :) )
=============================================
ETA: Per the solution given, here is my new code (in case anyone reading this is interested):
public override IQueryable<X> ApplyFilter<X>(IQueryable<X> source)
{
List<string> searchStrings = new List<string>(AllowedValues);
// <SNIP> build collection of search values
string[] searchArray = searchStrings.ToArray();
Expression<Func<X, bool>> expression = PredicateBuilder.False<X>();
for (int i = 0; i < searchArray.Length; i++)
{
string value = searchArray[i];
expression = expression.Or(x => x.Username.Contains(value));
}
return source.Where(expression);
}
(One caveat I noticed: Following the PredicateBuilder's example, an empty collection of search strings will return false (false || value1 || ... ), whereas in my original version, I was assuming an empty list should just coallesce to the unfiltered source. As I thought about it more, the new version seems to make more sense for my needs, so I adopted that)
=============================================
You can use the PredicateBuilder from the LINQkit to dynamically construct your query.