Being new to attribute routing, I'd like to ask for help getting this to work.
This test is a simple dynamic DB table viewer: Given a table name (or stored query name or whatever) and optionally some WHERE parameters, return query results.
Table COMPANIES (one of any number of tables which has an associated SELECT query stored somewhere, keyed by table name):
ID NAME HQ INDUSTRY
1 Apple USA Consumer electronics
2 Bose USA Low-quality, expensive audio equipment
3 Nokia FIN Mobile Phones
Controller:
[Route("view/{table}/{parameters}")]
public object Get(string table, Dictionary<string, string> parameters) {
var sql = GetSql(table);
var dbArgs = new DynamicParameters(parameters);
return Database.Query(sql, dbArgs); // Return stuff/unrelated to problem
}
SQL stored in some resource or table. Obviously the parameters must match exactly:
SELECT * FROM companies
WHERE name = :name
-- OR hq = :hq
-- OR ...etc. Doesn't matter since it never gets this far.
Request (Should look clean, but the exact URL format isn't important):
www.website.com/view/companies?hq=fin --> 404: No matching controller
www.website.com/view/companies/hq=fin --> parameters is null
www.website.com/view/companies/hq=fin&name=nokia --> Exception: A potentially dangerous Request.Path value was detected from the client (&).
When I use: [Route("view/{table}{parameters}")] I get:
A path segment cannot contain two consecutive parameters. They must be separated by a '/' or by a literal string. Parameter name: routeTemplate. Makes sense.
My question is: How do I accept a table name and any number of unknown parameters in the usual key1=val1&key2=val2 form (not some awkward indexed format like the one mentioned here) which will be later bound to SQL parameters, preferably using a vanilla data structure rather than something like FormCollection.
I don't think that binding URL parameters to a Dictionary is built-in to the framework. I'm sure there's a way to extend it if you wanted to.
I think quickest (but still acceptable) option is to get the query string parameters using Request.GetQueryNameValuePairs() like this:
[Route("view/{table}")]
public object Get(string table) {
Dictionary<string, string> parameters = Request.GetQueryNameValuePairs()
.ToDictionary(x => x.Key, x => x.Value);
var sql = GetSql(table);
var dbArgs = new DynamicParameters(parameters);
return Database.Query(sql, dbArgs); // Return stuff/unrelated to problem
}
Related
I've gone down a bit of a path and hit a wall with how this could be possibly achieved.
Basically, a query is constructed using JPA and passed to an Oracle DB. On the DB there is a Package, used to generate a reference, and this is dynamically named, based on the environment. This value is user-editable, and stored as a DB property within the application. I don't have any control over the architecture of this.
At a pre-JPA stage, a Query String is generated using the reference value for the Package, which is set as a property (again, I can't change the way this has been designed). I set this up using the Query method setParameter(), like so:
(pseudocode replacing the irrelevant parts for focused context)
String referenceRef = [ reference is fetched from DB properties ];
String queryString = "SELECT ?1 FROM sys.dual";
final Query myQuery = getEntityManager().createNativeQuery( queryString );
myQuery.setParameter( 1, referenceRef );
return myQuery.getSingleResult();
I pretty much did this as a reflex, only to realise (in retrospec, quite obviously) that this won't actually work, as it is escaping the element that should not be escaped...
So, where the referenceRef = "DynamicallyNamedPackage.DoThisDynamicallyNamedThing", the above code will just return "DynamicallyNamedPackage.DoThisDynamicallyNamedThing", as it is obviously making it safe, and the point of doing so is, to a certain extent, the antethesis of what I'm trying to do.
Is it possible to achieve this without creating a whole chunk of additional code? All I can currently think of, as an alternative, is to query dba_procedures for all package objects that match, and using the result of that query to construct the queryString (hence circumnavigating using any user-editable values), but it feels like it's going to be convoluted. This is the alternative, which I am using in lieu of an improvement:
final String verifyReference = "SELECT object_name FROM "
+ "dba_procedures WHERE object_type = 'PACKAGE' AND object_name =?1";
final Query refQuery = getEntityManager().createNativeQuery( verifyReference );
refQuery.setParameter( 1, referenceRef );
final String result = refQuery.getSingleResult();
final String queryString = "SELECT " + result + " FROM sys.dual";
final Query myQuery = getEntityManager().createNativeQuery( queryString );
return myQuery.getSingleResult();
It will essentially look up the user-editable property reference against a list of existing packages, then use the result of that query for building the original reference. It has more null checking and so on involved, and does remove the vulnerability, but feels a bit 'unpolished'.
(As has already been mentioned in the comments, this sort of is designed to need a SQL injection, but needs to prevent "SQL Injection" as a definition of not allowing the DB to be manipulated outside of the design by using an unintended value.)
The Oracle dictionary view all_procedures contains a list of all procedures accessible to the current user.
Specifically in the view there are columns OWNER, OBJECT_NAME (=package name), PROCEDURE_NAME.
You may use this view to sanitize the configured input by simple adding an EXISTS subquery such as:
select
?
from dual where exists (
select null from all_procedures
where
OWNER||'.'||OBJECT_NAME||'.'||PROCEDURE_NAME = upper(?) and
object_type = 'PACKAGE');
You will have to bind twice the same input parameter.
The query returns no data if there is not procedure with the given name, so you may raise an exception.
The query above expects a full qualified stored procedure name, i.e. owner.package.procedure, you'll have to adapt it slightly if you allow unqualified names (without the owner).
Is there a way to restrict the "columns" returned from a Realm Xamarin LINQ query?
For example, if I have a Customer RealmObject and I want a list of all customer names, do I have to query All<Customer> and then enumerate the results to build the names list? That seems cumbersome and inefficient. I am not seeing anything in the docs. Am I missing something obvious here? Thanks!
You have to remember that Realm is an object based store. In a RDBMS like Sqlite, restricting the return results to a sub-set of "columns" of an "record" makes sense, but in an object store, you would be removing attributes from the original class and thus creating a new dynamic class to then instantiate these new classes as objects.
Thus is you want just a List of strings representing the customer names you can do this:
List<string> names = theRealm.All<Customer>().ToList().Select(customer => customer.Name).ToList();
Note: That you take the Realm.All<> results to a List first and then using a Linq Select "filter" just the property that you want. Using a .Select directly on a RealmResults is not currently supported (v0.80.0).
If you need to return a complex type that is a subset of attributes from the original RealObject, assuming you have a matching POCO, you can use:
var custNames = theRealm.All<Customer>().ToList().Select((Customer c) => new Name() { firstName = c.firstName, lastName = c.lastName } );
Remember, once you convert a RealmResult to a static list of POCOs you do lose the liveliness of using RealmObjects.
Personally I avoid doing this whenever possible as Realm is so fast that using a RealmResult and thus the RealObjects directly is more efficient on processing time and memory overhead then converting those to POCOs everytime you need to new list...
Using Apache Cayenne I am trying to figure out how to avoid doing iterative calls to the DB when I have a Collection of attribute values.
Say we have a persistent object Person with an attribute name of type String. If I have a List containing names of the Person I would like to generate an expression that allows a single DB call rather than iterating over the list and getting each Person in turn.
This doesn't actually exist but I would like something like this:
List<String> names = ...;
ExpressionFactory.orLikeExp(Person.NAME_PROPERTY, names);
You can use ExpressionFactory.join(..):
List<Expression> pairs = new ArrayList<>(names.size());
for(String name : names) {
// use an expression appropriate for comparison...
// "like" in this example, but can be "equals", etc.
pairs.add(ExpressionFactory.likeExp(Person.NAME_PROPERTY, name));
}
Expression e = ExpressionFactory.joinExp(Expression.OR, pairs);
I have a stored proc that returns a list of users (rows in User table).
var admins = db.aspnet_UsersInRoles_GetUsersInRoles('/', "Admin");
LINQ generated aspnet_User classes for me, so can I somehow map the result to a List of aspnet_User type? Something like:
List<aspnet_User> admins = db.aspnet_UsersInRoles_GetUsersInRoles('/', "Admin");
Here is a capture of what is returned.
It's entirely possible that you just need:
List<aspnet_User> admins = db.aspnet_UsersInRoles_GetUsersInRoles('/', "Admin")
.ToList();
But it's hard to know without seeing what type the method call returns.
Perhaps this should be a comment but it is way too long...
Well, you do not really want the internal class <aspnet_User> you should want a MembershipUser.
So how about not using the stored procedure that comes with the membership provider but really use the Membership provider itsself.
There is a beautiful class: Roles in System.Web.Security
And it gives you this:
public static string[] GetUsersInRole(string roleName)
From here, a foreach to get the MembershipUser(s) in a list is not that complicated.
By default a stored procedure will return a type that it determines based on the output columns with Result tacked on to the end. It doesn't associate it with types you have already determined. To change this, you can either change the Return Type in the property window to the type you have already defined in your model, or when dragging the stored proc into your model, drop it directly on the type that you want the stored proc to be mapped into.
You don't get the opportunity to change the column mappings for stored procs however, so make sure the shape that the stored proc generates is the same as your target object structures.
It's an old post and I was working on it today and I get the same issue,
I think you are requesting asp membership ?
You can not convert this stored procedure to aspnet_User because it returns aspnet_UsersInRoles_GetUsersInRolesResult type.
but from this aspnet_UsersInRoles_GetUsersInRolesResult you can get userName, then request the aspnet_User table:
String app = "your application name";
String role = "your role name";
ArrayList userInRoleList = new ArrayList();
//Get the role ID
ASPNETDBDataContext aspDB = new ASPNETDBDataContext();
var userInRole = aspDB.aspnet_UsersInRoles_GetUsersInRoles(app, role);
foreach (aspnet_UsersInRoles_GetUsersInRolesResult users in userInRole)
{
userInRoleList.Add(users.UserName);
}
I'm trying to setup something for a movie store website (using ASP.NET, EF4, SQL Server 2008), and in my scenario, I want to allow a "Member" store to import their catalog of movies stored in a text file containing ActorName, MovieTitle, and CatalogNumber as follows:
Actor, Movie, CatalogNumber
John Wayne, True Grit, 4577-12 (repeated for each record)
This data will be used to lookup an actor and movie, and create a "MemberMovie" record, and my import speed is terrible if I import more than 100 or so records using these tables:
Actor Table: Fields = {ID, Name, etc.}
Movie Table: Fields = {ID, Title, ActorID, etc.}
MemberMovie Table: Fields = {ID, CatalogNumber, MovieID, etc.}
My methodology to import data into the MemberMovie table from a text file is as follows (after the file has been uploaded successfully):
Create a context.
For each line in the file, lookup the artist in the Actor table.
For each Movie in the Artist table, lookup the matching title.
If a matching Movie is found, add a new MemberMovie record to the context and call ctx.SaveChanges().
The performance of my implementation is terrible. My expectation is that this can be done with thousands of records in a few seconds (after the file has been uploaded), and I've got something that times out the browser.
My question is this: What is the best approach for performing bulk lookups/inserts like this? Should I call SaveChanges only once rather than for each newly created MemberMovie? Would it be better to implement this using something like a stored procedure?
A snippet of my loop is roughly this (edited for brevity):
while ((fline = file.ReadLine()) != null)
{
string [] token = fline.Split(separator);
string Actor = token[0];
string Movie = token[1];
string CatNumber = token[2];
Actor found_actor = ctx.Actors.Where(a => a.Name.Equals(actor)).FirstOrDefault();
if (found_actor == null)
continue;
Movie found_movie = found_actor.Movies.Where( s => s.Title.Equals(title, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
if (found_movie == null)
continue;
ctx.MemberMovies.AddObject(new MemberMovie()
{
MemberProfileID = profile_id,
CatalogNumber = CatNumber,
Movie = found_movie
});
try
{
ctx.SaveChanges();
}
catch
{
}
}
Any help is appreciated!
Thanks, Dennis
First:
Some time ago I wrote an answer about calling SaveChanges after 1, n or all rows:
When should I call SaveChanges() when creating 1000's of Entity Framework objects? (like during an import)
It is actually better to call SaveChanges after more than 1 row, but not after all.
Second:
Make sure you have index on name in Actors table and title in Movies, that should help. Also you shouldn't select whole Actor, if you need only his ID:
Instead of:
Actor found_actor = ctx.Actors.Where(a => a.Name.Equals(actor)).FirstOrDefault();
you can select:
int? found_actor_id = ctx.Actors.Where(a => a.Name.Equals(actor)).Select(a => a.ID).FirstOrDefault();
and then
Something.ActorID = found_actor_id;
This can be faster, because doesn't require whole Actor entity and doesn't require additional lookups, specially when combined with index.
Third:
If you send a very large file, there is still probability of timeout, even with good performance. You should run this import in separate thread and return response immediately. You can give some kind of identifier to every import and allow user to check status by this ID.