Calling user defined functions in Entity Framework 4 - c#

I have a user defined function in a SQL Server 2005 database which returns a bit. I would like to call this function via the Entity Framework. I have been searching around and haven't had much luck.
In LINQ to SQL this was obscenely easy, I would just add the function to the Data context Model, and I could call it like this.
bool result = FooContext.UserDefinedFunction(someParameter);
Using the Entity Framework, I have added the function to my Model and it appears under SomeModel.Store\Stored Procedures in the Model Browser.
The model has generated no code for the function, the XML for the .edmx file contains:
<Function Name="UserDefinedFunction" ReturnType="bit" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="true" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo">
<Parameter Name="someParameter" Type="int" Mode="In" />
</Function>
The closest I could get was something like this:
bool result = ObjectContext.ExecuteFunction<bool>(
"UserDefinedFunction",
new ObjectParameter("someParameter", someParameter)
).First();
But I got the following error message:
The FunctionImport
'UserDefinedFunction' could not be
found in the container 'FooEntities'.
Names have been changed to protect the innocent.
tldr: How do I call scalar valued user defined functions using Entity Framework 4.0?

I have finally worked it out :D For scalar functions you can append the FROM {1} clause.
bool result = FooContext.CreateQuery<bool>(
"SELECT VALUE FooModel.Store.UserDefinedFunction(#someParameter) FROM {1}",
new ObjectParameter("someParameter", someParameter)
).First();
This is definitely a case for using LINQ to SQL over EF.

