I have a page where the user can input a SQL query. This query can be against any table in my database. I need to execute this query against the corresponding table and show the result in my view. How to do this?
For eg: user can input Select * from ABC or select max(price) from items.
I tried:
var results = DbContext.Database.SqlQuery<string>(query).ToList();
But this throws an error:
The data reader has more than one field. Multiple fields are not valid
for EDM primitive or enumeration types.
Whatever the result I should be able to pass it to the view and display it.
Please help.
The error message states the problem exactly:
The data reader has more than one field. Multiple fields are not valid for EDM primitive or enumeration types.
To correct the problem, you can either submit a SQL string that returns a single string column from the database, or supply a type parameter that allows EF to map multiple columns.
For example:
SELECT someStringColumn FROM ABC
OR
var results = DbContext.Database.SqlQuery<MyDto>(query).ToList();
Where MyDTO looks something like this:
class MyDto
{
public int AddressID { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
}
you can use
SqlQuery<dynamic>"
this will resolve the error but you will only be able to get the count of the result returned. So you can just verify the query has returned some data. But still will need to know the type of the returned data.
It is a risk of providing the user to input query to database.
Related
Is there any way to address/refer to a SQL SERVER table, by its Unique Index rather its TableName ?
my Application is using multiple html tables, which are acutally within a form,
for CRUD opporations,(more R/U than Create or delete) of an Existing SQL Table.
now... that i need to mix two separated tables, in same update-form (that's my first 'mix')
as i am using Jquery to pass parameters to C# code behind, so it will take care of the
update opporations, and will be able to differentiate the source of the update post/request
and i do know, i could address the table by its TableName,
i just wanted to make sure, as i was searching for some information on the web,
i've encountred results with the Terms sql server 2005... table index or id,
though all results seem to have something to do with, what i can call, some kind of "manipulation", in oreder to create some indexing system /schema (i think it's like hdds has a FAT table)
and I Emphasize "Create",cause I was actualy looking for an existing /built in, numeric value for a "TableID", just the same as a Table has rows and each row has its row IDENTITY - Column.
so at this point i am turning back to the real question at the top .
so that's what i came up with aventually : a simple struct + a class to hold values for database
public class DBMetaDetails
{
public struct DbTable
{
public DbTable(string tableName, int tableId): this()
{
this.Name = tableName;
this.ID = tableId;
}
public string HtmlOutput;
public string Name { get; private set; }
public int ID { get; private set; }
}
}
public sealed class tblsIDs
{
public const int firstTbl= 1, SecondTbl = 2;
}
another class should be for table names
public sealed class tblsNames
{
public const string firstTbl= "firstTbl", SecondTbl = "SecondTbl";
}
another that will hold tables columns names ...etc
I want to create an object proxy, similar to what ISession.Load is returning, but with some fields initialized. For other properties, when accessed, the proxy will fetch the entire object from database.
Consider the following example:
public class User
{
protected User() {
}
public User(int id, string username, string email) {
// ...
}
// initialize the following fields from other datasources
public virtual int Id { get; set; }
public virtual string UserName { get; set; }
public virtual string Email { get; set; }
// the rest of fields when accessed will trigger a select by id in the database
public virtual string Field1 { get; set; }
public virtual string Field2 { get; set; }
public virtual DateTime Field3 { get; set; }
public virtual ISet<Comment> Comments { get; set; }
}
The Id, UserName, Email are well-known in my case, so I could create an object proxy containing these fields, and for the others leave the default proxy behavior. In addition to throwing an exception if this id is not found in the database, i could throw an exception if preinitialized fields do not match or overwrite them silently. I am using NHibernate.ByteCode.Castle for proxy factories.
Edit:
The purpose for this is to be able to have some projection properties from an entity which can be queried elsewhere (say. a lucene index) and to avoid database calls. Then instead of wrapping these fields in a custom component class containing only these subset of properties, I want to use the proxy object directly so that I am able to load the rest of fields if needed. In the best case scenario I wouldn't hit the database at all, but in some corner cases I'd like to access other fields, too. The SELECT N+1 problem's impact can be greatly reduced by using batching.
An hypothetical version of code I want to use would be:
// create User object proxy with some fields initialized
var user = Session.Load<User>(5, new { UserName = "admin", Email = "admin#company.com" });
Console.WriteLine(user.Id); // doesn't hit the database
Console.WriteLine(user.UserName); // doesn't hit the database
Console.WriteLine(user.FullName); // doesn't hit the database
if (somecondition) {
Console.WriteLine(user.Field1); // fetches all other fields
}
You can specify an eager fetch inside the query to actually retrieve the needed associations. This could be done in different ways depending on what query style ( Criteria,Hql or LINQto NH ) you are using. But the key is changing the fetch mode.
for non-collection properties, I wouldn't do that;
the cost of prefetching them from the DB when you load your entity is (usually) so small that I wouldn't even bother.
for collection properties, just mark the collection fetch strategy as 'lazy=true'.
The only case where I would think about doing something like that is when I have a very large number of properties which I don't need (in your example- say Field1..Field20).
In that case I would either:
1. Define those properties together as a component, or
2. create a DTO for fetching only a subset of your entity's properties.
specifying lazy = "true" (or Not.LazyLoad() for Fluent NHib) on properties Field1, Field2, Field3, Comments mappings may help, though not sure about Select N+1 issue.
another way to go is specifying lazy = "false" for UserName, Email
I'm reading the documentation about DetachedCriteria. The documentation clearly shows that setting an alias for your projection is optional. However, whenever I omit the alias my model properties contain no data. Here are my two test models.
[ActiveRecord("INCIDENT")]
public class Incident : ActiveRecordBase<Incident>
{
[PrimaryKey(PrimaryKeyType.Native, "INCIDENT_ID", ColumnType = "Int32")]
public virtual int IncidentId { get; set; }
[Property("CREATION_DATETIME", ColumnType = "DateTime")]
public virtual DateTime? CreationDatetime { get; set; }
[BelongsTo("CAUSE_CD")]
public virtual Cause Cause { get; set; }
}
[ActiveRecord("CAUSE")]
public class Cause : ActiveRecordBase<Cause>
{
[PrimaryKey(PrimaryKeyType.Native, "CAUSE_CD", ColumnType = "String")]
public virtual string CauseCd { get; set; }
[Property("CAUSE_DESC", ColumnType = "String", NotNull = true)]
public virtual string CauseDesc { get; set; }
}
Here is what I use to query the database.
DetachedCriteria incidentCriteria = DetachedCriteria.For<Incident>("i")
.SetProjection(Projections.ProjectionList()
.Add(Projections.Property("i.IncidentId"))
.Add(Projections.Property("i.CreationDatetime"))
)
.SetResultTransformer(Transformers.AliasToBean<Incident>());
IList<Incident> incidents = Incident.FindAll(incidentCriteria);
Both projections properties do not get populated unless I set an alias. So my question is, why is the alias optional? I'm sure I'm simply missing something else. But if I put a random alias (e.g. abc) it'll return an error saying that it could not find property "abc" in the Incident class. Fair enough, I add the appropriate alias to match my property names. And voila! My properties are now property being populated.
Now comes the issue of when I want to query a lookup table. I add
.Add(Projections.Property("c.CauseDesc"), "CauseDesc")
to my ProjectionList and append
.CreateCriteria("i.Cause", "c")
But now it complains that it can't find "CauseDesc" from my Incident model.
What am I missing from this whole criteria ordeal?
Update:
The following code
IList<Incident> results = sess.CreateCriteria<Incident>("i")
.SetProjection(Projections.ProjectionList()
.Add(Projections.Property("i.IncidentId"), "IncidentId")
.Add(Projections.Property("i.CreationDatetime"), "CreationDatetime")
.Add(Projections.Property("c.CauseDesc"), "CauseDesc")
)
.Add(Expression.Gt("i.IncidentId", 1234567))
.CreateAlias("Cause", "c")
.List<Incident>();
This does create a valid query (I checked it with a profiler) but it seems to be having issues populating my generic list. It gives me error "The value \"System.Object[]\" is not of type \"oms_dal.Models.Incident\" and cannot be used in this generic collection.\r\nParameter name: value". However, all works fine if I don't use a projection but then it selects 50+ fields which I don't want. Does that mean I'm forced to use a DTO in this circumstance?
You need to specify the projection property name like...
.Add(Projections.Property("i.IncidentId"), "IncidentId")
also, in general you do not project into the same domain object. You should create an incident dto like...
public class IncidentDTO
{
public int IncidentID { get; set; }
public DateTime CreationDatetime { get; set; }
}
and then...
.SetProjection(Projections.ProjectionList()
.Add(Projections.Property("i.IncidentId"), "IncidentId")
.Add(Projections.Property("i.CreationDatetime"), "CreationDatetime")
)
.SetResultTransformer(Transformers.AliasToBean<IncidentDTO>());
If you want Incidents matching some criteria (not a DTO), then don't set projections/resulttransformer. Instead you simply do something like this...
IList<Incident> incidents = session.CreateCriteria<Incident>()
.CreateAlias("Cause", "c") //now you can access Cause properties via `c.`
.Add(Restrictions.Eq("c.CauseDesc", "some cause"))
.List<Incident>();
See how the root criteria object doesn't need an alias. If it helps, I only use CreateCriteria for the initial object. If I need to reference child objects, I use CreateAlias.
public class SearchText
{
public virtual int Id { get; set; }
public virtual string Text { get; set; }
}
public class SearchTextLog
{
public virtual int Id { get; set; }
public virtual SearchText SearchText { get; set; }
public virtual User User { get; set; }
public virtual int SearchCount { get; set; }
public virtual DateTime LastSearchDate { get; set; }
}
I am trying to select the top 5 SearchText items based on the sum of their count within the SearchTextLog. Currently I have only been able to resolve this by first performing a query to get the top 5 items, and then using the result within a second query. I was wondering if someone could show me the light and teach me how I could integrate these two seperate queries into a single unit.
Here is what I have currently:
var topSearchCriteria = Session.CreateCriteria(typeof (SearchTextLog))
.SetProjection(Projections.ProjectionList()
.Add(Projections.GroupProperty("SearchText.Id"))
.Add(Projections.Alias(Projections.Sum("SearchCount"), "SearchCount")))
.AddOrder(Order.Desc("SearchCount"))
.SetMaxResults(topSearchLimit)
.List<int>();
return Session.CreateCriteria<SearchText>()
.Add(Restrictions.In("Id", topSearchCriteria.ToArray()))
.List<SearchText>();
Edit:
Oh no, I just realised my current solution will lose the important order by of the results. So I will definitely have to incorporate the queries. :-/
Edit:
I tried a bidirectional mapping too to allow the following statement, however, I can't get it to return SearchText items. It simply complains that the SearchText properties aren't in a grouping.
return Session.CreateCriteria<SearchText>()
.CreateAlias("SearchTextLogs", "stl")
.AddOrder(Order.Desc(Projections.Sum("stl.SearchCount")))
.SetMaxResults(topSearchLimit)
.SetResultTransformer(Transformers.AliasToEntityMap)
.List<SearchText>();
Excuse my ignorance, but Nhibernate is completely new to me, and requires a completely different way of thinking.
Ok, I think I have figured out a solution.
My original solution as per my question won't work because NHibernate doesn't yet support the ability to do a group by property without adding it to the select clause (see: link text).
While fooling around however, I came across these cool things called ResultTransformers. Using the AliasToBean result transformer Nhibernate will automatically map the alias's I give to each projection item to properties by the same name within a type I specify. I simply specified my SearchText object (however, I had to add an additional TotalSearchCount property for the sum projection item). It populated my objects perfectly and returned them.
return Session.CreateCriteria(typeof(SearchTextLog))
.CreateAlias("SearchText", "st")
.SetProjection(Projections.ProjectionList()
.Add(Projections.Alias(Projections.GroupProperty("st.Id"), "Id"))
.Add(Projections.Alias(Projections.GroupProperty("st.Text"), "Text"))
.Add(Projections.Alias(Projections.Sum("SearchCount"), "TotalSearchCount")))
.SetMaxResults(topSearchLimit)
.AddOrder(Order.Desc("TotalSearchCount"))
.SetResultTransformer(Transformers.AliasToBean(typeof(SearchText)))
.List<SearchText>();
I am surprised this wasn't easier to do. It's taken me about 4 to 5 hours of research and dev to figure this one out. Hopefully my NHibernate experience will get easier with more and more experience.
I hope this helps someone else out there!
doesn't this work?
var critterRes = Session.CreateCriteria(typeof (SearchTextLog))
.SetProjection(Projections.ProjectionList()
.Add(Projections.GroupProperty("SearchText"))
.Add(Projections.Property("SearchText"))
.Add(Projections.Alias(Projections.Sum("SearchCount"), "SearchCount")))
.AddOrder(Order.Desc("SearchCount"))
.SetMaxResults(topSearchLimit)
.List<SearchText>()
I´m having some trouble retrieving a collection of strings in a projection:
say that I have the following classes
public class WorkSet {
public Guid Id { get; set; }
public string Title { get; set; }
public ISet<string> PartTitles { get; protected set; }
}
public class Work {
public Guid Id { get; set; }
public WorkSet WorkSet { get; set; }
//a bunch of other properties
}
I then have a list of Work ids I want to retrieve WorkSet.Title, WorkSet.PartTitles and Id for.
My tought was to do something like this:
var works = Session.CreateCriteria<Work>()
.Add(Restrictions.In("Id", hitIds))
.CreateAlias("WorkSet", "WorkSet")
.SetProjection(
Projections.ProjectionList()
.Add(Projections.Id())
.Add(Projections.Property("WorkSet.Title"))
.Add(Projections.Property("WorkSet.PartTitles")))
.List();
The Id and Title loads up just fine, but the PartTitles returns null.
Suggestions please!
This might not work using criteria. Most probably, it is because the set can not be retrieved by the same sql query that is generated by the criteria.
I would actually really consider to retrieve the whole object. It is much easier and if it is not a very very special case, it is not worth the troubles. (By the way, it could be faster to retrieve whole objects, the may be already in the cache.) What usually counts is the number of queries (and its complexity of course), not the number of columns retrieved.
It probably works with HQL, there is a elements function there. Consider to use HQL for static queries anyway, it is more powerful.
select
ws.Title,
elements(ws.PartTitles),
w.id
from
Work w
inner join w.WorkSet ws
where w.id in (:ids)
elements is allowed in the select clause, but I don't know what you'll get. You most probably get as many records as there are PartTitles in the result, because there is only one SQL statement built.