How to use the Procedure with User Defined Table Type in Entity Framework


Question

How to use a procedure with a user-defined table type in Entity Framework?

I have the EF with database-first approach, when I add a procedure with a user-defined table type columns it will not reflected in the EF will update the model.

And how can I pass the user-defined table parameter in EF with procedure?

My procedure :

Sample_Proce_Sp ( @TableTest @UserDefinedTable Readonly ) AS BEgin Select * from @TableTest END

In EF I have updated the model, will adding the stored procedure it shows the error like

The model was generated with warnings or errors.
Please see the Error List for more details. These issues must be fixed before running your application.
Loading metadata from the database took 00:00:03.1330217.
Generating the model took 00:00:01.9251464.
Successfully registered the assembly 'System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' in the Web.Config file. 

Accepted Answer

EntityFrameworkExtras on Github looks promising.

From the Git hub Site:

DbContext context = new DbContext("ConnectionString");

var proc = new AddMemberStoredWithAddressesProcedure()
    {
        FirstName = "Michael",
        LastName = "Bovis",
        Age = 26,
        Addresses = new List<Address>()
        {
            new Address() {Line1 = "16", Line2 = "The Lane", Postcode = "MA24WE"}
        }
    };

context.Database.ExecuteStoredProcedure(proc);

Where "AddMemberStoredWithAddressesProcedure" and "Address" are both defined with special attributes.

I got a chance to try it out..

    CREATE TYPE [dbo].[UdtGuidList] AS TABLE(
        [Guid] [uniqueidentifier] NULL
    )
GO

CREATE PROCEDURE [dbo].[MyUdtSproc] 
    -- Add the parameters for the stored procedure here
    (@GuidList  UdtGuidList READONLY)
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    -- Insert statements for procedure here
    SELECT *
    from Blog b
    INNER JOIN @GuidList gl ON gl.Guid = b.BlogGuid
    --where b.BlogGuid in (SELECT gl.Guid from @GuidList gl)

END

It created this dynamic SQL with 5 rows of the custom type..

declare @p3 dbo.UdtGuidList
insert into @p3 values(N'333f3916-c823-e311-84f2-0022198ef787')
insert into @p3 values(N'33327a17-c34e-e211-9a8c-0022198ef787')
insert into @p3 values(N'333ebc24-c44e-e211-9a8c-0022198ef787')
insert into @p3 values(N'3338d557-c44e-e211-9a8c-0022198ef787')
insert into @p3 values(N'333d7f92-c44e-e211-9a8c-0022198ef787')

exec sp_executesql N'EXEC [dbo].[MyUdtSproc] @GuidList = @GuidList ',N'@GuidList [UdtGuidList] READONLY',@GuidList=@p3

This might get the work done, but the sql could grow large with many rows in the user defined table. I'm going to compare this to old ADO.

I tried the old DataTable ADO (below) and traced SQL to see what it produced. It was EXACTLY the same!

   var dt = new DataTable();

    dt.Columns.Add("Guid");

    foreach (var r in list)
    {
        var row = dt.NewRow();
        row["Guid"] = r.Guid;
        dt.Rows.Add(row);
    }

    using (var conn = new SqlConnection(@"Server=AComputer\DEVSQL;Database=Booyaa;Trusted_Connection=True"))
    {
        using (var sproc = new SqlCommand("[dbo].[MyUdtSproc]", conn))
        {
            var param = new SqlParameter("@GuidList", SqlDbType.Structured);
            param.TypeName = "[dbo].[UdtGuidList]";
            param.SqlValue = dt;
            sproc.Parameters.Add(param);

            if (conn.State != ConnectionState.Open) conn.Open();
            var reader = sproc.ExecuteReader();
        }
    }

Cool! I say this library is a valid option.

Update:

More information from my blog

A Sample Visual Studio Project





Licensed under: CC-BY-SA
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why