Implementing a feature to track if the user is repeating a search - c#

I have a view model that represents all the fields available for searching. I'd like to add some logic that would be able to identify if the search values are all the same and determine whether to hit the DB again for their query.
I think I would have to do something like..
after user submits form save form values to some
temporary field.
upon second submission compare temp value to form values collection.
if values are equal set property in view
model IsSameSearch = true
I'd like to use the Post Redirect Get Pattern too. So that My search View doesn't do anything except post the form values to another action that processes and filters the data, which is then "Getted" using Ajax.
The SearchViewModel contains many many search parameters. Here is an abbreviated version.
public bool UseAdvancedSearch { get; set; }
public bool isSameSearch { get; set; }
/// <summary>
/// Gets or sets the page.
/// </summary>
[HiddenInput]
[ScaffoldColumn(false)]
public int Page { get; set; }
[HiddenInput]
[ScaffoldColumn(false)]
public string SortOption { get; set; }
/// <summary>
/// Gets or sets the address keywords.
/// </summary>
[Display(Name="Address")]
public string AddressKeywords { get; set; }
/// <summary>
/// Gets or sets the census.
/// </summary>
public string Census { get; set; }
/// <summary>
/// Gets or sets the lot block sub.
/// </summary>
public string LotBlockSub { get; set; }
/// <summary>
/// Gets or sets the owner keywords.
/// </summary>
[Display(Name="Owner")]
public string OwnerKeywords { get; set; }
/// <summary>
/// Gets or sets the section township range.
/// </summary>
public string SectionTownshipRange { get; set; }
/// <summary>
/// Gets or sets the strap.
/// </summary>
///
[Display(Name="Account Number/Parcel ID")]
public string Strap { get; set; }
/// <summary>
/// Gets or sets the subdivision.
/// </summary>
public string Subdivision { get; set; }
/// <summary>
/// Gets or sets the use code.
/// </summary>
[Display(Name = "Use Code")]
public string UseCode { get; set; }
/// <summary>
/// Gets or sets the zip code.
/// </summary>
[Display(Name="Zip Code")]
public string ZipCode { get; set; }

If you are getting data from Entity Framework you could cache the data at EF level. Look at the package entity framework extended https://github.com/loresoft/EntityFramework.Extended. It is as simple as adding method .FromCache () to the query you use to retrieve and filter the data and it will cache the query result. Make sure you load all the data required using includes etc.
You wouldn't have to worry about same search in model as the caching provider would look at filter settings and determine that it was different. Alternatively cache the data before filtering and then filter the cached results. This is more appropriate if you have lots of filter parameters with significant variance as you will only have to cache 1 large result rather than thousands of smaller results.
You can get more advanced and specify cache period e.g. Cache for 10 minutes

What you are describing is called caching.
One way to accomplish that in your scenario would be to implement GetHashCode() in a way that it would take into account all your fields/properties to compute a unique value. That way you can use your Hash as the key entry in your cache, and store the results with that key.
For that actual caching you could just use the MemoryCache class provided by the .Net Framework if you are not deploying to a web farm.
Also, if you are familiar with IoC and DI (such as using Unity), things like this can be implemented as an Interceptor, and only requiring you to add an attribute to the method you'd like to cache. That way you implement caching only once as a cross-cutting concern and not fill up your application code with things like this.

Related

Update asp.net gridview element from json specification

I have a simple aspx page "Default" with a code behind. The code behind has a set of simple methods.
What I'm looking to do is to create a method OnGridViewChange() - see the mockup below - which is called whenever the user changes a row in the gridview. The row updates a local list of Rows and then converts to object to a json string which is showed to the user.
Essentially what I'm building is a glorified json viewer in asp.net web forms which takes a json string, displays it as a table and then updates the json and table on user changes.
I think I have most of the major methods outlined below, but what I need help with is the OnUpdate method,
public partial class _Default : Page
{
public class FoobarRow
{
public string Name { get; set; }
public int NumberOfHats { get; set; }
}
public List<FoobarRow> FoobarRows { get; set; }= new List<FoobarRow>();
/// <summary>
/// Vera important json formatted string. Gets updated when gridview changes
/// </summary>
public string Json { get; set; }
/// <summary>
/// Updates gridview on the page from the json entered by the user on a previous page
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void Page_Load(object sender, EventArgs e)
{
FoobarRows = JsonConvert.DeserializeObject<List<FoobarRow>>(GetJsonFromUserWhenPageLoads());
UpdateGridviewFromList();
}
/// <summary>
/// Actually updates the gridview from the list of FoobarRows
/// </summary>
private void UpdateGridviewFromList()
{
ApplicationGridView.DataSource = FoobarRows;
ApplicationGridView.DataBind();
}
/// <summary>
/// Is called whenever user edits any row in the gridview and saves the changes. Updates the json and the local list of rows
/// </summary>
/// <param name="rows"></param>
public void OnGridViewChange(List<FoobarRow> rows)
{
FoobarRows = rows;
DisplayCurrentJsonOnPageForUser(JsonConvert.SerializeObject(rows));
UpdateGridviewFromList();
}
public string GetJsonFromUserWhenPageLoads()
{
//Finish later
return "some json the user will have entered on a previous page and will be loaded into a label or something of the like";
}
public void DisplayCurrentJsonOnPageForUser(string json)
{
//Finish later
}
}
Having looked around, I found this documentation from Microsoft https://msdn.microsoft.com/en-us/library/ms972948.aspx?f=255&MSPPError=-2147217396 about the aspx gridview but it seems needlessly complicated for my usecase.
Does anyone know of any datagrid plugin for webforms or other asp.net element that would allow me to tie changes to a simple event such as OnGridViewChange(). I'm new to web forms and the above mentioned article from Microsoft is the best I've managed to find but I feel like there must be a better way!
Apparently this can be achieved by using this event model found here.
https://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.gridview.rowupdating%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396

