How to use SQL user defined functions in .NET?
Asked Answered
L

2

10

I created a scalar function in the DB

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[fn_GetUserId_Username]
    (
    @Username varchar(32)
    )
RETURNS int
AS
    BEGIN
    DECLARE @UserId int
    SELECT @UserId = UserId FROM [User] WHERE Username = @Username
    RETURN @UserId
    END

Now I want to run it within my .NET C# or VB.NET code.

I use Entity Framework, I tried to map it with function mapping and I did not success. i don't care to do it with simple DbCommand, the problem is that I get no results (the function exists in the Entities class):

public int GetUserIdByUsername(string username)
{
    EntityConnection connection = (EntityConnection)Connection;            
    DbCommand com = connection.StoreConnection.CreateCommand();
    com.CommandText = "fn_GetUserId_Username";
    com.CommandType = CommandType.StoredProcedure;
    com.Parameters.Add(new SqlParameter("Username", username));
    if (com.Connection.State == ConnectionState.Closed) com.Connection.Open();
    try
    {
        var result = com.ExecuteScalar(); //always null
    }
    catch (Exception e)
    { 
    }
    return result;
}

Is there any solution? Posts in either C# or VB.NET will be welcommed.

Livingston answered 29/6, 2009 at 3:0 Comment(0)
D
18

It sounds like the right way in this case is to use the functionality of the entity framework to define a .NET function and map that to your UDF, but I think I see why you don't get the result you expect when you use ADO.NET to do it -- you're telling it you're calling a stored procedure, but you're really calling a function.

Try this:

public int GetUserIdByUsername(string username)
{
    EntityConnection connection = (EntityConnection)Connection;            
    DbCommand com = connection.StoreConnection.CreateCommand();
    com.CommandText = "select dbo.fn_GetUserId_Username(@Username)";
    com.CommandType = CommandType.Text;
    com.Parameters.Add(new SqlParameter("@Username", username));
    if (com.Connection.State == ConnectionState.Closed) com.Connection.Open();
    try
    {
        var result = com.ExecuteScalar(); // should properly get your value
        return (int)result;
    }
    catch (Exception e)
    {
        // either put some exception-handling code here or remove the catch 
        //   block and let the exception bubble out 
    }
}
Debenture answered 29/6, 2009 at 3:7 Comment(5)
Result: SqlException: 'fn_GetUserId_Username' is not a recognized function name.Livingston
@Shimmy: specify the owner name, "dbo" --> "select dbo.fn_GetUserId_Username(@Username)"Curious
Modified to add ".dbo" as suggested by Sung Meister.Debenture
@Jonathan Rupp it gives me this Error::: The name 'Connection' does not exist in the current context .. how can i fix this? plzWinder
@HammamMuhareb - you need to use whatever variable/property has your connection in it - it may be different than 'Connection'.Debenture
H
3

This is very similar to the above answer, but the below code allows you to call a UDF with any number of parameters and any return type. This might be useful as a more general solution. This also hasn't been tested thoroughly...I think it will have some problems with varchars.

public class MyDBAccess
{
    private SqlConnection sqlConnection = new SqlConnection("databaseconnectionstring");

    public int GetUserIdByUsername(string username)
    {
        int userID = CallUDF<int>("dbo.fn_GetUserId_Username", new SqlParameter("@Username", username));
        return userID;
    }

    internal static T1 CallUDF<T1>(string strUDFName, params SqlParameter[] aspParameters)
    {
        using (SqlConnection scnConnection = sqlConnection)
        using (SqlCommand scmdCommand = new SqlCommand(strUDFName, scnConnection))
        {
            scmdCommand.CommandType = CommandType.StoredProcedure;

            scmdCommand.Parameters.Add("@ReturnValue", TypeToSqlDbType<T1>()).Direction = ParameterDirection.ReturnValue;
            scmdCommand.Parameters.AddRange(aspParameters);

            scmdCommand.ExecuteScalar();

            return (T1)scmdCommand.Parameters["@ReturnValue"].Value;
        }
    }

    private SqlDbType TypeToSqlDbType<T1>()
    {
        if (typeof(T1) == typeof(bool))
        {
            return SqlDbType.Bit;
        }
        else if (typeof(T1) == typeof(int))
        {
            return SqlDbType.Int;
        }
        //
        // ... add more types here
        //
        else
        {
            throw new ArgumentException("No mapping from type T1 to a SQL data type defined.");
        }
    }
}
Horizon answered 28/1, 2015 at 6:51 Comment(2)
Proper and brilliant! Thnx.Yamamoto
Great solution if you want to call the generic function use int userID = CallUDF<int>("dbo.fn_GetUserId_Username", new SqlParameter("@Username", username));Ritualize

© 2022 - 2024 — McMap. All rights reserved.