C# Entity Framework text SQL query wrapper - c#

I have a problem similar to this:
How to retrieve multiple columns from non-entity type sql query?
I need to implement the method string[,] DirectQuery(string sqlText, string[] param) which is basically a C# equivalent of SQL Server Management Studio.
The user is supposed to enter a SQL query as string text (+ string parameters to avoid SQL injection) and receive back a string matrix containing the outcome of the query.
Internally, I'm using Entity Framework.
Here's my implementation:
public string[,] DirectQuery(string sqlQuery, string[] param)
{
//discover how many fields are specified in the select clause
string ip = sqlQuery.ToLower().Split(new string[] { "from" }, StringSplitOptions.None)[0];
int cols = ip.Count(y => y == ',') + 1;
//execute the query
DbRawSqlQuery<string> res = param != null ? _context.Database.SqlQuery<string>(sqlQuery, param) : _context.Database.SqlQuery<string>(sqlQuery);
//wrap everything in a matrix to return
return res.ToArray().Array2Matrix(res.ToArray().Length /cols, cols);
}
where
public static T[,] Array2Matrix<T>(this T[] flat, int rows, int cols) where T : class
is my custom method that turns flat arrays into rows x cols matrices.
If in the select clause users specify a single attribute, that works fine, but in case of 2+ fields needed the execution of DirectQuery fires the runtime exception dbrawsqlquery he data reader has more than one field. Multiple fields are not valid for EDM primitive or enumeration types. That's completely reasonable, but since the query can be whatever I can't create a custom class to wrap every possible outcome.
What do you suggest?

The problem is that you're using a method - DbRawSqlQuery which must be told what type to expect, and you're telling it to expect just a string, so it has no idea what to do with more than one returned column.
Maybe it would work if you specified string[] or IEnumerable<string> or something? Alternatively you could define a series of objects with 1, 2, 3, 4 etc values and detect the number of items at runtime and use the correct class... but that seems absurd.
Really though, I'd suggest NOT using EF as someone suggested above. Find something which can return dynmamic objects, OR just use ADO.Net directly.

Related

It is possible to run LINQ queries over reflected generic collections?

I'm building SQL query engine that should take SQL Query as a string in the following format
from (Collection) select (fields) where (conditions)and run it over my Data class (which consists of List fields like List<Person>) and return the result of query
I've already created classes etc. now I just need to run the queries.
Query consists of Source string, ConditionsSet object which have the list of conditions, and Fields string collection which consists of names of fields that we want to display if the record match the conditions.
Let's jump to the code.
public void RunQuery(Data data, Query query)
{
var table = data.GetType().GetField(query.Source).GetValue(data); //Source object
// var output = from entry in table where QueryEngine.IsMatching(entry, query.ConditionsSet) select entry;
// Is something like this is possible? How to approach/do that? Am I forced to not use linq?
// The compiler tells that I cant use Linq because it cant find GetEnumerator in the table object
}
private bool IsMatching(object entry, ConditionsSet set)
{
foreach (Condition c in set.Conditions) // For example we assume the operator is == equality and every condition is separated by AND keyword
if (entry.GetType().GetField(c.Field).GetValue(entry).ToString() != c.Value) //c.Value is string
return false;
return true;
}
How should I approach that? Is LINQ unavailable for me?

NHibernate custom projection with Less than or Equal to