If you want to call table-valued function in MS SQL via Entity Framework;
You can use this:
var retval = db.Database.SqlQuery<MyClass>(String.Format(#"select * from dbo.myDatabaseFunction({0})", id));

calling user defined static type SQL function
You can use ExecuteStoreQuery,
first parameter takes the SQL function calling statement in string format
second parameter takes an object of SqlParameter class in which you pass your function parameter name with its value. as shown in the below method
public string GetNumberOFWorkDays(Guid leaveID)
{
using (var ctx = new INTERNAL_IntranetDemoEntities())
{
return ctx.ExecuteStoreQuery<string>(
"SELECT [dbo].[fnCalculateNumberOFWorkDays](#leaveID)",
new SqlParameter { ParameterName = "leaveID", Value = leaveID }
).FirstOrDefault();
}
}

Calling a function in EF4 with ODPNET beta 2 (Oracle), which returns a NUMBER:
using (var db = new FooContext())
{
var queryText = "SELECT FooModel.Store.MY_FUNC(#param) FROM {1}"
var result = db.CreateQuery<DbDataRecord>(queryText,new ObjectParameter("param", paramvalue));
return result.First().GetDecimal(0);
}
I'm adding it here because based on Evil Pigeon's code I could figure it out for Oracle. (Also upvoted his answer).

ObjectResult result = context.ExecuteStoreQuery("select EmployeeName from User where Id = {0}", 15);
http://msdn.microsoft.com/en-us/library/ee358758.aspx

You might find this helpful. It would appear that you can only call them via eSQL directly and not using Linq.

thought id add a note for anybody else that come across this, if your sql function parameter is nullable you have to call the function in sql with parameter value of "default". To call a function like that using Entity Framwork try
bool result = FooContext.CreateQuery<bool>(
"SELECT VALUE FooModel.Store.UserDefinedFunction(null) FROM {1}"
).First();

Related

Teradata Query Using Dapper C#

I am trying to dynamically query a Teradata database using Dapper but am having some issues. Here is the code:
// model variable is the parameter passed in with search information
using (IDbConnection con = new TdConnection(connection.GetConnectionString()))
{
var builder = new SqlBuilder();
var selector = builder.AddTemplate($"SELECT * FROM Temp_Table /**where**/");
if (model.Id != 0)
{
builder.Where("Id = ?", new { model.Id });
}
if (!string.IsNullOrEmpty(model.Employee_Id))
{
builder.Where("Employee_Id = ?", new { model.Employee_Id });
}
var data= con.Query<TableModel>(selector.RawSql, model).ToList();
return data;
}
The error I am getting is:
[Teradata Database] [3939] There is a mismatch between the number of
parameters specified and the number of parameters required.
I have used very similar code to query DB2 which worked just fine; what do I need to do differently with Teradata?
Managed to figure it out. Changed the line for getting the data to:
var data= con.Query<TableModel>(selector.RawSql, selector.Parameters).ToList();
Not sure why passing in the model worked just fine in my DB2 version but not this Teradata version.
At first glance it appears to be falling through and not adding any "where" condition. Try to structure it in such a way that if it falls through then add 1=1 or a Teradata equivalent if that doesn't work.
I'm unfamiliar with the SqlBuilder() class; but if you have a way of seeing if there aren't any Where constraints added, then to add a generic one. Or, a dirtier way would be to keep a bool reference and check at the end.
Update
Try passing in the parameters:
var data= con.Query<TableModel>(selector.RawSql, selector.Parameters).ToList();

EntityCommandCompilationException specified method not supported Entity Framework

I am new to Entity Framework and I keep getting the EntityCommandCompilationException specified method not supported in Entity Framework. I can't figure out why this exception is being raised.
I have created a custom UDF aggregate function my_Func() for my installation of MySQL server 5.7 using the guidelines posted here. It works just like any ordinary aggregate function e.g. Sum() would work. i.e. I can execute the statement select my_Func(Column4) from db.table and it returns the desired result as a double. I have tested it and it works in MySQL server. I want to be able to use this method in a linq to entities query and in order to do this I have done the following.
using (var context = new dbEntities())
{
var results = from items in context.table
group items by new
{ items.Column1, items.Column2 } into groupingItem
select new OutputType()
{
GroupedResult = groupingItem.OrderBy(x => x.Column3).Select(x => x.Column4).my_Func()
};
}
I created a static class which contains the method.
public static class ModelDefinedFunctions
{
[DbFunction("dbModel.Store", "my_Func")]
public static double my_Func(this IEnumerable<double> items)
{
throw new NotSupportedException("Direct calls are not supported.");
}
}
in the .edmx file I have added the following tag manually
<Function Name="my_Func" ReturnType="double" Aggregate="true"
BuiltIn="false" NiladicFunction="false"
IsComposable="true" ParameterTypeSemantics="AllowImplicitConversion" Schema="db">
<Parameter Name="value" Type="Collection(double)" Mode="In" />
</Function>
Use the following code to see the SQL that Entity Framework creates and then try to run it directly on your database.
IQueryable query = from x in appEntities
where x.id = 32
select x;
var sql = ((System.Data.Objects.ObjectQuery)query).ToTraceString();
or in EF6:
var sql = ((System.Data.Entity.Core.Objects.ObjectQuery)query)
.ToTraceString();
(stolen from How do I view the SQL generated by the Entity Framework?)
Most likely EF is not generating the query you think it is. You might consider building the query in a StringBuilder and executing it via EF like this:
using (var db = new DbEntity())
{
var sqlStatement = new StringBuilder();
sqlStatement.AppendFormat("SELECT * FROM TABLENAME WHERE ID = '{0}' ", id);
var result = db.ExecuteStoreQuery<MyTableObject>(sqlStatement.ToString()).ToList();
}

Call a Scalar valued function in entity framework 6

How can I call a scalar function in entity framework 6 ?
I have tried the following code
using (MhEntities DContext = new MhEntities())
{
var Account_IdParameter = Account_Id.HasValue ? new ObjectParameter("Account_Id", Account_Id) : new ObjectParameter("Account_Id", typeof(long));
string res = ((IObjectContextAdapter)DContext).ObjectContext.CreateQuery<string>("MoneyforHealthEntities.Fn_LEVEL0_Acount_Id", Account_IdParameter).FirstOrDefault();
return Convert.ToInt64(res);
}
No need to use ObjectContext to do this. Also, I don't think you can simply pass in the name of the function, you need to give it complete, valid SQL.
So I would try something like this instead:
using (MhEntities DContext = new MhEntities())
{
string res = DContext.Database.SqlQuery<string>("SELECT MoneyforHealthEntities.Fn_LEVEL0_Acount_Id(#p0)", Account_Id).FirstOrDefault();
return Convert.ToInt64(res);
}
Since you didn't give any details about which database you are using, or the exact function definition, it's possible that the above may need further tweaking. But it should at least give you the basic idea.
DateTime ServerTime = new ContextDbEntities().Database.SqlQuery<DateTime>("Select getdate();").FirstOrDefault();
MessageBox.Show(ServerTime.ToString());
All the answers right but if someone uses a stored procedure, it needs to be edit on function import by:
1. Right-click on your function then click on edit.
2. On the edit function Import window.
3. Select Scaler on the returns a collection section.
4. Finally, click ok and save your model.
In my example, I call an Insert stored procedure that returns a string value.
using (DbModel db = new DbModel())
{
string result = db.storedprocedureName(value1,value2).FirstOrDefault();
return result;
}

Calling a SQL User-defined function in a LINQ query

I am having a hard time getting this to work. I am trying to do a radius search using the following Filter helper on an IQueryable. There are a set of other filters that get applied before RadiusSearch applies. The order shouldn't really matter since the goal is to get the query to be deferred until a ToList() operation.
public static IQueryable<ApiSearchCommunity> RadiusSearch(this IQueryable<ApiSearchCommunity> communities)
{
var centerLatitude = 30.421278;
var centerLongitude = -97.426261;
var radius = 25;
return communities.Select(c => new ApiSearchCommunity()
{
CommunityId = c.CommunityId,
City = c.City,
//Distance = c.GetArcDistance(centerLatitude, centerLongitude, c.Latitude, c.Longitude, radius)
});
}
Can I somehow write a helper like GetArcDistance above which in turn calls a UDF on SQL? The query I am trying to generate is the following
SELECT
comms.community_id,
comms.city,
comms.distance
FROM (
SELECT
c.community_id,
c.city,
dbo.udf_ArcDistance(
30.421278,-97.426261,
c.community_latitude,
c.community_longitude
) AS distance
FROM communities c) AS comms
WHERE comms.distance <= 25
ORDER BY comms.distance
Ok, I think I understand the question - the gist of it is you want to be able to call a SQL UDF as part of your Linq to Entities query.
This is if you're using database or model first:
This article explains how to do it: http://msdn.microsoft.com/en-us/library/dd456847(VS.100).aspx
To sum it up, you first need to edit your edmx file in an xml editor, in the edmx:StorageModels >> Schema section you need to specify a mapping to your sql udf, eg
<Function Name="SampleFunction" ReturnType="int" Schema="dbo">
<Parameter Name="Param" Mode="In" Type="int" />
</Function>
Then you need to create a static function somewhere with the EdmFunction attribute on it, something like this:
public static class ModelDefinedFunctions
{
[EdmFunction("TestDBModel.Store", "SampleFunction")]
public static int SampleFunction(int param)
{
throw new NotSupportedException("Direct calls are not supported.");
}
}
This method will get mapped to the UDF at query time by entity framework. The first attribute argument is the store namespace - you can find this in your edmx xml file on the Schema element (look for Namespace). The second argument is the name of the udf.
You can then call it something like this:
var result = from s in context.UDFTests
select new
{
TestVal = ModelDefinedFunctions.SampleFunction(22)
};
Hope this helps.
if you use Code-First approach, then you cannot call UDFs as you want (as of EF6) - here is the proof, and another one. You are only limited to calling UDF as a part of your SQL query:
bool result = FooContext.CreateQuery<bool>(
"SELECT VALUE FooModel.Store.UserDefinedFunction(#someParameter) FROM {1}",
new ObjectParameter("someParameter", someParameter)
).First();
which is ugly IMO and error-prone.
Also - this MSDN page says:
The process for calling a custom function requires three basic steps:
Define a function in your conceptual model or declare a function in your storage model.
which essentially means you need to use Model-First approach to call UDFs.

Entity Framework EdmFunction import is not recognized

First of all, I had a previous question some time before, which, although seems very similar, is NOT the same problem, have a look at it: Function import from SQL Server to Entity Framework runtime exception
This function, HammingDistance, used to work well after creating the scalar function in SQL Server, and then imported into the Entity Framework using the EDM function mapping. I kept building my site, made some differences around the site and the EDMX, but haven't touched the function and kept it safe. Here is the SQL function at the SQL Server:
ALTER function [dbo].[HammingDistance]
(#first bigint, #second bigint) returns int
as
begin
declare #xor bigint = #first ^ #second;
declare #one bigint = 1;
declare #diff int = 0;
declare #and bigint;
while (#xor != 0)
begin
set #and = #xor & #one;
if(#and = #one)
begin
set #diff = #diff + 1;
end
set #xor = #xor / 2;
end
return #diff;
end
And here is my C# code:
Declaration:
public static class EdmFunctionMapping
{
[EdmFunction("MainDB.Store", "HammingDistance")]
public static int GetHammingDistance(long hash1, long hash2)
{
throw new NotSupportedException("This method can only be used in a LINQ-to-Entities query");
}
}
Usage:
var query = (from p in VisibleObjects.OfType<Photo>()
let hd = EdmFunctionMapping.GetHammingDistance(targetPhoto.Analysis.Hash, p.Analysis.Hash)
let cd = Math.Abs(targetPhoto.Analysis.High.Red - p.Analysis.High.Red)+
Math.Abs(targetPhoto.Analysis.High.Green - p.Analysis.High.Green)+
Math.Abs(targetPhoto.Analysis.High.Blue - p.Analysis.High.Blue) +
Math.Abs(targetPhoto.Analysis.Low.Red - p.Analysis.Low.Red) +
Math.Abs(targetPhoto.Analysis.Low.Green - p.Analysis.Low.Green) +
Math.Abs(targetPhoto.Analysis.Low.Blue - p.Analysis.Low.Blue)
where
hd < 5 ||
(hd < 15 || cd < 100)
orderby hd ascending
select p).Take(50);
return query.ToList();
where VisibleObjects does not evaluate the Enumerable. Here it is anyway:
static IEnumerable<GlobalObject> VisibleObjects
{
get
{
return from obj in db.GlobalObjectSet where obj.IsVisible && !obj.SiteUser.IsDeactivated orderby obj.ID descending select obj;
}
}
Everything used to work about two or three weeks ago, and in that interval I've added/removed lots of things and did not check if Hamming Distance worked or not, so I can't tell anything about when it broke. It doesn't work anymore, it's just like EdmFunction attribute is not there, trying to evaluate in my application and throwing up the exception I wrote saying that it could only be used in an L2E query. I've double checked everything, even re-created the database completely and generated from EDMX, and created and imported the function using Update Model from Database, and it sees the function in EDMX, and it is available (obviously) in the EDMX file SSDL section such as:
<Function Name="HammingDistance" ReturnType="int" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="true" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo">
<Parameter Name="first" Type="bigint" Mode="In" />
<Parameter Name="second" Type="bigint" Mode="In" />
</Function>
But when it comes to the app, it just won't map to EDM! Tried several times, creating all the database from scratch, nothing changes, it throws the exception in query.ToList(), and there's no inner exception (it makes sense as it's throwing my own exception, which, should NOT be executed by the app). I have no idea of the source of the problem, am I missing something obvious?
If it doesn't recognize your Edm mapped function make sure that you didn't accidentally change the query to linq-to-objects somewhere because that is exactly the reason why you can see that error. To validate that, simply try to create another test query which will do just direct entity access and call your function. Something like:
long hash = targetPhoto.Analysis.Hash;
var query = from p in db.GlobalObjectSet.OfType<Photo>()
select new { EdmFunctionMapping.GetHammingDistance(hash, p.Analysis.Hash) };
If such query passes you have definitely problem I mentioned.
Edit:
I think I see the problem now. Your VisibleObjects returns IEnumerable => linq-to-objects. Try this instead:
public static IQueryable<GlobalObject> GetVisibleObjects(this IQueryable<GlobalObject> query)
{
return from obj in query
where obj.IsVisible &&
!obj.SiteUser.IsDeactivated
orderby obj.ID descending
select obj;
}
And call your method like:
from p in db.GlobalObjectSet.GetVisibleObjects().OfType<Photo>() ...

Categories

Resources