Should Controllers read custom app settings from web.config?

In my mvc application during certain times of the year we want to show one of two links. Basically I have to switch the link when I get a call from management. So, I thought instead of having to recompile the app I would add a custom app setting to the web.config file. Then I created a wrapper so that it is strongly typed. Now, my problem is I don't know where to execute the logic. Should add a property to my view model and set it in the controller based on the configuration setting value? Or should I read it directly in my View and toggle between the two links? I'm pretty sure this only belongs in the view or the controller, and not the service layer, since it is used specifically for UI stuff.
Details.cshtml //current code
#if(Search.App.ParcelDetailDisplayMode == Search.App.DisplayMode.Tax ){
<a id="tax-link" href="#taxlink" title="View Tax Bill on Tax Collectors Website">Tax Bill</a>
}
else if(Search.App.ParcelDetailDisplayMode == Search.App.DisplayMode.Trim ){
<a id="trim-link" href="#trimlink" title="View your TRIM notice online">Trim Notice</a>
}
web.config
<add key="ParcelDetailDisplayMode" value="Tax"/>
config wrapper
namespace Search
{
/// <summary>
/// The app.
/// </summary>
public static class App
{
/// <summary>
/// Gets the tax bill link.
/// </summary>
public static string TaxBillLink
{
get
{
return ConfigurationManager.AppSettings["TaxBillLink"];
}
}
/// <summary>
/// Gets the trim notice link.
/// </summary>
public static string TrimNoticeLink
{
get
{
return ConfigurationManager.AppSettings["TrimLink"];
}
}
/// <summary>
/// Gets the map link.
/// </summary>
public static string MapLink
{
get
{
return ConfigurationManager.AppSettings["MapLink"];
}
}
/// <summary>
/// Gets the update address link.
/// </summary>
public static string UpdateAddressLink
{
get
{
return ConfigurationManager.AppSettings["UpdateAddressLink"];
}
}
/// <summary>
/// Gets the release name.
/// </summary>
public static string ReleaseName
{
get
{
return ConfigurationManager.AppSettings["ReleaseName"];
}
}
/// <summary>
/// Gets the parcel detail display mode.
/// </summary>
public static DisplayMode ParcelDetailDisplayMode
{
get
{
var r = DisplayMode.Tax;
DisplayMode.TryParse(ConfigurationManager.AppSettings["ParcelDetailDisplayMode"], out r);
return r;
}
}
/// <summary>
/// The display mode.
/// </summary>
public enum DisplayMode
{
/// <summary>
/// The trim.
/// </summary>
Trim,
/// <summary>
/// The tax.
/// </summary>
Tax
}
}
}
I would say it does not really matter. Adding it as a property of your model feels to give a little bit more separation.
What does matter though is that your wrapper is static. This will make it really difficult to mock it for the purpose of unit testing (or any other purpose)
There should be no logic in the controller.
Read this for example: Where should I put my controller business logic in MVC3
or this one: https://softwareengineering.stackexchange.com/questions/165444/where-to-put-business-logic-in-mvc-design
I know it's tempting but the less logic you put there the best you will find yourself in the future.
the answer in my opinion is:
You should read your property in a business layer benhead the controller and pass it all the way up to the view in a model object.
I agree with Maurizio in general that all business logic should be in some service/business logic layer. However in this case since you're only fetching a value from web.config whether, in your controller action, you do:
var someValue = App.TaxBillLink;
or you do:
var someValue = _linkService.GetTodaysLink();
really doesn't matter much unless there is some sort of logic there that needs to be unit tested.

