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>() ...
Related
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.
I create a complex search query in native SQL. It's basically something like this:
SELECT ID FROM t_Product WHERE Name LIKE #criteria
SELECT publisher, count(*) as number FROM t_Product GROUP BY publisher
It has 2 SELECT statements and I want it to be sent to DB server in one round trip.
But I can't figure out how to achieve this in Nhibernate.
I considered following options but none seems to work
Use CreateMultiQuery, but this only accept HQL, not native SQL
Use CreateSQLQuery, but call to List() only return result for the first SELECT statement
Moving to a stored procedure is not an option since the whole SQL is very dynamic.
We still use Nhibernate 1.2 thus new features in later version couldn't be used either.
Advice are welcome.
Not possible using NH version 1.2
Futures was released in version 2.1 which allows you to do exactly this.
e.g.
var blogs = s.CreateCriteria<Invoice>()
.SetMaxResults(30)
.Future<Invoice>();
var countOfInvoices = s.CreateCriteria<Invoice>()
.SetProjection(Projections.Count(Projections.Id()))
.FutureValue<int>();
So you are going to either upgrade, fall back to ADO.NET and use multiple recordsets or live with what you have! Sorry!
This is really going to be scenario-specific, but if you're stuck with NH Version 1.2, and eliminating the round-trip is your goal, you could consider rewriting this as a single query using a sub-select.
Something along the lines of:
SELECT publisher, count(*) as number,
(SELECT ID FROM t_Product WHERE Name LIKE #criteria) As theId
FROM t_Product GROUP BY publisher
Would work if your subquery only returned a single value.
I don't think that it is possible, because both queries are SELECTs.
You may try a semicolon after the first query, and two line feeds between them, this is required for some databases. I successfully run query-scripts like this. If it runs, use a debugger to see what you get back ...
If this doesn't work, you need separate round trips or switch to HQL / Criteria.
You can use MultiQuery "Hack" like this:
The procudure:
CREATE PROCEDURE [dbo].[proc_Name]
AS BEGIN
SELECT * FROM t_Question where ...
SELECT * FROM t_Question where ........
END
The NHibernate Query Code:
public void ProcdureMultiTableQuery()
{
var session = Session;
var procSQLQuery = session.CreateSQLQuery("exec [proc_Name] ?,?");// prcodure returns two table
procSQLQuery.SetParameter(0, userId);
procSQLQuery.SetParameter(1, page);
procSQLQuery.AddEntity(typeof(Question));
var multiResults = session.CreateMultiQuery()
.Add(procSQLQuery)
// More table your procedure returns,more empty SQL query you should add
.Add(session.CreateSQLQuery(" ").AddEntity(typeof(Question))) // the second table returns Question Model
.List();
if (multiResults == null || multiResults.Count == 0)
{
return;
}
if (multiResults.Count != 2)
{
return;
}
var questions1 = ConvertObjectsToArray<Question>((System.Collections.IList)multiResults[0]);
var questions2 = ConvertObjectsToArray<Question>((System.Collections.IList)multiResults[1]);
}
static T[] ConvertObjectsToArray<T>(System.Collections.IList objects)
{
if (objects == null || objects.Count == 0)
{
return null;
}
var array = new T[objects.Count];
for (int i = 0; i < array.Length; i++)
{
array[i] = (T)objects[i];
}
return array;
}
this is somewhat tricky to figure out I think, perhaps I am missing something.
I am a newbie trying to rig a database mapped via Linq-to-SQL to my server. There is a function called by clients which retrieves UserAccount from the database:
public static explicit operator Dictionary<byte, object>(UserAccount a)
{
Dictionary<byte, object> d = new Dictionary<byte, object>();
d.Add(0, a.AuthenticationDatas.Username);
int charCount = a.Characters.Count;
for (int i = 0; i < charCount; i++)
{
d.Add((byte)(i + 1), (Dictionary<byte, object>)a.Characters[i]);
}
return d;
}
What this actually does is convert a UserAccount type to my server datatype of Dictionary. UserAccount itself is retrieved from database then converted via this function.
However when I run this function, I get InvalidCastException on line:
int charCount = a.Characters.Count;
Moreover, when VS breakpoints # this line, I can wait a few seconds and proceed and the excpetion will be gone! It retrieves Characters.Count correctly after that.
Here is my Characters mapping:
[global::System.Data.Linq.Mapping.AssociationAttribute(Name="UserAccount_Character", Storage="_CharactersTBs", ThisKey="UID", OtherKey="UID")]
public EntitySet<Character> Characters
{
get
{
return this._Characters;
}
set
{
this._Characters.Assign(value);
}
}
I believe whats happening is that request is somehow executed on another thread then the one that interacts with database, and it errors out before database can actually retrieve Characters table. I am not quite sure...
Does anyone know what the problem might be and how can I syncronize it (without adding some gimp delay)?
EDIT:
Ok I narrowed down the problem. It has nothing to do with different threads networking or what not... Its just me being stupid. Here is a simple databse query which throws InvalidCastException # line int count = UA.Characters.Count;
static void Main(string[] args)
{
IEnumerable<UserAccount> query = from p in PBZGdb.Instance.AuthenticationDatas
where p.Username == "Misha" && p.Password == "123"
select p.UserAccount;
UserAccount UA = query.ElementAt(0);
int count = UA.Characters.Count;
Console.WriteLine(count);
Console.ReadKey();
}
(p.s.) UA is NOT null it indeed finds a correct instance of userAccount and it has 2 Characters. If I wait few seconds and try again exception goes away..
What am I doing wrong? This is the first time I really use a database in VS please help! :)
It looks like you are running in to a problem with the deferred execution of the EntitySet. A simple way to check this and potentially work around it will be to try calling the .Count() method, instead of accessing the .Count property.
You could have a look in the debugger as soon as you hit that line, and look at the value of a.Characters.IsDeferred also.
edit
Another thing you could try would be to force execution of the query by implicitly calling it's .GetEnumerator() (and associated .MoveNext()) by replacing your loop with a foreach:
int i = 0;
foreach (var character in a.Characters)
{
d.Add( /* ... */ );
++i;
}
double edit
removed commentary about
d.Add((byte)(i + 1), (Dictionary<byte, object>)a.Characters[i]);
after clarification in the comments below
Hey just want anyone having the same problem know, I figured it out. What happened was I manualy renamed LINQ .dbml file when I added it to my project after it was geneerated by sqlmetal. And of course I did it inconsistently (it was renamed in designer but not in its .cs file). I just re-generated a new .dbml file with sqlmetal with a correct name this time and everything works like butter!
Thanks guys!
I use C# on WP7 (Mango). I try to use a special query because I receive an error:
Method 'Int32 orderBirthday(System.DateTime)' has no supported
translation to SQL.
Yes, I know... Linq can't use my function but I don't know the right way...
I have a database table with the columns name and birthday. In my query I will calculate how many days are to the next birthday (from all items) and then I will order with "descending".
static int orderBirthday(DateTime Birthday)
{
DateTime today = DateTime.Today;
DateTime birthday = Birthday;
DateTime next = new DateTime(today.Year, birthday.Month, birthday.Day);
if (next < today)
next = next.AddYears(1);
int numDays = (next - today).Days;
// No Conversion
return numDays;
}
public void LoadCollectionsFromDatabase()
{
DateTime today = DateTime.Today;
var toDoItemsInDB = from ToDoItem todo in toDoDB.Items
let daysToBirthday = orderBirthday(todo.ItemDate)
orderby daysToBirthday ascending
select todo;
// Query the database and load all to-do items.
AllToDoItems = new ObservableCollection<ToDoItem>(toDoItemsInDB);
.
.
.
}
You either have to pull everything from the database and sort it locally (as Enigmativity) shows, or find a way to express the sort operation in a LINQ statement itself. And since you extracted the sorting behavior into its own function, you probably want to reuse this logic. In that case your best bet is to create a function that filters an IQueryable.
Here is an example of how to do this:
public static IOrderedQueryable<Item> OrderByBirthday(
this IQueryable<Item> items)
{
return
from item in items
let today = DateTime.Today
let birthday = item.ItemDate
let next = new DateTime(today.Year, birthday.Month, birthday.Day)
let next2 = next < today ? next.AddYears(1) : next
orderby (next - today).Days
select item;
}
You can use the method as follows:
var toDoItemsInDB = OrderByBirthday(toDoDB.Items);
Or you can use it as an extension method:
var toDoItemsInDB = toDoDB.Items.OrderByBirthday();
It's easy if you do this:
var toDoItemsInDB = from ToDoItem todo in toDoDB.Items.ToArray()
let daysToBirthday = orderBirthday(todo.ItemDate)
orderby daysToBirthday ascending
select todo.;
Notice the .ToArray() added to Items. You basically bring the results into memory and them your function can work.
Two ways:
One: Pull it from Linq2SQL to Linq2Objects using ToEnumerable(), and then use orderBirthday at the C# level.
Advantage is that it's simple to code and maintain, disadvantage is that it can be less efficient (depends on just what you are doing.
Two: Write an equivalent function in SQL, let's say it was called dbo.orderBirthday. Make your orderBirthday method a non-static method of your datacontext-derived class, and then mark your method as having a SQL function equivalent:
[Function(Name="dbo.orderBirthday",IsComposable=true)] //IsComposable is true for functions that can be used within queries, false for stored procedures that must be called on their own.
public int OrderBirthday([Parameter(Name="#birthday",DbType="datetime") DateTime birthday)
{
return Helper.OrderBirthday(birthday); // just to show that we can keep the static version around if we want and call into it. Alternatively we could just move the whole body here.
}
Here the C# code is used in a non-Linq2SQL context, and the SQL code is used in composing a SQL query in a Linq2SQL context.
Advantage: Can stay within SQL longer. Disadvantage: Two versions of the same method can fall out of sync and cause bugs.
It's also possible to have the C# code call the SQL code all the time:
[Function(Name="dbo.orderBirthday",IsComposable=true)]
public int OrderBirthday([Parameter(Name="#birthday",DbType="datetime") DateTime birthday)
{
return (int)ExecuteMethodCall(this, (MethodInfo)MethodInfo.GetCurrentMethod(), birthday).ReturnValue;
}
Advantage: Keeps one version (the SQL) as the only version, so it can't fall out of synch with the C# version. Disadvantage: Calls SQL even when working on objects that have nothing to do with SQL.
If you don't want to load all the items in memory and you want the database execute the calculation, you can write a stored procedure that can execute complex calculation and call the procedure using ADO or EF.
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();