Convert Dataset to IQueryable<T> or IEnumerable<T> - linq

Since there is no Linq to DB2 yet (c'mon IBM!), and I want to deal with IQueryables or IEnumerables in my code, how would I convert a DataTable to an IQueryable? Or an IEnumerable?
I have an interface and a class that matches the columns in the datatable...
IQueryable<IMyData> GetAS400Data(..parameters..)
{
DataSet d = GetData();
...
//Some code to convert d to IQueryable<IMyData>
}
DataTable.Rows does not support .AsQueryable, since MSFT yanked it, so I'm not sure what to do here.

table.AsEnumerable()...
table.AsEnumerable().AsQueryable()...
However, you'd need to write your own translation (Select) to your type; and the IQueryable<T> would still be using LINQ-to-Objects; the only purpose (in this scenario) of using IQueryable<T> over IEnumerable<T> would be to use expressions for some other reason - perhaps for the dynamic LINQ library.

you can use something like this.
DataSet ds = GetData();
DataTable dt= ds.Tables[0];
var query =
from row in dt.AsEnumerable()
select new IMyData()
{
property1= row[0],
property2= row[1]
};

Take a look here it seems that a provider with entity framework for DB2 exists.

Related

How to invoke a UDF with LINQ in Cosmos DB SDK v3?

The documentation for LINQ to SQL translation for Cosmos DB states:
User-Defined Function Extension function: Supports translation from the stub method UserDefinedFunctionProvider.Invoke to the corresponding user-defined function.
However, this function is not publicly accessible in .NET SDK v3 (though it is in v2). So what's the workaround until the bug has been fixed?
There are several workarounds:
Don't use LINQ.
Use LINQ but without calling UDFs, then get the query string using query.ToQueryDefinition.QueryText and manipulate it to insert UDF calls at the places you need them, and then evaluate the SQL string (yuck!).
Hack it. First add a dummy method:
public static object Invoke(string udfName, object[] arguments) { return null; }
Use this method in your lambda expressions whenever you would otherwise use UserDefinedFunctionProvider.Invoke. Implement an ExpressionVisitor that replaces all occurrences of MethodCallExpression mce where mce.Method matches the dummy method by
Expression.Call(null, udfMethod, mce.Arguments[0], mce.Arguments[1])
where udfMethod is
MethodInfo udfMethod = typeof(CosmosClient).Assembly
.GetType("Microsoft.Azure.Cosmos.Linq.UserDefinedFunctionProvider", throwOnError: true)
.GetMethod("Invoke", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
Use the resulting expression in your Where, Select etc.
(Add a unit test to check when the bug has been fixed and the hack can be removed.)
I am using the Microsoft.Azure.Cosmos 3.22.1 SDK for my example with the UDF (user defined functions) and it worked fine.
I used the example from microsoft.
It is of course also possible to use the UDF in combination with where clauses.
CosmosClient client = new(endpoint, key);
IQueryable<Product> queryable = client
.GetContainer("database", "products")
.GetItemLinqQueryable<Product>()
.Select(b => new Product{ id = b.id, price = b.price, priceWithTax = CosmosLinq.InvokeUserDefinedFunction("tax", b.price).ToString() });
var productIterator = queryable.ToFeedIterator<Product>();
while (productIterator.HasMoreResults)
{
var responseMessage = await productIterator.ReadNextAsync();
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(responseMessage));
}

String extension method in Linq query

How to use string extension method in linq query:
public NewsType GetNewsType(string name)
{
var newsType = db.NewsTypes.FirstOrDefault(x => x.Name.ToFriendlyUrl() ==
name.ToFriendlyUrl());
return newsType;
}
Above query x.Name.ToFriendlyUrl() is not allowed at the minute. Is anyone know how to achieve with it.
Extension methods are indeed allowed in LINQ queries, moreover the LINQ methods themselves are implemented as extension methods.
It's quite another issue however, to use extension methods (or most other methods) in LINQ-to-SQL or LINQ-to-Entities queries. Those queries are not actually run in the C# code, but they are treated like expressions that are translated to SQL. I.e.
db.News.Where(x => x.Published).Select(x => x.Name)
is translated to the SQL Statement
Select Name
From News
Where Published = 1
and it's results are returned to the C# code.
Since there is not way to transfer the ToFriendlyUrl() method to SQL, your code throws an error.
You have basically, two solutions/workarounds. One is to transform the call to a form could be translated into SQL, e.g. if the ToFriendlyUrl() method was just:
public static string ToFriendlyURL(this string value)
{
return value.ToLower();
}
you can inline that code in the LINQ call, and that would work. If however, the methods is more complex, than your only solution is to just fetch the data from the base and then process it on the C# side:
var newsTypeQuery = db.NewsTypes.Where(x => // other conditions, if any);
var newsTypes = newsTypes.ToList(); //forces execution of the query
// the result is now a C# list
var newsType = newsTypes.FirstOrDefault(x =>
x.Name.ToFriendlyUrl() == name.ToFriendlyUrl());
Assuming the NewsTypes is an IQueryable this is a result of Entity Framework not being able to convert you extension method into SQL (how should it?). Unless you can rewrite your predicate into something that Entity Framework can translate into SQL you will have to perform the query client side:
public NewsType GetNewsType(string name)
{
var newsType = db.NewsTypes.AsEnumerable().FirstOrDefault(x => x.Name.ToFriendlyUrl() == name.ToFriendlyUrl());
return newsType;
}
Notice how AsEnumerable() has been added before FirstOrDefault. Unfortunately this may pull all the rows returned by NewsTypes from the server to client and thus may be quite costly.
This
var newsType = db.NewsTypes.FirstOrDefault(
x => x.Name.ToFriendlyUrl() == name.ToFriendlyUrl());
can't be done in Entity Framework. ToFriendlyUrl is an extension method. It's something that is in the "client" computer. The query will be executed on the SQL server. The SQL server doesn't have a ToFriendlyUrl function.
The "standard" solution is to save in a second column named FriendlyName a precalculated version of the ToFriendlyUrl(), so your query becomes:
var friendlyName = name.ToFriendlyUrl();
var newsType = db.NewsTypes.FirstOrDefault(
x => x.FriendlyName == friendlyName);
Instead try like this
public NewsType GetNewsType(string name)
{
var newsType = db.NewsTypes.FirstOrDefault(x => x.Name == name).ToFriendlyUrl();
return newsType;
}

LINQ to Entities does not recognize the method 'Boolean CheckMeetingSettings(Int64, Int64)' method

I am working with code first approach in EDM and facing an error for which I can't the solution.Pls help me
LINQ to Entities does not recognize the method 'Boolean
CheckMeetingSettings(Int64, Int64)' method, and this method cannot be
translated into a store expression.
My code is following(this is the query which I have written
from per in obj.tempPersonConferenceDbSet
where per.Conference.Id == 2
select new PersonDetials
{
Id = per.Person.Id,
JobTitle = per.Person.JobTitle,
CanSendMeetingRequest = CheckMeetingSettings(6327,per.Person.Id)
}
public bool CheckMeetingSettings(int,int)
{
///code I have written.
}
Please help me out of this.
EF can not convert custom code to SQL. Try iterating the result set and assigning the property outside the LINQ query.
var people = (from per in obj.tempPersonConferenceDbSet
where per.Conference.Id == 2
order by /**/
select new PersonDetials
{
Id = per.Person.Id,
JobTitle = per.Person.JobTitle,
}).Skip(/*records count to skip*/)
.Take(/*records count to retrieve*/)
.ToList();
people.ForEach(p => p.CanSendMeetingRequest = CheckMeetingSettings(6327, p.Id));
With Entity Framework, you cannot mix code that runs on the database server with code that runs inside the application. The only way you could write a query like this, is if you defined a function inside SQL Server to implement the code that you've written.
More information on how to expose that function to LINQ to Entities can be found here.
Alternatively, you would have to call CheckMeetingSettings outside the initial query, as Eranga demonstrated.
Try:
var personDetails = obj.tempPersonConferenceDbSet.Where(p=>p.ConferenceId == 2).AsEnumerable().Select(p=> new PersonDetials
{
Id = per.Person.Id,
JobTitle = per.Person.JobTitle,
CanSendMeetingRequest = CheckMeetingSettings(6327,per.Person.Id)
});
public bool CheckMeetingSettings(int,int)
{
///code I have written.
}
You must use AsEnumerable() so you can preform CheckMeetingSettings.
Linq to Entities can't translate your custom code into a SQL query.
You might consider first selecting only the database columns, then add a .ToList() to force the query to resolve. After you have those results you van do another select where you add the information from your CheckMeetingSettings method.
I'm more comfortable with the fluid syntax so I've used that in the following example.
var query = obj.tempPersonConferenceDbSet
.Where(per => per.Conference.Id == 2).Select(per => new { Id = per.Person.Id, JobTitle = per.Person.JobTitle })
.ToList()
.Select(per => new PersonDetails { Id = per.Id,
JobTitle = per.JobTitle,
CanSendMeetingRequest = CheckMeetingSettings(6327, per.Person.Id) })
If your CheckMeetingSettings method also accesses the database you might want to consider not using a seperate method to prevent a SELECT N+1 scenario and try to express the logic as part of the query in terms that the database can understand.

linq to sql ExecuteQuery() as IQueryable

ExecuteQuery() method returns an IEnumerable but is there a way to make it return IQueryable?
Well, you can call AsQueryable, but it won't do any good. The problem is that when you use ExecuteQuery, the query isn't composable because LINQ to SQL doesn't "understand" it as such.
One of the core purposes of IQueryable<T> is to allow the various aspects of a query to be composed together and then LINQ to SQL can convert them into a single SQL query. That just doesn't work when one of the bits of the query is effectively opaque.
If you want, you can export the result of your query to a list, and next convert it to IQueryable. See the next example code:
public IQueryable<Data> GetData()
string query = #"select ...";
object[] parameters = new object[...]{...};
var resultQuery = this.DataContext.ExecuteQuery<SICDB.Data>(query, parameters);
var tempList = resultQuery .ToList();
return tempList.AsQueryable();
}

How would I get the column names from a Model LINQ?

I am looking to get a list of the column names returned from a Model. Anyone know how this would be done, any help would be greatly appreciated.
Example Code:
var project = db.Projects.Single(p => p.ProjectID.Equals(Id));
This code would return the Projects object, how would I get a list of all the column names in this Model.
Thanks
This would be nice to have as an extension method:
public static class LinqExtensions
{
public static ReadOnlyCollection<MetaDataMember> ColumnNames<TEntity> (this DataContext source)
{
return source.Mapping.MappingSource.GetModel (typeof (DataContext)).GetMetaType (typeof (TEntity)).DataMembers;
}
}
example:
var columnNames = myDataContext.ColumnNames<Orders> ();
Thanks guys, you got me started on the right track. I found my solution with the following code. I can then iterate through the DataMembers and pull out their individual properties such as name, type, etc.
var db = new GMPDataContext();
var columnNames = db.Mapping.MappingSource
.GetModel(typeof(GMPDataContext))
.GetMetaType(typeof(Project))
.DataMembers;
Your Projects wrapper will have a set of properties each with a [Column] attribute. So just use reflection to enumerate the properties with that attribute.
Using Todd Smiths(+1) solution you get all properties (included entity sets, etc).
To filter out all non-column properties this will do the trick:
var columnNames = db.ColumnNames<Orders>().Where(n => n.Member.GetCustomAttributes(typeof(System.Data.Linq.Mapping.ColumnAttribute), false).FirstOrDefault() != null).Select(n => n.Name);
I am sorry, I don't have working experience with LINQ.
This is purely based on looking at MSDN.
DataContext has a Mapping property, which returns an instance of MetaModel.
MetaModel has GetMetaType, which takes a Type. In your case it could be typeof(Project).
GetMetaType returns a MetaType which has the GetDataMember method, which takes a MemberInfo parameter. You will have to use reflection on your Projects object to get the MemberInfo object.
The MetaDataMember instance returned by GetDataMember should have all the things, you need.
I hope I am somewhat in right direction (purely looking at MSDN & traversing)
Your columns should be mapped as properties on your Project model. I'm not sure if you can get the underlying database structure when using LINQ to SQL. The entire point of LINQ to SQL is to abstract the database away.
Here an another way:
public string[] GetColumnNames()
{
var propnames = GetPropertyNames(_context.Users);
return propnames.ToArray();
}
static IEnumerable<string> GetPropertyNames<T>(IEnumerable<T> lst)
{
foreach (var pi in typeof(T).GetProperties())
{
yield return pi.Name;
}
}

Resources