I am trying to create a projection which will filter results from the database using the Levenshtein search distance calculation. To create this I open a session to the DB and then use CreateCriteria to query the database:
...
return session.CreateCriteria<Contact>()
.Add(Expression.Le(Levenshtein("FullName", "Bob"), 5)
...
Created a small helper method to return a new instance of the projection class
public static LevenshteinProjection Levenshtein(string propertyName, string searchValue)
{
return new LevenshteinProjection(propertyName, searchValue);
}
Essentially everything works fine when creating the string to compile the text but when I look at the SQL that is produced the value I want to be less than or equal to is a ?!
... {rest of sql select} WHERE levenshtein(this_.full_name, 'Bob') <= ?
Why is it adding the ? I've set the return type to
public override IType[] GetTypes(ICriteria criteria, ICriteriaQuery criteriaQuery)
{
return new IType[] { NHibernateUtil.Int32 };
}
Thanks
The ? represents the parameter placeholder.
NHibernate generates a 'parameterized query'. Instead of creating a query with the parameter-value hardcoded, it generates a query that contains a parameter.
The parameter will be assigned with the value that you provide.
This enables the DBMS to cache query execution plans.

c# Linq to Sql dynamic Data Context assignment

`Hi,
Can somebody please give me a pointer on this? I have 8 servers each with 8 databases which look identical exept server/database name. We are talking thousands of tables.
I create my data contexts with sqlmetal.exe
After creating my data contexts, I import them into the application and then I run comparison scripts over the databases to compare results.
My problem is dynamically switching between data contexts.
Datacontext.DAL.DUK1 duk1sdi = new Datacontext.DAL.DUK1(connectionString);
Datacontext.DAL.DUK3 duk3sdi = new Datacontext.DAL.DUK3(connectionString);
string fromOne = runQuery(duk1sdi);
string fromThree = runQuery(duk3sdi);
public static string runQuery(DataContext duk)
{
var query =
from result in duk.TableA
select result.Total;
string returnString = query;
return returnString;
}
I have no problem with the query running when the duk is predefined, however how do I define and pass the datacontext to the function?
The error I get is:
Error 1 'System.Data.Linq.DataContext' does not contain a definition
for 'TableA' and no extension method 'TableA' accepting a first
argument of type 'System.Data.Linq.DataContext' could be found (are
you missing a using directive or an assembly reference?)
You could use the GetTable<T> method, where T is the type of the table, e.g. TableA.
public static string runQuery(DataContext duk) {
var table = duk.GetTable<TableA>();
var query = from result in table select result.Total;
...
}
However, all types of TableA will need to be the same type, strictly (I'm pretty sure).
Otherwise you would need to literally branch the logic for the handling of each context. Since you can extend your DataContext instances (in general, maybe not in your specific case) then you could have them share an interface that exposes a collection property of TableA, but you would need a higher level context wrapper to pass around then - unless you pass around the collection by altering the method signature.
You can use interfaces. Check this answer, but be sure to script the interfaces using a .tt file with the amount of tables you have.
Edit:
If you have generated contexts which you want to use interchangeably in a reusable method, you have the problem that the generated TableA classes are not reusable, since they are different types (even though the names may match, but that doesn't make them equal). Therefore you need to abstract the actual types, and one way to do this, is to use interfaces. You build your reusable method around an interface which abstracts the specific context-type and table-type. The downside is that you have to implement the interfaces on the generated contexts and tabletypes. This though is something you can solve using a .tt script.
Pseudo code:
// Define interface for table
public interface ITableA {
// ... properties
}
// Define interface for context
public interface IMyContext {
IQueryable<ITableA> TableA { get; }
}
// Extend TableA from DUK1
public partial class TableA: ITableA {
}
// Extend DUK1
public partial class Datacontext.DAL.DUK1: IMyContext {
IQueryable<ITableA> IMyContext.TableA {
get { return TableA; }
}
}
// Same for DUK3 and TableA FROM DUK3
// Finally, your code
Datacontext.DAL.DUK1 duk1sdi = new Datacontext.DAL.DUK1(connectionString);
Datacontext.DAL.DUK3 duk3sdi = new Datacontext.DAL.DUK3(connectionString);
string fromOne = runQuery(duk1sdi);
string fromThree = runQuery(duk3sdi);
public static string runQuery(IMyContext duk) {
// Note: method accepts interface, not specific context type
var query = from result in duk.TableA
select result.Total;
string returnString = query;
return returnString;
}
If your schema is identical between databases, why script the dbml for all of them? Just create one context with it's associated classes and dynamically switch out the connection string when instantiating the context.
var duk1sdi = new Datacontext.DAL.DUK1(connectionString1);
var duk3sdi = new Datacontext.DAL.DUK1(connectionString2);
Thanks, guys, I think I found the simplist solution for me based a bit of both your answers and by RTFM (Programming Microsoft Linq in Microsoft .NET Framework 4 by Paulo Pialorsi and Marco Russo)
In this way I don't have to use the large DBML files. It is a shame because I'm going to have to create hundreds of tables in this way, but I can now switch between connection strings on the fly.
First I create the table structure. (outside the program code block)
[Table(Name = "TableA")]
public class TableA
{
[Column] public int result;
}
Then I define the table for use:
Table<TableA> TableA = dc.GetTable<TableA>();
And then I can query from it:
var query =
from result in TableA
select TableA.result;

PetaPOCO and more than 4 joins

Is petapoco capable of achieving the following :
1.Unlimited joins in one query
2.Unlimited One to Many relations in one query
I have looked at PetaPOCO and it seems like it is not capable of doing more than 4 joins, the longest signature looks like :
db.Query<T1, T2, T3 , T4>
Also seems like it supports a one to many relation , but only for one composite object such as below :
db.FetchOneToMany<T1, T2> where T2 is a foreign key of T1
I'm testing some of the micro ORMs out there to stick to the best one. Do you know of any of them that can handle these situations and if none of the micro ORMs are supporting this feauture, how do you deal with an object that is like the following :
class A
{
List<B> member1;
List<C> member2;
Z member3; //Composit object
Z1 member4; //Composit object
Z2 member5; //Composit object
Z3 member6; //Composit object
Z4 member7; //Composit object
}
And then even more complicated is , what if member one (type B) has some composite object within itself ? What if we have :
class B
{
G member0;
}
Please don't propose a solution to hit database multiple times, coz it's going to be way too many calls when the objects become just a little bit complex.
Oh and i also know that one other way of tackling the case of unlimited joins is creating a very flat object that hols all fields combined. It's not an elegant solution at all.
The T1..T$ Query() overloads all pass through to the main Query(..Type[]..) method. You can either add more Query() overloads yourself to handle more T parameters, or pass in all the types you need in a Type array (which is what the T1-T4 functions do) :
Query<TRet>( new Type[]{typeof(Poco1), typeof(Poco2), typeof(Poco3), typeof(Poco4), typeof(Poco5)}, null, sql, args);
You can have multiple one to many relationships but Schotime is right, you need to be very careful of swathes of duplicate data coming back in your result set. Write the sql query and look at the result set, is the amount of duplication acceptable to you? If so then in Petapoco there is a concept of relator callbacks where you write a small class that handles the different pocos in a single result row and add each poco to the list properties on the parent poco.
http://www.toptensoftware.com/Articles/115/PetaPoco-Mapping-One-to-Many-and-Many-to-One-Relationships
I've never had to do this with multiple one to many but quoted from the above
"If you're joining more than two tables you'll need something more
complex but it's really just extensions of the above."
Another option is to have a stored procedure that does all the work in a single database request and have it return multiple result sets which I believe Schotime has achieved in his branch of petapoco but I've not used it myself yet so I can't really comment on if it will help here :
http://schotime.net/blog/index.php/2011/11/20/petapoco-multiple-result-sets/
If I absolutely had to wire up all the data in one go for objects as complex and nested as you are suggesting then I would use a stored procedure (a single db call) and stitch it all together with code. Only then would I figure out how to do this in Petapoco. However if your UI doesn't show all the nested data until the user clicks on an expander button (or similar) I'd use an AJAX call at that point rather than get all the data initially.
The answer is correct, but I came to this page from another forum and no one there could make this work, so I thought I would chip in what I did to make things clearer. Basically, I had code like the following:
var sql = "select * from someTable where tableId = #0";
var listOfStuff = _petapoco.Fetch<FirstType, SecondType, ThirdType, FourthType, FirstType>(new RelatorClass().MapIt, sql, idVar);
Since I needed to add in a fifth poco, and all the Fetch methods eventually lead to the master Query method listed above in the accepted answer, I had to do this:
var sql = "select * from someTable where tableId = #0";
Func<FirstType, SecondType, ThirdType, FourthType, FifthType, FirstType> mapIt = new RelatorClass().MapIt;
var listOfStuff = _petapoco.Query<FirstType>(new[] { typeof (FirstType), typeof (SecondType), typeof (ThirdType), typeof (FourthType), typeof(FifthType)}, mapIt, sql, idVar).ToList();
Now I can query with 5 pocos and I didn't have to modify the PetaPoco code. The only other thing to do would be to add to your relator class so you can tell PetaPoco where to map the new data and you're good to go.
Delegate:
Note: you don't have to return the (UserActivity,int) anonymous type like this, you can return a single type without the parenthesis! I'm just lazy and don't want to create a new model for the return type.
private delegate (UserActivity, int) GetIt(int fk_AccountTypeValue, UserActivityModel ua, User u, Client c, Client_Account ca);
SQL Operation:
public List<(UserActivity,int)> SomeMethodName(int orgnizationID)
{
var sql = Sql.Builder
.Select("TOP(200) at.FK_AccountTypeValue, ua.*, u.*, c.*, ca.*")
.From("UserActivity ua")
.LeftJoin("Users u").On("u.PK_UserID = ua.FK_UserID")
.LeftJoin("Client c").On("c.FK_UserID = u.PK_UserID")
.LeftJoin("Client_Account ca").On("ca.FK_ClientID = c.PK_ClientID")
.LeftJoin("AccountType at").On("at.PK_AccountType = c.FK_AccountTypeID")
.Where("u.FK_OrganizationID =#0", orgnizationID)
.OrderBy("ua.Timestamp desc");
GetIt obj = new GetIt(youKnowIt);
var typs = new Type[]{typeof(int), typeof(UserActivityModel), typeof(User), typeof(Client), typeof(Client_Account)};
var uaList = _database.Query<(UserActivity, int)>(typs, obj, sql.SQL, sql.Arguments).ToList();
return uaList;
}
Method pointed to by the delegate:
private (UserActivity,int) youKnowIt(int fk_AccountTypeValue, UserActivityModel ua, CurrentDesk.Models.User u, CurrentDesk.Models.Client c, CurrentDesk.Models.Client_Account ca)
{
// do stuff
var uam = new UserActivity()
{
// assign stuff
};
return (uam, fk_AccountTypeValue);
}

How to use a c# method in a sql stored procedure

I wanted to use linq as so:
MyDBEntities context = new MyDBEntities();
context.MyTable.Where(i => MyMethod(i.column, valueToTest).ToList();
with
public bool MyMethod(Object a, Object b)
but apparently using such a method with isn't possible
so I was hopping I could use the methode in a stored procedure I would be able to call with linq
do you think is it possible ?
Generally it is possible to create C# function and use it in SQL Server (2005 and newer) but it is not so simple - you must use SQL CLR which means separate project for your function, special references, special types, etc. At last you must deploy the assembly to SQL server to be able to use the function in SQL. General documentation also covering how to create custom function:
Creating SQL Server Objects in Managed Code
Once you have your function on SQL server you can use it within stored procedure and you can use it within query. I'm actually not sure if you can import these functions into Linq-to-sql or EF model and use them in Linq-to-sql or Linq-to-entities queries.
Take a look here for a complete sample:
Calling custom methods in LINQ-to-SQL
I hope I understand you correctly.
Let's say that MyTable is a database table that contains the columns Name, and Address
Here's how you would get a value back whether the results contain the specified value you passed.
public void SomeMethod()
{
MyTable table= new MyTable();
bool b= MyMethod(table.Name, "Fred");
if(b)
//Do something
else
//Do something else
}
public bool MyMethod(MyTable a, object value)
{
using(var context= new MyDBEntities())
{
return context.MyTable.Where(i => a == value).Any();
}
}
This is what the database table 'MyTable' looks like behind the scenes.(the data context generated this)
public class MyTable
{
public string Name { get; set; }
public string Address { get; set; }
}
So you can see in the first method I pass table.Name to MyMethod, that's only possible because MyTable has a pubic property called Name. Also notice that we are using type Object for the value, as the parameter could an int, a string, a date time, who knows.
Note: This is untested code, but should get off to right track if I understand you correctly.

Categories

Resources