NEST C# - elasticsearch - ecommerce filter combination

I am trying to implement elasticsearch into my webshop but having some troubles on using filters. The filtering is done dynamically.
Example:
I start with showing all the products that are indexed. So no filter is applied. Visitors can choose their own filters like: color, size, brand, type, category, ....
But I don't now how to build the search result with elasticsearch and NEST.
This is my solution without filtering:
var query = ElasticClient.Search<Product>(s => s
.From(from)
.Size(size)
);
I also have another question on indexing a collection<> or list<>. I had to use JsonIgnore on those collections. Could I index those as well?
This is my class:
/// <summary>
/// Represents a product
/// </summary>
public partial class Product {
private ICollection<ProductCategory> _productCategories;
private ICollection<ProductManufacturer> _productManufacturers;
private ICollection<ProductPicture> _productPictures;
/// <summary>
/// Gets or sets the name
/// </summary>
public virtual string Name { get; set; }
/// <summary>
/// Gets or sets the short description
/// </summary>
public virtual string ShortDescription { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the entity is published
/// </summary>
public virtual bool Published { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the entity has been deleted
/// </summary>
public virtual bool Deleted { get; set; }
/// <summary>
/// Gets or sets the date and time of product creation
/// </summary>
public virtual DateTime CreatedOnUtc { get; set; }
/// <summary>
/// Gets or sets the date and time of product update
/// </summary>
public virtual DateTime UpdatedOnUtc { get; set; }
/// <summary>
/// Gets or sets the collection of ProductCategory
/// </summary>
[JsonIgnore] /* added - wesley */
public virtual ICollection<ProductCategory> ProductCategories
{
get { return _productCategories ?? (_productCategories = new List<ProductCategory>()); }
protected set { _productCategories = value; }
}
/// <summary>
/// Gets or sets the collection of ProductManufacturer
/// </summary>
[JsonIgnore] /* added - wesley */
public virtual ICollection<ProductManufacturer> ProductManufacturers
{
get { return _productManufacturers ?? (_productManufacturers = new List<ProductManufacturer>()); }
protected set { _productManufacturers = value; }
}
/// <summary>
/// Gets or sets the collection of ProductPicture
/// </summary>
[JsonIgnore] /* added - wesley */
public virtual ICollection<ProductPicture> ProductPictures
{
get { return _productPictures ?? (_productPictures = new List<ProductPicture>()); }
protected set { _productPictures = value; }
}
}
Is there someone who can help me?
Be sure to read the whole documentation on writing queries here: http://nest.azurewebsites.net/nest/writing-queries.html
What follows is a pasted excerpt from there.
Conditionless queries
Writing complex boolean queries is one thing but more often then not you'll want to make decisions on how to query based on user input.
public class UserInput
{
public string Name { get; set; }
public string FirstName { get; set; }
public int? LOC { get; set; }
}
and then
.Query(q=> {
QueryDescriptor<ElasticSearch> query = null;
if (!string.IsNullOrEmpty(userInput.Name))
query &= q.Term(p=>p.Name, userInput.Name);
if (!string.IsNullOrEmpty(userInput.FirstName))
query &= q
.Term("followers.firstName", userInput.FirstName);
if (userInput.LOC.HasValue)
query &= q.Range(r=>r.OnField(p=>p.Loc).From(userInput.Loc.Value))
return query;
})
This again turns tedious and verbose rather quickly too. Therefor nest allows you to write the previous query as:
.Query(q=>
q.Term(p=>p.Name, userInput.Name);
&& q.Term("followers.firstName", userInput.FirstName)
&& q.Range(r=>r.OnField(p=>p.Loc).From(userInput.Loc))
)
If any of the queries would result in an empty query they won't be sent to elasticsearch.
So if all the terms are null (or empty string) on userInput except userInput.Loc it wouldn't even wrap the range query in a boolean query but just issue a plain range query.
If all of them empty it will result in a match_all query.
This conditionless behavior is turned on by default but can be turned of like so:
var result = client.Search<ElasticSearchProject>(s=>s
.From(0)
.Size(10)
.Strict() //disable conditionlessqueries by default
///EXAMPLE HERE
);
However queries themselves can opt back in or out.
.Query(q=>
q.Strict().Term(p=>p.Name, userInput.Name);
&& q.Term("followers.firstName", userInput.FirstName)
&& q.Strict(false).Range(r=>r.OnField(p=>p.Loc).From(userInput.Loc))
)
In this example if userInput.Name is null or empty it will result in a DslException. The range query will use conditionless logic no matter if the SearchDescriptor uses .Strict() or not.
Also good to note is that conditionless query logic propagates:
q.Strict().Term(p=>p.Name, userInput.Name);
&& q.Term("followers.firstName", userInput.FirstName)
&& q.Filtered(fq => fq
.Query(qff =>
qff.Terms(p => p.Country, userInput.Countries)
&& qff.Terms(p => p.Loc, userInput.Loc)
)
)
If both userInput.Countries and userInput.Loc are null or empty the entire filtered query will be not be issued.

property inaccessible due to protection level?

This is the line that the error is showing on:
public IOAuth2ServiceProvider<IElance> ElanceServiceProvider { get; set; }
It's showing the error on the IElance Type on that line, but here's the interface:
public interface IElance : IApiBinding
{
/// <summary>
/// Search all jobs, list jobs associated with an employee or a freelancer, and retrieve information for a specific job.
/// </summary>
IJobOperations JobOperations { get; }
/// <summary>
/// Access all messages, users, messages, and Work View™ data associated with an Elance Workroom.
/// </summary>
IWorkRoomOperations WorkroomOperations { get; }
/// <summary>
/// View all of the information associated with an employee or a freelancer.
/// </summary>
IProfileOperations ProfileOperations { get; }
/// <summary>
/// View detailed information on freelancers, and retrieve a list of all freelancers employed by an employer.
/// </summary>
IFreelancerOperations FreelancerOperations { get; }
/// <summary>
/// List Elance groups, and retrieve lists of members and jobs belonging to a group.
/// </summary>
IGroupOperations GroupOperations { get; }
/// <summary>
/// Obtain ancillary Elance information, such as the current list of all job categories.
/// </summary>
IUtilityOperations UtilityOperations { get; }
}
I can't for the life of me figure out why it's telling me this. Am I missing something obvious? I would greatly appreciate any direction on this error.
This is probably caused by IApiBinding or one of its base interfaces not being public. Another possibility is that one of the types used by the interface members is not public.

Database not showing up in Visual Studio

I'm developing an application using C# in Visual Studio. This is my first C# application which will use a local database and I am therefore unsure exactly how this is done.
I have been following this article from codeguru.
I have declared my entities, all of which are currently just inheriting from this:
public class Reference
{
/// <summary>
/// ID of the reference.
/// </summary>
public int Id { get; set; }
/// <summary>
/// Reference to the element in theTVDB.
/// </summary>
public int TheTVDBId { get; set; }
/// <summary>
/// Whether or not the reference has been marked as watched.
/// </summary>
public bool IsWatched { get; set; }
}
I have also declared my DbContext as the following:
public class Library : DbContext
{
/// <summary>
/// Constructor using the base constructor.
/// This constructor names the database "Library".
/// </summary>
public Library() : base("Library")
{
}
/// <summary>
/// Set of TVSeriesReferences stored in the database.
/// </summary>
public DbSet<TVSeriesReference> TVSeriesReferences { get; set; }
/// <summary>
/// Set of SeasonReferences stored in the database.
/// </summary>
public DbSet<SeasonReference> SeasonReferences { get; set; }
/// <summary>
/// Set of EpisodeReferences stored in the database.
/// </summary>
public DbSet<EpisodeReference> EpisodeReferences { get; set; }
}
I am trying to store entities in the database doing the following:
Library db = new Library();
TVSeriesReference reference1 = new TVSeriesReference();
reference1.TheTVDBId = 1234;
reference1.IsWatched = true;
db.TVSeriesReferences.Add(reference1);
TVSeriesReference reference2 = new TVSeriesReference();
reference2.TheTVDBId = 8000;
db.TVSeriesReferences.Add(reference2);
int i = db.SaveChanges();
All of this seems to work. At least, I get no errors and i is 2 on every run.
The problem is that the database (which is named "Library") does not show up anywhere. Actually, I don't even have that "Object Explorer" view and I can't seem to find it.
As the database doesn't show up, I am unsure whether or not this is working and if my data is actually stored.
Does anyone know what I am doing wrong or if I am missing something?
I seem to have solved this problem.
I did the following:
In the Server Explorer, I right clicked the Data Connections, chose "Add Connection..." and created a Microsoft SQL Server. I set the server name to .\SQLEXPRESS and the database name to Library.
I then added the following to app.config:
It seems to work now.
<connectionStrings>
<add name="Library"
providerName="System.Data.SqlClient"
connectionString="Data Source=.\SQLEXPRESS;Database=Library;Trusted_Connection=true;" />
</connectionStrings>

Categories

Resources