How could i extract House, Car, and Work value from this query string?
http://'mysite'/Result/Environments?House=1&Car=0&Work=1
Then assign values in LINQ statement below.
Action Method in Controller
public ActionResult Environments()
{
//int totalSmokers = repository.Results.Where(x=>x.House =
return View();
}
You should have your ActionResult take them as parameters, like so (I'm assuming bool, if they're int, change them to int).
public ActionResult Environments(bool House, bool Car, bool Work)
Then you can use them in your LINQ statement, something like:
int totalSmokers = repository.Results
.Where(x => x.House == House && x.Car == Car && x.Work == Work)
.Count();
Even cleaner though, you could create a model (and return this in your view like you asked in comments), something like:
public class SmokersModel
{
public bool House { get; set; }
public bool Car { get; set; }
public bool Work { get; set; }
public int TotalSmokers { get; set; }
}
Then that tidies up your action method, you can do:
public ActionResult Environment(SmokersModel Model)
{
Model.TotalSmokers = repository.Results
.Where(x => x.House == Model.House && x.Car == Model.Car && x.Work == Model.Work)
.Count();
return View(Model);
}
Then change your view's model type to be SmokersModel:
#model SmokersModel
You can add parameters to your Action to retrieve the values passed in the query string.
public ActionResult Environments(int House, int Car, int Work)
{
//int totalSmokers = repository.Results.Where(x=>x.House ==
return View();
}
You can also get it using Request["House"], Request["Car"] or Request["Work"] inside your function.
The following example writes the query ?House=1 to the console.
Uri baseUri = new Uri ("http://mysite.com/");
Uri myUri = new Uri (baseUri, "/Result/Environments?House=1&Car=0&Work=1");
Console.WriteLine(myUri.Query);
Hope this Helps!
You can access these through Request.QueryString. There a couple ways to do this. The simplest is probably Request.QueryString["House"], but keep in mind this will be a string. QueryString also has an AllKeys collection, so you can check that to see which values were actually provided.
Related
I'm trying to find the "best" way to check the coherence between data in the request URL and data in the body of the request.
For example, let's say we have this PUT endpoint:
https://host:port/foo/bar/{carId}
where I can pass a Car object as body:
class Car {
string CarId { get; set; }
string Brand { get; set; }
int MaxSpeed { get; set; }
...
}
Now, in my controller I have something like this:
[HttpPut("foo/bar/{carId}")]
public async Task updateCar([FromRoute] string carId, [FromBody] Car car) {
...
}
And I want to be sure that carId in the route matches the CarId property in the body.
What is the "best" way to achieve this? Of course I could simply check with an if in the body controller, but since this is a validation task (or at least I think so), I'd like to have this logic in my validation layer.
Personal ideas so far
Ok, the question is over, here I'll just put some ideas I've tried or I want to try.
I have a custom action filter to check the validation, and I'm trying to play with it in order to see if I can do something there, or add another custom action filter only where I want to check the coherence, but this doesn't look promising.
At the moment I've seen that in an action filter I have access to the controller method parameters through context.ActionArguments property, but I don't know how to check if these arguments were "bound arguments" (namely had some [FromXXX] attribute). If I could do this maybe I could check if there are arguments with the same name (or with a property with the same name), and then compare their values. But this seems very cumbersome and inconsistent.
I've read about custom binders, but I'm still scratching the surface (I hope to learn something more in the next few hours): can they be a possible solution?
You should do it like this:
[AttributeUsage(AttributeTargets.Property)]
public class VerifyWithRouteParams : Attribute
{
public string ParamName
{
get
{
return paramName;
}
}
private readonly string paramName;
public VerifyWithRouteParams(string paramName)
{
this.paramName = paramName;
}
}
Consider you have a car model that is being recieved through body:
public class Car
{
[VerifyWithRouteParams("carId")]
public string CarId { set; get; }
public string AnotherParam {set; get;}
}
You should have a default filter:
public class RouteBodyVerificationActionFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
}
public void OnActionExecuting(ActionExecutingContext context)
{
// do something before the action executes
if (context.ActionArguments != null && context.ActionArguments.Count > 0)
{
foreach (var arg in context.ActionArguments)
{
if (arg.Value == null) continue;
bool isThereAnyObjectInArgumentsWithVerificationAttribute = arg.Value
.GetType()
.GetProperties()
.Any(
x => x.GetCustomAttributes(typeof(VerifyWithRouteParams), false).Any()
);
if (isThereAnyObjectInArgumentsWithVerificationAttribute)
{
foreach (var prop in arg.Value.GetType().GetProperties())
{
var verificationAttr = prop.GetCustomAttributes(typeof(VerifyWithRouteParams), false).FirstOrDefault() as VerifyWithRouteParams;
if (null == verificationAttr) continue;
string routeArgumentName = verificationAttr.ParamName;
context.ActionArguments.TryGetValue(routeArgumentName, out var routeArgumentValue);
if (null == routeArgumentValue)
{
context.ModelState.AddModelError("invalid argument value", routeArgumentName);
}
if (routeArgumentValue?.Equals(prop.GetValue(arg.Value)) != true)
{
context.ModelState.AddModelError("invalid argument value", routeArgumentName);
}
}
}
}
}
}
}
Then you should add a default filter in startup:
services.AddControllers(cfg =>
{
cfg.Filters.Add<RouteBodyVerificationActionFilter>();
});
Then you can check it by model validation errors in controller:
if (!ModelState.IsValid)
{
return Content("invalid model");
}
I know this question has already been asked but I couldn't find an answer that satisfied me. What I am trying to do is to retrieve a particular DbSet<T> based on its type's name.
I have the following :
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("MyDllAssemblyName")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("MyCallingAssemblyName")]
class MyDbContext : DbContext {
public DbSet<ModelA> A { get; set; }
public DbSet<ModelB> B { get; set; }
public dynamic GetByName_SwitchTest(string name) {
switch (name) {
case "A": return A;
case "B": return B;
}
}
public dynamic GetByName_ReflectionTest(string fullname)
{
Type targetType = Type.GetType(fullname);
var model = GetType()
.GetRuntimeProperties()
.Where(o =>
o.PropertyType.IsGenericType &&
o.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>) &&
o.PropertyType.GenericTypeArguments.Contains(targetType))
.FirstOrDefault();
if (null != model)
return model.GetValue(this);
return null;
}
}
I have no trouble getting the type itself whether it is via a simple switch or reflection. I need however to return the type as a dynamic since I do not know what DbSet type it will be.
Then somewhere else in the same assembly, I use it this way :
// MyDbContext MyDbContextInstance..
var model = MyDbContextInstance.GetByName_SwitchTest("A");
var record1 = model.FirstOrDefault(); // It crashes here with RunTimeBinderException
At this point model contains an instance of a InternalDbSet<ModelA> type. From there, any use I do with the model object I get a RunTimeBinderException :
'Microsoft.Data.Entity.Internal.InternalDbSet' does not contain a definition for 'FirstOrDefault'
Investigating on the web, I found a blog post explaining that (dixit his blog) :
the reason the call to FirstOrDefault() fails is that the type
information of model is not available at runtime. The reason it's not
available is because anonymous types are not public. When the method
is returning an instance of that anonymous type, it's returning a
System.Object which references an instance of an anonymous type - a
type whose info isn't available to the main program.
And then he points that a solution :
The solution is actually quite simple. All we have to do is open up
AssemplyInfo.cs of the ClassLibrary1 project and add the following
line to it: [assembly:InternalsVisibleTo("assembly-name")]
I did try this solution on my code but it doesn't work. For info I have an asp.net 5 solution with two assemblies running on dnx dotnet46. An app and a dll containing all my models and DbContext. All the concerned calls I do are located on the dll though.
Does this solution have any chance to work ?
Am I missing something ?
Any pointers would be greatly appreciated ?
Thanks in advance
[EDIT]
I have tried to return IQueryable<dynamic> rather than dynamic and I could do the basic query model.FirstOrDefault(); but above all I'd like to be able to filter on a field too :
var record = model.FirstOrDefault(item => item.MyProperty == true);
So how did I do it when I am not aware of <T> during compile time.
First need to get the type as DbContext.Set method returns a non-generic DbSet instance for access to entities of the given type in the context and the underlying store.
public virtual DbSet Set(Type entityType)
Note here argument is the type of entity for which a set should be returned.And set for the given entity type is the return value.
var type = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(t => t.Name == <Pass your table name>);
now once I have this type
if(type != null)
{
DbSet context = context.Set(type);
}
Or a one liner would be
DbSet mySet = context.Set(Type.GetType("<Your Entity Name>"));
*Disclaimer: This response doesn't give a stricto sensu answer to my question. It is rather a different approach to resolve my own problem. I am aware this is a specific example for a given situation that will not work for everyone. I am posting this approach in the hope it helps someone but will not mark it as the answer as I am still hoping for a real solution.
To start with, let's accept the fact that the only useful information we can get out of the current code is whether a record exists or not.. Any attempt of a dynamic queries after that would give the RuntimeBinderException.
Then let's continue with another fact; DbContext.Add(object) and DbContext.Update(object) are not template based so we can use them to save our models ( Instead of db.A.Add() or db.A.Update() )
In my own situation, no more is required to work out a procedure
Define models a little differently
To start with, I need a field that is retrievable across all my models which should obviously be a way to identify a unique record.
// IModel give me a reliable common field to all my models ( Fits my DB design maybe not yours though )
interface IModel { Guid Id { get; set; } }
// ModelA inherit IModel so that I always have access to an 'Id'
class ModelA : IModel {
public Guid Id { get; set; }
public int OtherField { get; set; }
}
// ModelB inherit IModel so that I always have access to an 'Id'
class ModelB : IModel {
public Guid Id { get; set; }
public string WhateverOtherField { get; set; }
}
Re-purpose the dynamic queries a bit to do something we know works
I haven't found a way to do smart query dynamically, so instead I know I can reliably identify a record and know if it exists or not.
class MyDbContext : DbContext {
public DbSet<ModelA> A { get; set; }
public DbSet<ModelB> B { get; set; }
// In my case, this method help me to know the next action I need to do
// The switch/case option is not pretty but might have better performance
// than Reflection. Anyhow, this is one's choice.
public bool HasRecord_SwitchTest(string name) {
switch (name) {
case "A": return A.AsNoTracking().Any(o => o.Id == id);
case "B": return B.AsNoTracking().Any(o => o.Id == id);
}
return false;
}
// In my case, this method help me to know the next action I need to do
public bool HasRecord_ReflectionTest(string fullname)
{
Type targetType = Type.GetType(fullname);
var model = GetType()
.GetRuntimeProperties()
.Where(o =>
o.PropertyType.IsGenericType &&
o.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>) &&
o.PropertyType.GenericTypeArguments.Contains(targetType))
.FirstOrDefault();
if (null != model)
return (bool)model.GetValue(this).AsNoTracking().Any(o => o.Id == id);
return false;
}
// Update and save immediately - simplified for example
public async Task<bool> UpdateDynamic(object content)
{
EntityEntry entry = Update(content, GraphBehavior.SingleObject);
return 1 == await SaveChangesAsync(true);
}
// Insert and save immediately - simplified for example
public async Task<bool> InsertDynamic(object content)
{
EntityEntry entry = Add(content, GraphBehavior.SingleObject);
return 1 == await SaveChangesAsync(true);
}
}
A little bit of plumbing to give a sense to my situation
Next, what I needed to do with that dynamic queries was a way to replicate data from a server down to my client. ( I have omitted a big chunk of the architecture to simplify this example )
class ReplicationItem
{
public ReplicationAction Action { get; set; } // = Create, Update, Delete
public string ModelName { get; set; } // Model name
public Guid Id { get; set; } // Unique identified across whole platform
}
Connecting the bits.
Now, here's the routine that connects the bits
public async void ProcessReplicationItem(ReplicationItem replicationItem)
{
using (var db = new MyDbContext())
{
// Custom method that attempts to get remote value by Model Name and Id
// This is where I get the strongly typed object
var remoteRecord = await TryGetAsync(replicationItem.ModelName, replicationItem.Id);
bool hasRemoteRecord = remoteRecord.Content != null;
// Get to know if a local copy of this record exists.
bool hasLocalRecord = db.HasRecord_ReflectionTest(replicationItem.ModelName, replicationItem.Id);
// Ensure response is valid whether it is a successful get or error is meaningful ( ie. NotFound )
if (remoteRecord.Success || remoteRecord.ResponseCode == System.Net.HttpStatusCode.NotFound)
{
switch (replicationItem.Action)
{
case ReplicationAction.Create:
{
if (hasRemoteRecord)
{
if (hasLocalRecord)
await db.UpdateDynamic(remoteRecord.Content);
else
await db.InsertDynamic(remoteRecord.Content);
}
// else - Do nothing
break;
}
case ReplicationAction.Update:
[etc...]
}
}
}
}
// Get record from server and with 'response.Content.ReadAsAsync' type it
// already to the appropriately
public static async Task<Response> TryGetAsync(ReplicationItem item)
{
if (string.IsNullOrWhiteSpace(item.ModelName))
{
throw new ArgumentException("Missing a model name", nameof(item));
}
if (item.Id == Guid.Empty)
{
throw new ArgumentException("Missing a primary key", nameof(item));
}
// This black box, just extrapolate a uri based on model name and id
// typically "api/ModelA/{the-guid}"
string uri = GetPathFromMessage(item);
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:12345");
HttpResponseMessage response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
return new Response()
{
Content = await response.Content.ReadAsAsync(Type.GetType(item.ModelName)),
Success = true,
ResponseCode = response.StatusCode
};
}
else
{
return new Response()
{
Success = false,
ResponseCode = response.StatusCode
};
}
}
}
public class Response
{
public object Content { get; set; }
public bool Success { get; set; }
public HttpStatusCode ResponseCode { get; set; }
}
ps: I am still interested in a real answer, so please keep posting for other answer if you have a real one to share.
You could use this to get the DBSet for a specific type:
public object GetByType(DbContextcontext, Type type) {
var methode = _context.GetType().GetMethod("Set", types: Type.EmptyTypes);
if (methode == null) {
return null;
}
return methode.MakeGenericMethod(type).Invoke(_context, null);
}
Good morning everybody,
I am doing a AngularJS project using ASP.NET-MVC, C#, EF and an SQL Express DB.
I have an HTML page calling some AngularJS functions calling some functions on MyController.cs.
In MyController.cs I have quite a lot functions using EF.
In my DB, I have hundred of tables with the same columns.
And I want to have the same HTML page for each table, so executing the same functions with different names
For example when I go to the link http://..../Index/TABLE1, MyController.cs would look like :
public ActionResult getCaptions()
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.MaxJsonLength = 500000000;
var data =
_db
.TABLE1
.OrderBy(i => i.CodeId)
.ToArray();
return Content(serializer.Serialize(data).ToString(), "application/json");
}
and when I go to the link http://..../Index/TABLE2, MyController.cs would look like :
public ActionResult getCaptions()
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.MaxJsonLength = 500000000;
var data =
_db
.TABLE2
.OrderBy(i => i.CodeId)
.ToArray();
return Content(serializer.Serialize(data).ToString(), "application/json");
}
I have thought about a solution like this :
Declare a global variable on the controller tableName
Modify the Index ActionResult
public ActionResult Index(string id)
{
tableName = id;
return View();
}
Now I am stuck ...
Any help is needed, thanks.
EDIT : If you downvote, can you at least explain why ? Thanks
I see you #aBennouna may have already given up on this question, but I took an interest in the question and decided it needed a solution. It may not be 100% what you asked for since here you can't take in a table name as a string parameter.
First if all the tables have same columns, you can inherit them from a same base:
public class TableBase
{
[Key]
public int CodeId { get; set; }
public int Prop1 { get; set; }
public int Prop2 { get; set; }
public int Prop3 { get; set; }
}
Using this base we can define our tables:
public class Table1 : TableBase
{
}
public class Table2 : TableBase
{
}
// etc...
This enables us to write a generic GetCaptions method like #CodeCaster suggested (thanks for the nudge in right direction):
public ActionResult GetCaptions<T>() where T : TableBase
{
var set = db.Set<T>();
// get all objects to array
var list = set.OrderBy(i => i.CodeId).ToList();
// serialize and return result
// OR get single object and a value
var item = set.FirstOrDefault();
var propertyValue = item.Prop1;
}
Usage:
GetCaptions<Table1>();
GetCaptions<Table2>();
Something weird is happening and I am not able to understand why.. here's the scenario -
I have a model with few properties when I populate the model the properties in model does have values set (checked by putting breakpoints). It comes on the view also but it is not being shown on textbox. It is showing the default value (guessing by seeing the item textbox on the page as it has 0).
Below is my model -
public class PriceEnquiryModel
{
[DisplayName("Item")]
public int item { get; set; }
[DisplayName("Description")]
public string description { get; set; }
[DisplayName("UOP")]
public string uop { get; set; }
[DisplayName("UOS")]
public string uos { get; set; }
[DisplayName("Pack Description")]
public string pack_description { get; set; }
[DisplayName("Pack Size")]
public string PackSize { get; set; }
}
This is the controller;s code -
public ActionResult Search(PriceEnquiryModel price)
{
var priceEnquiryModel = new PriceEnquiryModel();
// Read parameter values from form.
int item = Convert.ToInt32(Request.Form["txtSearch"].ToString());
int maxrow = Convert.ToInt32(Request.Form["txtmaxrow"].ToString());
string priceType = !string.IsNullOrWhiteSpace(price.priceType) && price.priceType.ToUpper().Equals("STA") ? "N" : "Y";
// Get the price information
var operationResult = priceBal.SearchPriceEnquiry(0, item, price.price_scheme, priceType, maxrow);
var priceEnquiryDomList = (List<PriceEnquiryDom>)operationResult[0].Result;
// Check if we have something
if (priceEnquiryDomList != null && priceEnquiryDomList.Count > 0)
{
// Parse the model.
priceEnquiryModel = helper.ConvertDomToModel(priceEnquiryDomList[0]);
// Prepare the list.
priceEnquiryModel.PriceEnquiryModelList = new List<PriceEnquiryModel>();
foreach (var priceEnquiryDom in priceEnquiryDomList)
{
var priceEnquiryModelListItem = helper.ConvertDomToModel(priceEnquiryDom);
priceEnquiryModel.PriceEnquiryModelList.Add(priceEnquiryModelListItem);
}
Session["mainModel"] = priceEnquiryModel;
}
// Prepare product drop down list items if searched by product desc
if (TempData.Count > 0 && TempData["Products"] != null)
{
var products = TempData["Products"] as List<ProductSearchByDescModel>;
ViewBag.Products = products;
}
return View("Index", priceEnquiryModel);
}
This is the model on the View (while debugging) -
This is how I am rendering the model on the view -
This is the page after running -
Does anyone has any idea what is going on ? I have done the same thing on multiple pages and all run as expected.
Thanks in Advance.
Rohit
The issue is that your method has parameter PriceEnquiryModel price but then you return a new instance of PriceEnquiryModel (named priceEnquiryModel). The process of model binding includes binding your model and adding its values to ModelState (along with any validation errors).
When you return the view, the html helper methods use the values from ModelState (not the models values) so attempting to change the values (which I assume is what priceEnquiryModel = helper.ConvertDomToModel(priceEnquiryDomList[0]); is doing) is ignored by the helpers.
For an explanation of why this is the default behavior, refer the second part of this answer
One option to call ModelState.Clear() before setting new values for the properties of PriceEnquiryModel
I have class like this:
public class Lugar
{
[Key]
public int LugarId { get; set; }
public List<Review> Reviews { get; set; }
public int SumReviews { get; set; }
public double AverageReviews { get {
if (Reviews == null)
return 0;
else if (Reviews.Count == 0)
return 0;
else
return (double)SumReviews/(Reviews.Count); } }
}
And in my controller I have this:
[HttpPost, Authorize]
public ActionResult WriteReview(int id, FormCollection formCollection)
{
Lugar lugar = db.Lugares.Find(id);
Review review=new Review();
review.User = User.Identity.Name;
review.Rating=Convert.ToInt32(formCollection["Rating"]);
review.Texto = formCollection["Review"];
if (lugar != null)
{
if( lugar.Reviews==null)
lugar.Reviews=new List<Review>();
lugar.Reviews.Add(review);
lugar.SumReviews += review.Rating;
db.SaveChanges();
}
else
return RedirectToAction("Index");
return RedirectToAction("Index");
}
}
The problem is in the line:
if( lugar.Reviews==null)
lugar.Reviews=new List();
Everytime I execute I am getting ( lugar.Reviews==null) as true.....
Even if I already added a Review for that place, the if statement returns true.....
Try using the 'virtual' keyword where you declare your List and see if you have any more luck.
You might want to introduce a constructor in your Lugar class and instantiate the list there.
Something like this:
public void Lugar()
{
Reviews = new List<Review>();
}
Hope this helps. of not please let me know.
P.S. another thing that is not related to your specific question, but certainly an improvement is to use view model rather than FormCollection.
It will simplify your life in a major way. For example of how to use it please take a look at this successfully answered question: What is ViewModel in MVC?
You have 2 options here, lazy loading (which is enabled by putting virtual on navigation properties) This will pull down your second entitiy when you access the property in C#
or eager loading by using a .Include(/*lambda or string property name*/) statement in your query.
Personally i perfer eager loading as you have more control over when entities are loaded