I have been using the following method pattern in my Web API for searching entities given a set of criteria:
public IEnumerable<Employee> Search(SearchCriteria searchCriteria)
{
var employees = _dbContext.Employees;
if(searchCriteria.Age.HasValue)
{
employees = employees.Where(e => e.Age == searchCriteria.Age.Value);
}
if(searchCriteria...)
{
employees = employees.Where(e => ...);
}
return employees;
}
If a search criterion is not specified, then the property on the SearchCriteria object will be null and I simply do not filter based on this criterion. If it is specified, then it will have a value, and it is used for filtering.
The problem, from a design perspective, is that if I actually want the employees that do not have an age, I simply cannot do it this way, since I use null to determine whether or not I will use the given criterion.
What other approach should I look into in order to emcompass both cases?
Perhaps looking at the URI instead of letting Web API do the object mapping, I could determine whether or not the criterion was actually present?
mysite.com/api/employee?keyword=a&age=null
vs
mysite.com/api/employee?keyword=a
I think you'll have to extend your SearchCriteria a bit. Instead of Nullable<T> properties like Nullable<int> Age you'll need a more detailed structure, that gives you the info, if that criterion should be checked or not.
This might additionally give you the option to not just combine your criterions with logical ANDs but also tweak some more functionality out of it ;)
For the filtering the age, maybe you can set the age to -1, for querying employees that do not have an age like:
if(searchCriteria.Age.HasValue){
if(searchCriteria.Age != -1)
employees = employees.Where(e => e.Age == searchCriteria.Age.Value);
}
else{
employees = employees.Where(e => e.Age == null);
}
}
There is no need to use a conditional check here. If the database value of age can be null, then that means your domain entity must be int? in which case Age can be used without .Value.
public IEnumerable<Employee> Search(SearchCriteria searchCriteria)
{
var employees = _dbContext.Employees.Where(e => e.Age == searchCriteria.Age);
if(searchCriteria...)
{
employees = employees.Where(e => ...);
}
return employees;
}
Related
I want to filter Entities from a Database, but I am kinda stuck on how to correctly chain the Methods together....
The User can search from one Textfield the Id, Title and Description. This SearchString will be bound to SearchString in SearchData.
I have a method:
public List<Movies> Search(SearchData search)
{
var movies = from m in entities select m;
if (!String.isNullOrEmpty(search.SearchString))
{
movies = movies.Where(x => x.Title.Contains(search.SearchString)).Where(//descritpion);
}
return movies;
}
This works, but I also need to check for the Id
.Where(x=>x.Id == search.SearchString)
This won´t work since Id is a int and SearchString a String.
I have tried multiple ways to do so:
I did use "Convert.ToInt32" on the SearchString or "Convert.ToString" on the Id, but out of some reason I won´t get anything back with this and an Error when I search for a String.
I tried to use a block with in the where : .Where(x => {if(Tryparse(Searchstring) {}else{}}), but it doesn´t like it when I try to return the Movie object or null.
I also tried to split the clause up:
if (int.tryparse(searchstring))
movies = movies.where(x=>x.id ==Int32.Parse(SearchString));
movies = movies.where(//title and desc)
,but with this all the Movies I have found in the if will be filtered out due to the title and desc.
My questions are:
1.)Is it possible to "split" those Methods so that they behave like an OR instead of an AND?.... Or that they will not be executed anymore after one worked since the User will only be allowed to enter an Int OR a String. I have more values I am filtering against for which I would need this too.
2)How can I test against more "complex" Logic inside the Lambdas?
I'm not sure if I understand it correctly but If you want to search where id == search.SearchString, and also any other condition with OR then you should do something like this:
.Where(x=>x.Id == search.SearchString && (x.description.Contains(search.Description) || x.Title.Contains(search.Title) || x.Whatever.Contains(search.Whatever)));
You can use an OR in the Where clause.
public List<Movies> Search(SearchData search)
{
var movies = from m in entities select m;
if (!String.IsNullOrEmpty(search.SearchString))
{
movies = movies.Where(x => x.Title.Contains(search.SearchString) || x.Description.Contains(search.SearchString));
}
return movies;
}
I don't understand why the movie Title should contain the movie Id, for my point of view it's bad practice,
i think you need to update the SearchData and add a separate field for the Id, and use it for filtering, this will make things more clear and easy for debugging
In order to respect both conditions, I'd first check whether the search string can be parsed to an integer. If so, you can compare the id against it, if not, the Id comparison should be omitted:
public List<Movies> Search(SearchData search)
{
var movies = from m in entities select m;
if (!String.isNullOrEmpty(search.SearchString))
{
int? searchId;
if (int.TryParse(search.SearchString, out i)
searchId = i;
else
searchId = null;
movies = movies.Where(x =>
(searchId.HasValue && x.Id == searchId.Value)
|| x.Title.Contains(search.SearchString)).Where(//descritpion);
}
return movies;
}
In the comparison, the first part checks whether the searchId is set and - if so - compares the id of the row against it. In addition, it checks whether the title contains the search string. If any of the two conditions are met, the row is returned. However, if the user enters 123 and this is both a valid id and part of a title, both rows will be returned.
If you want to search for id (exclusive) or a part of a text, you could use the following approach:
public List<Movies> Search(SearchData search)
{
var movies = from m in entities select m;
if (!String.isNullOrEmpty(search.SearchString))
{
Expression<Func<Movie, bool>> whereClause;
if (int.TryParse(search.SearchString, out searchId)
whereClause = (x) => x.Id == searchId;
else
whereClause = (x) => x.Title.Contains(search.SearchString);
movies = movies.Where(whereClause).Where(//descritpion);
}
return movies;
}
When entering 123, above sample searches for the id, when entering anything that cannot be parsed into an integer, it looks for a part of the title.
As for your second question: when using Entity Framework, the conditions in your lambda expressions are translated to SQL. This limits the available options. However, as shown in the first example, you can prepare your data and adjust the conditions accordingly in many cases. Nevertheless, not every valid lambda expression can be translated to SQL.
Basically I want to know if it is possible using as an example, Entity Framework, to retrieve all records from the DB table when the provided id doesn't match any of the id's in the table, but if there are id's that match then only retrieve those records.
It is possible to do obviously if you use an if statement or a ?: expression, as an example below.
var dbDocuments = new List<tblSalesQuoteDocument>();
if (id < 0)
dbDocuments = dbContext.tblSalesQuoteDocuments.ToList();
else
dbDocuments = dbContext.tblSalesQuoteDocuments.Where(x => x.HeaderId == id).ToList();
But I find this pretty ugly because if you want all records your URL is basically Documents/Index/-1 or any value less than 0.
Is there a better way?
Why I want one ActionResult is because I do a lot of filtering and code specific stuff in it. I could use two methods, 1 for all records, and another for specific records.
So should I do it as my question above or just use two methods and abstract all my filtering and other code away in Helper Methods to reduce code duplication?
You could add your filter expression on demand. Example:
ActionResult MyAction(int? id = null)
{
// ...
IQueryable<QuoteDocuments> docs = dbContext.tblSalesQuoteDocuments;
if (id != null)
{
docs = docs.Where(x => x.HeaderId == id.Value);
}
var list = docs.ToList();
// ...
}
docs = dbContext.tblSalesQuoteDocuments.Any(x => x.HeaderId == id)? dbContext.tblSalesQuoteDocuments.Where(x => x.HeaderId == id) : dbContext.tblSalesQuoteDocuments.ToList();
You should rather use
if(dbContext.tblSalesQuoteDocuments.Any(x => x.HeaderId == id)){
...
}
Is there a pattern or a best option to improve this method and reduce duplicate ifs?
I've already use the Adapter pattern to transform an interface to another.
public string GetInvoice(int id)
{
// Search in Mongodb and get the object
var invoice = MongoRepository.Get<IInvoiceEntity>(x => x.Invoice.Id == id)
.First();
if (invoice == null)
{
// Search in SQL
invoice = EFRepository.Get<IInvoiceEntity>(x => x.Invoice.Id == id)
.First();
}
if (invoice == null)
{
// This invoice is an old system item
var oldInvoice = WCFClient.InvoiceService.Get(id);
var adapter = new OldInvoiceAdapter(oldInvoice);
invoice = adapter.AdaptEntity();
}
return invoce.ToJson();
}
Thanks!
I'd extract out the various strategies for getting the invoice into methods (which obviates the need for much of your commenting) and then simplify this method, like so:
var invoice = GetInvoiceFromMongoDb(id)
?? GetInvoiceFromSql(id)
?? GetOldSystemInvoice(id);
return invoce.ToJson();
This makes your method really easy to understand, and breaks your code into modules, which will probably help you follow the Single-Responsibility Principle better.
The pattern of returning a given value if it's not null and using a different value if it is null is exactly what the null convalescing operator (??) does. This can help you write:
var invoice = GetFromMongo(id) ?? GetFromSQL(id) ?? GetFromOldArchive(id);
return invoice.ToJson();
I have a Dropdownlist that I fill with all consultants.
This is my entities I am using for this LINQ query:
What I want to do is to get all consultants ids that have a goalcard with Complete_date set. I have no idea how to do this.
Right now I have this following LINQ query to get all consultants ids.
public List<Consultant> GetAllConsultantsByID()
{
var allConsultants = from id in db.Consultant
select id;
return allConsultants.ToList();
}
Any kind of help is appreciated
Thanks in advance
Update:
This is how I have to use my Linq method in my Get Action Method:
var consultants = repository.GetAllConsultantsByID();
model.Consultants = new SelectList(consultants, "Id", "Name");
You can use the Consultant.GoalCard navigation property and the Any extension method:
var query = from con in db.Consultant
where con.GoalCard.Any(card => card.Completed_Date != null)
select con;
return query.ToList();
Consultant.Goalcard exposes all GoalCards of the Consultant as a queryable property. So you can perform queries on that, too. (This example assumes Completed_Date is nullable)
Note: Seeing that a Consultant can have several GoalCards, you might want to rename the Consultant's GoalCard navigation property to GoalCards (to make it clear there can be several).
Now assuming the Complete_Date is of type DateTime?, you could do it like that:
public IEnumerable<Consultant> GetConsultantIds()
{
return db.Consultant.Where(c => c.GoalCard != null && c.GoalCard.Completed_Date.HasValue).Select(c => c.Id).AsEnumerable();
}
[EDIT]
Since GoalCard is a collection (misleading name :) ), you can do something like that to get the IDs of Consultants who have at least one completed date set on any of the cards:
public IEnumerable<int> GetConsultantIds()
{
return db.Consultant.Where(c => c.GoalCard != null && c.GoalCard.Any(card => card.Completed_Date.HasValue)).Select(c => c.Id).AsEnumerable();
}
That's for the list of IDs only, for the list of Consultant objects meeting the criteria:
public IEnumerable<Consultant> GetConsultantIds()
{
return db.Consultant.Where(c => c.GoalCard != null && c.GoalCard.Any(card => card.Completed_Date.HasValue)).AsEnumerable();
}
I have a database table with a list of products (clothing). The products belong to categories and are from different stores.
Sample categories: tops, bottoms, shoes
Sample stores: gap.com, macys.com, target.com
My customers can request to filter products in the following ways:
all products (no filter)
by category
by store
by category and store
Right now I have ONE method in my "Products" class that returns the products depending on the type of filter requested by the user. I use a FilterBy enum to determine which products need to be returned.
For example, if the user wants to view all products in the "tops" category I call this function:
Products.GetProducts(FilterBy.Category, "tops", "");
I have the last parameter empty because it's the string that contains the "store" to filter by but in this case there is no store. However, if the user wants to filter by category AND store I'd call the method this way:
Product.GetProducts(FilterBy.CategoryAndStore, "tops", "macys.com");
My question is, what's a better way to do this? I just learned about the strategy design pattern. Could I use that to do this in a better (easier to extend and easier to maintain) way?
The reason I'm asking this question is because I figure this must be a pretty common problem that people are repeatedly solving (filtering products in various ways)
According to Eric Evan's "Domain Drive Design" you need the specification pattern. Something like this
public interface ISpecification<T>
{
bool Matches(T instance);
string GetSql();
}
public class ProductCategoryNameSpecification : ISpecification<Product>
{
readonly string CategoryName;
public ProductCategoryNameSpecification(string categoryName)
{
CategoryName = categoryName;
}
public bool Matches(Product instance)
{
return instance.Category.Name == CategoryName;
}
public string GetSql()
{
return "CategoryName like '" + { escaped CategoryName } + "'";
}
}
Your repository can now be called with specifications
var specifications = new List<ISpecification<Product>>();
specifications.Add(
new ProductCategoryNameSpecification("Tops"));
specifications.Add(
new ProductColorSpecification("Blue"));
var products = ProductRepository.GetBySpecifications(specifications);
You could also create a generic CompositeSpecification class which would contain sub specifications and an indicator as to which logical operator to apply to them AND/OR
I'd be more inclined to combine LINQ expressions though.
Update - Example of LINQ at runtime
var product = Expression.Parameter(typeof(Product), "product");
var categoryNameExpression = Expression.Equal(
Expression.Property(product, "CategoryName"),
Expression.Constant("Tops"));
You can add an "and" like so
var colorExpression = Expression.Equal(
Expression.Property(product, "Color"),
Expression.Constant("Red"));
var andExpression = Expression.And(categoryNameExpression, colorExpression);
Finally you can convert this expression into a predicate and then execute it...
var predicate =
(Func<Product, bool>)Expression.Lambda(andExpression, product).Compile();
var query = Enumerable.Where(YourDataContext.Products, predicate);
foreach(Product currentProduct in query)
meh(currentProduct);
Probably wont compile because I have typed it directly into the browser, but I believe it is generally correct.
Another update :-)
List<Product> products = new List<Product>();
products.Add(new Product { CategoryName = "Tops", Color = "Red" });
products.Add(new Product { CategoryName = "Tops", Color = "Gree" });
products.Add(new Product { CategoryName = "Trousers", Color = "Red" });
var query = (IEnumerable<Product>)products;
query = query.Where(p => p.CategoryName == "Tops");
query = query.Where(p => p.Color == "Red");
foreach (Product p in query)
Console.WriteLine(p.CategoryName + " / " + p.Color);
Console.ReadLine();
In this case you would be evaluating in memory because the source is a List, but if your source was a data context that supported Linq2SQL for example I think this would evaluate using SQL.
You could still use the Specification pattern in order to make your concepts explicit.
public class Specification<T>
{
IEnumerable<T> AppendToQuery(IEnumerable<T> query);
}
The main difference between the two approaches is that the latter builds a known query based on explicit properties, whereas the first one could be used to build a query of any structure (such as building a query entirely from XML for example.)
This should be enough to get you started :-)
The strategy pattern doesn't necessarily knit well with the common interface-based repository approach. Personally, I'd probably go one of two ways here:
One search method that supports combinations of options:
IList<Product> GetProducts(string category, string store, ...);
(then selectively apply the combinations of filters (i.e. null means "any") - either when building a command, or pass down to a SPROC that does something similar.
With LINQ, maybe a predicate expression?
IList<Product> GetProducts(Expression<Func<Product,bool>> predicate);
Of course, with LINQ you could also use composition by the caller, but that is harder to write a closed / fully-tested repository for:
`IQueryable<Product> Products {get;}`
(and have the caller use .Where(x=>x.Category == "foo")) - I'm not so sure about this last one long-term...
I think I'd make a Category class and a Store class, instead of just strings:
class Category
{
public Category(string s)
{
...
}
...
}
And then maybe:
Product.GetProducts(
Category category, //if this is null then don't filter on category
Store store //if this is null then don't filter on store
)
{
...
}
The Category and Store classes might be related (they might both be subclasses of a Filter class).
I am answering this based on my little knowledge of patterns.
Decorator pattern might help here (considering you can add a filter & get results. Apply new filters on it & get new results)
I would go with something like a strategy for the filters themselves and write CategoryFilter and StoreFilter classes. Then I would use a composite or decorator to combine the filters.
Can't you just add Where stuff as you go here?
var products = datacontext.Products;
if(!String.IsNullOrEmpty(type))
products = products.Where(p => p.Type == type);
if(!String.IsNullOrEmpty(store))
products = products.Where(p => p.Store == store);
foreach(var p in products)
// Do whatever
or something like that...