Calling SQL scalar function from Linq Query - sql

There are many similar threads here, but none of them can't solve my problem.
As far as I know, there are two methods for calling SQL scalar function from linq.
1. method:
I've added my function to .edmx file using XML editor and my function is:
<Function Name="prisustvoPostotci" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="false" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo">
<CommandText>
select dbo.prisustvoPostotci(#matica_ID,#godina)
</CommandText>
<Parameter Name="matica_ID" Type="int" Mode="In" />
<Parameter Name="godina" Type="int" Mode="In" />
</Function>
I went to model browser and double clicked on my function in Function Imports and changed return collection type to Int32. My function is returning integer.
Now I can call my function from linq using:
using (DB_Entities dm = new DB_Entities())
{
dm.prisustvoPostotci(1, 2016).FirstOrDefault();
}
It returns valid integer value!
But if I call my function from Linq Query like this:
query = query.Where(x => x.date.Value.Year == max_year &&
dm.prisustvoPostotci(x.ID, max_year).FirstOrDefault() >= 50);
It throws this error:
LINQ to Entities does not recognize the method
'System.Data.Entity.Core.Objects.ObjectResult1[System.Nullable1[System.Int32]]
prisustvoPostotci(System.Nullable1[System.Int32],
System.Nullable1[System.Int32])' method, and this method cannot be
translated into a store expression.
2. method:
I've added my function to .edmx file using XML editor and my function is:
<Function Name="prisustvoPostotci" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="false" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo">
<CommandText>
select dbo.prisustvoPostotci(#matica_ID,#godina)
</CommandText>
<Parameter Name="matica_ID" Type="int" Mode="In" />
<Parameter Name="godina" Type="int" Mode="In" />
</Function>
I went to model browser and double clicked on my function in Function Imports and changed return collection type to Int32. My function is returning integer.
Then I created a partial class and wrote this method:
public static class EntityFunctions
{
[EdmFunction("Model.Store", "prisustvoPostotci")]
public static int prisustvoPostotci(int matica_ID, int godina)
{
throw new NotSupportedException("Direct calls not supported");
}
}
"Model.Store" is the correct name of my Model Store read from my Schema Namespace inside .edmx file.
Now, if I call my function from linq using:
EntityFunctions.prisustvoPostotci(119, 2016).ToString()
It throws this error:
throw new NotSupportedException("Direct calls not supported");
Also, if I call my function from Linq Query like this:
query = query.Where(x => x.date.Value.Year == max_year &&
EntityFunctions.prisustvoPostotci(x.ID, max_year) >= 50);
It throws this error:
The function or function import 'Model.Store.prisustvoPostotci' is not composable. A non-composable function or function import cannot be called in a query expression.
I tried to edit my .edmx file and change a property IsComposable="true", but it gives me this error:
Functions declaring command text cannot be composed.
Can You help me solve this problem!?
Many thanks in advance !!
::cheers::
Josip

Thanks to Gerd Arnold I realize that scalar functions cannot be used inside query where statement.
Here is how I managed to filter my query by calling scalar function outside query:
var result = query.ToList();
for (int i = 0; i < result.Count; i++)
{
// prisustvoPostotci(ID, year) is my scalar function
if (dm.prisustvoPostotci(result[i].ID, max_year).FirstOrDefault() >= 50)
{
result.Remove(result[i]);
i--;
}
}
This way calling scalar function will work and we can remove matching records from the result!
Hope this will help someone.
:: cheers ::
Josip

Related

A better way to obtain SQL data than I am currently using

I need to retrieve data from an external SQL Server database and view it (not store it) in my ASP.NET MVC application. The only way I can connect to the server is by using a server name, port number and access to a SQL Server stored procedure that the server owners provide.
Currently the only way I know how to do this is by:
a) Writing a .sql script to retrieve the data. Note I can't see any of the SQL Server tables, I just have the name of the stored procedure and the criteria. I save the result as a .txt file
EXEC dbo.listData #Criteria = '<Portal><Data Name="Data" Format="Narrow" Interval="5m">
<Where>
<Column Name="Point" Project="XXX" Value="XXXX" Operator="LIKE" />
<Column Name="Point" Project="YYY" Value="YYYY" Operator="LIKE" />
</Where>
</Data>
</Portal>'
, #StartDateTime = '12/28/2020',
#EndDateTime = '12/29/2020'
b) creating a model class
public class Alarm_DataModel
{
public string Project { get; set; }
public string Point { get; set; }
}
c) Creating a controller to put the data into the model to pass to the view
public ActionResult Index()
{
string[] texts = System.IO.File.ReadAllLines(Server.MapPath("~/App_Data/Test/test.txt"));
texts = texts.Skip(2).ToArray();
List<Alarm_DataModel> Alarm_Data = new List<Alarm_DataModel>();
foreach (string row in texts)
{
if (!string.IsNullOrEmpty(row))
{
int x = row.Length;
Alarm_Data.Add(new Alarm_DataModel
{
Project = row.Substring(0, 25),
Point = row.Substring(26, 60), 6
});
}
}
ViewBag.Data = texts;
return View(Alarm_Data);
}
My question may have been answered many times, but I have looked and can't find anything that I can interpret.
Is there a way that I can obtain the data using my controller without having to rely on the .sql script being ran and generating the .txt file?
With my limited access to the database, what is the best way to query using the provided stored procedure and populating my model to pass to the view?
With Dapper the code would look something like this:
using Dapper;
using System.Data.SqlClient;
using System.Linq;
private IEnumerable<Alarm_DataModel> GetAlarmList()
{
var sql = #"EXEC dbo.listData #Criteria = '<Portal><Data Name=""Data"" Format=""Narrow"" Interval=""5m"">
<Where>
<Column Name=""Point"" Project=""XXX"" Value=""XXXX"" Operator=""LIKE"" />
<Column Name=""Point"" Project=""YYY"" Value=""YYYY"" Operator=""LIKE"" />
</Where>
</Data>
</Portal>'";
using( var connection = new SqlConnection("(connecting string here)") )
{
var values = new { StartDateTime = "2017.1.1", EndDateTime = "2017.12.31" };
return connection.Query<Alarm_DataModel>(sql, values).ToList();
}
}
[HttpGet]
public ActionResult Index()
{
var alarmList = GetAlarmList();
ViewBag.Data = "texts";
return View(alarmList);
}
If the Stored Procedure can execute and reply with some data, everything is working with the credentials you are given. This is a normal production security setup.
That you also want to view tables, is a different concern and can be solved with different credentials or access to a another server.
Thank you Frank Nielsen, your recommended code worked with just some minor edits with the criteria values. For some reason I needed to include them in the query for it to work.
Much appreciated, thanks
Here is the final code:
public class AlarmDataController : Controller
{
// GET: AlarmData
private IEnumerable<Alarm_DataModel> GetAlarmList()
{
var sql = #"EXEC dbo.listData #Criteria = '<Portal><Data Name=""Data"" Format=""Narrow"" Interval=""5m"">
<Where>
<Column Name=""Point"" Project=""XX"" Value=""XXXX"" Operator=""LIKE"" />
<Column Name=""Point"" Project=""YY"" Value=""YYYY"" Operator=""LIKE"" />
</Where>
</Data>
</Portal>', #StartDateTime = '12/28/2020',#EndDateTime = '12/29/2020'";
string connectionString = "Integrated Security=True;Data Source=XXXX;Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";
using (var connection = new SqlConnection(connectionString))
{
return connection.Query<Alarm_DataModel>(sql).ToList();
}
}
[HttpGet]
public ActionResult Index()
{
var alarmList = GetAlarmList();
return View(alarmList);
}
}

.NET Core - EntityFrameworkCore - Unable to cast object of type 'Query.Internal.EntityQueryable` to type 'DbSet`

I try to implement a search with entity when a search field is provided
but I get a weird casting error I just dont understand
Unable to cast object of type 'Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[SomeApp.Models.Partner]' to type 'Microsoft.EntityFrameworkCore.DbSet`1[SomeApp.Models.Partner]'.
here is the code of my controller entry point
I tried forcing the cast, but apparently there is something wrong with my code
[HttpPost]
public async Task<ActionResult<PartnersFetch>> GetPartners(PartnersSearch partnersSearch)
{
DbSet<Partner> data = _context.Partners;
if (partnersSearch.SearchById != null)
{
// the following line causes problems :
data = (DbSet <Partner>) data.Where( p => p.Id == partnersSearch.SearchById.GetValueOrDefault());
}
thanks for helping me on this
I forgot to use AsQueryable
var data = _context.Partners.AsQueryable();
if (partnersSearch.SearchById != null)
{
data = data.Where( p => p.Id == partnersSearch.SearchById.GetValueOrDefault());
}
data.Where(...) will return an IQueryable which you can materialize as follows
List<Partner> myResult = data.Where(...).ToList();
The DbSet<Partner> is only the set on which you can query data. Your goal very likely is to get the partners out of it, right?

Yii foreach error?

I have calling a function. get table number (result=0) results and updated same table value 0 to 1. i am using update query.i have run this function to return error :: Missing argument 2 for CDbCommand::update().
public function newdisplaycontent()
{
$count = Yii::app()->db->createCommand()
->select()
->from('scrolltable')
->where('result=:result', array(':result'=>0))
->queryAll();
$rs=array();
//print_r($count);
foreach($count as $item){
//process each item here
$rs=$item['ID'];
$user=Yii::app()->db->createCommand()
->update("scrolltable SET result = 1")
->where('ID=:id', array(':id'=>$rs));
}
return $rs;
}
thanks for your feature help..
The correct syntax of update() would be like below:
$user=Yii::app()->db->createCommand()
->update("scrolltable",array("result" => "1"))
->where('ID=:id', array(':id'=>$rs));
As official document:
update() Creates and executes an UPDATE SQL statement. The method will properly escape the column names and bind the values to be updated.
public integer update(string $table, array $columns, mixed $conditions='', array $params=array ( ))

Powershell: using splatting with scriptblocks?

I wrote a simple function to create a hashtable out of an xml-file which will hold params that should be passed to a cmdlet.
My XML-File looks like this:
<params>
<Parameter>
<Name>After</Name>
<Value>(get-date).adddays(-7)</Value>
</Parameter>
<Parameter>
<Name>Log</Name>
<Value>System</Value>
</Parameter>
</params>
My function looks like this:
function Create-ParamTable {
param ([string]$ConfigFile,[string]$Root = "params", [string]$Child = "Parameter")
$hash = #{}
[xml]$config = get-content $ConfigFile
foreach ($param in $config.$root.$child) {
$hash.add($param.name,$param.value)
}
return $hash
}
I'm using that returned hashtable with the splat-operator:
PS > $h = create-paramtable -configfile c:\tmp\params.xml ; get-eventlog #h
I want to be able to pass scriptblocks as parameter-value in order to use other cmdlets like get-date to calculate a few values.
For example: I want to store params for get-eventlog in a xml-config-file but I always want to have the logs from the past 7 days.
How do I have to store the value in order to get it executed when passing it to a cmdlet via splatting?
You need to evaluate the parameter values before sticking them in the hashtable. Something like this.
foreach ($param in $config.$root.$child) {
$hash.add($param.name,(Invoke-Expression $param.value))
}
This worked for me in limited testing:
$hash.add($($param.name),$($param.value))

Call to a member function num_rows() on a non-object [duplicate]

This question already has answers here:
Call to a member function on a non-object [duplicate]
(8 answers)
Closed 8 years ago.
I need to get the number of rows of a query (so I can paginate results).
As I'm learning codeigniter (and OO php) I wanted to try and chain a ->num_rows() to the query, but it doesn't work:
//this works:
$data['count'] = count($this->events->findEvents($data['date'], $data['keyword']));
//the following doesn't work and generates
// Fatal Error: Call to a member function num_rows() on a non-object
$data['count2'] = $this->events->findEvents($data['date'], $data['keyword'])->num_rows();
the model returns an array of objects, and I think this is the reason why I can't use a method on it.
function findEvents($date, $keyword, $limit = NULL, $offset = NULL)
{
$data = array();
$this->db->select('events.*, venues.*, events.venue AS venue_id');
$this->db->join('venues', 'events.venue = venues.id');
if ($date)
{
$this->db->where('date', $date);
}
if ($keyword)
{
$this->db->like('events.description', $keyword);
$this->db->or_like('venues.description', $keyword);
$this->db->or_like('band', $keyword);
$this->db->or_like('venues.venue', $keyword);
$this->db->or_like('genre', $keyword);
}
$this->db->order_by('date', 'DESC');
$this->db->order_by('events.priority', 'DESC');
$this->db->limit($limit, $offset); //for pagination purposes
$Q = $this->db->get('events');
if ($Q->num_rows() > 0)
{
foreach ($Q->result() as $row)
{
$data[] = $row;
}
}
$Q->free_result();
return $data;
}
Is there anything that i can do to be able to use it? EG, instead of $data[] = $row; I should use another (OO) syntax?
You function findEvents is returning $data which you declared to be an array at the start. This is not an object and does not allow you to access functions using the member access syntax.
To count the number of values in an array see count
Also, from what I understand you not only want the query results returned as an array, but you want to be able to access methods on the result $Q = $this->db->get('events'); However this is not possible as this is a local variable and it is not being returned out. The function here has a result type of array and is not an object and thus has no access to anything, but the array. One solution to this is to return an associative array:
return array("results" => $data, "count" => $Q->num_rows());
Then use array syntax to access the count or the results. Another option is return a new object that has result and count fields and use accessor methods to get to those.