I have a list filled with objects. I have to make a function that when given a certain value of one of the class atributes, gives back another one. In my case, when you specify the "index", the function will return "standysp"(I'm sorry if I can't explain it to well). I need to make this function work but I don't even know when to start. Here is my Code:
using System;
using FirebirdSql.Data.FirebirdClient;
using System.Collections.Generic;
namespace dokselect
{
class PozycjaMagazynowa
{
public double standysp;
public string nazwagrupy;
public string index;
public string nazwadl;
}
class Program
{
public static void Main()
{
string conn = "SECRET";
FbConnection myConnection = new FbConnection(conn);
FbDataReader myReader = null;
string sql = "select STANMAG.standysp,GRUPAKART.nazwagrupy, KARTOTEKA.indeks, kartoteka.nazwadl FROM stanmag JOIN kartoteka using(ID_KARTOTEKA) JOIN wystgrkart using(ID_KARTOTEKA) JOIN grupakart using(ID_GRUPAKART) ORDER BY nazwagrupy;";
FbCommand myCommand = new FbCommand(sql, myConnection);
myConnection.Open();
myReader = myCommand.ExecuteReader();
List<PozycjaMagazynowa> lista1 = new List<PozycjaMagazynowa>();
double standysp;
string nazwagrupy;
string index;
string nazwadl;
while (myReader.Read())
{
standysp = Convert.ToDouble(myReader[0]);
nazwagrupy = myReader[1].ToString();
index = myReader[2].ToString();
nazwadl = myReader[3].ToString();
lista1.Add(new PozycjaMagazynowa { standysp = standysp, nazwagrupy = nazwagrupy, index = index, nazwadl = nazwadl });
}
myConnection.Close();
Console.WriteLine(lista1.Count);
//LISTA DONE
void wyswietl()
{
//????????
}
}
}
}
I'm not sure you're doing it the right way.
First the List you create is not accessible to others methods.
Secund, maybe the List is not appropriate and you may use a HashSet<T> or Dictionnary with your PozycjaMagazynowa class implementing IEqualityComparer
I used this method and it worked thank you for help:
double wyswietl()
{
string myIndex="WSIP_MAT_GIM";
var result = lista1.FirstOrDefault(lista1 => lista1.index == myIndex).standysp;
Console.WriteLine(myIndex + " Zostalo zwrocone przez funkcje");
Console.WriteLine(result + " - to stan dyspozycyjny pasujacy do podanego indexu");
return result;
}
wyswietl();
I'm trying to send a List Object from my C# WebService method over to my stored procedure in Oracle.
Before posting here, I've tried all suggested duplicate links. Here's what I've accomplished so far:
Success: In C#, I can pass my List values from my HTML page over to my WebService method.
Success: In Oracle, I have created a Table, Object Type, Table Type, and Stored Procedure to accept the List values. I was able to test this using an Anonymous block and sample data.
Problem: I cannot get to pass my List values from my C# WebMethod over to my Oracle Stored Procedure.
I'm currently using the following setup:
Visual Studio 2017
.NET Framework 4.6.1
Oracle.ManagedDataAccess 18.6.0
Keep in mind that the version of Oracle.ManagedDataAccess 18.6.0 does NOT contain the OracleDbType.Array as suggested in the older examples.
public class Automobile
{
public string Make { get; set; }
public string Model { get; set; }
public string Year { get; set; }
public string Country { get; set; }
}
using Oracle.ManagedDataAccess.Client;
using Oracle.ManagedDataAccess.Types;
[WebMethod(EnableSession = true)]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public string InsertCars(List<Automobile> myCars, int userID)
{
DataSet dataSet = new DataSet();
using (OracleConnection sqlConnection = new OracleConnection(OracleDBConnection))
{
using (OracleCommand sqlCommand = new OracleCommand("sp_InsertCars", sqlConnection))
{
sqlConnection.Open();
sqlCommand.CommandType = CommandType.StoredProcedure;
sqlCommand.Parameters.Add(
new OracleParameter
{
CollectionType = OracleCollectionType.PLSQLAssociativeArray,
Direction = ParameterDirection.Input,
ParameterName = "p_CarList",
UdtTypeName = "tt_Automobile",
Size = myCars.Count,
Value = myCars.ToArray()
}
);
sqlCommand.Parameters.Add(
new OracleParameter
{
OracleDbType = OracleDbType.Int32,
Direction = ParameterDirection.Input,
ParameterName = "p_UserID",
Value = userID
}
);
sqlCommand.Parameters.Add(
new OracleParameter
{
OracleDbType = OracleDbType.RefCursor,
Direction = ParameterDirection.Output,
ParameterName = "o_Cursor"
}
);
using (OracleDataAdapter sqlAdapter = new OracleDataAdapter(sqlCommand))
{
sqlAdapter.SelectCommand = sqlCommand;
sqlAdapter.Fill(dataSet);
}
}
return JsonConvert.SerializeObject(dataSet);
}
}
CREATE TABLE tblCars
(
RecordID INT GENERATED BY DEFAULT AS IDENTITY NOMINVALUE NOMAXVALUE INCREMENT BY 1 START WITH 1 NOCACHE NOCYCLE NOORDER,
Make NVARCHAR2(100) NULL,
Model NVARCHAR2(100) NULL,
Year NVARCHAR2(4) NULL,
Country NVARCHAR2(100) NULL,
UserID INT NULL
);
CREATE OR REPLACE TYPE ot_Automobile AS OBJECT
(
Make varchar2(100),
Model varchar2(100),
Year varchar2(4),
Country varchar2(100)
);
CREATE OR REPLACE TYPE tt_Automobile AS TABLE OF ot_Automobile;
CREATE OR REPLACE PROCEDURE sp_InsertCars
(
p_CarList In tt_Automobile,
p_UserID In integer,
o_Cursor Out Sys_RefCursor
)
AS
BEGIN
DBMS_Output.Enable;
For RowItem In (Select * From Table(p_CarList))
Loop
Insert Into tblCars
(
Make,
Model,
Year,
Country,
UserID
)
Values(
RowItem.Make,
RowItem.Model,
RowItem.Year,
RowItem.Country,
p_UserID
);
End Loop;
-- Return our results after insert
Open o_Cursor For
Select Make, Model, Year, Country From tblCars Where UserID = p_UserID;
EXCEPTION
When Others Then
DBMS_Output.Put_Line('SQL Error: ' || SQLERRM);
END sp_InsertCars;
COMMIT
/
The result should allow me to pass my array Object from my WebService WebMethod over to my Oracle stored procedure and then loop through each item of the array to perform an Insert.
Here's an example of the data I'm trying to pass in.
this answer depends on commercial package, but if you're as desperate as i am, it's a lifesaver for very reasonable $300 (circa 2020-Q4)... scouts honor, i'm no shill
DevArt's Oracle provider makes elegant work of passing lists of objects to stored procs... it really works... .net core 3.1 compatible, tested on linux, no dependencies on native oracle client ... see my take on a working console app sample below based on the linked forum post
DevArt's "OracleType.GetObjectType()" API makes the UDT marshalling part of this incredibly trivial for us... way less to grok than the existing unmanaged ODP table type support samples i've seen out there for years
strategic consideration - if you already have a sizable code base on an oracle provider, consider just leaving all that code as-is and only take on regression testing this new dependency where the specialized table type support is actually needed
short and sweet sample for quick digestion
using System;
using System.Data;
using Devart.Data.Oracle;
namespace ConsoleApp1
{
class Program
{
private static int oraTable;
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
//good docs:
//direct connection: https://www.devart.com/dotconnect/oracle/docs/StoredProcedures-OracleCommand.html
//linux licensing: https://www.devart.com/dotconnect/oracle/docs/?LicensingStandard.html
using OracleConnection db = new OracleConnection("{insert yours}");
//devart trial licensing nutshell... on WINDOWS, download & run their installer...
//the only thing you really need from that install is the license key file dropped here:
// %programdata%\DevArt\License\Devart.Data.Oracle.key
// then just go nuget the "Devart.Data.Oracle" package reference
//on Windows, the trial license file gets automatically picked up by their runtime
//if you're on Linux, basically just read their good instructions out there
//i've just tested it on WSL so far and plan to try on Azure linux app svc within next day
//db.ConnectionString = "license key=trial:Devart.Data.Oracle.key;" + db.ConnectionString;
db.Direct = true; //nugget: crucial!! https://www.devart.com/dotconnect/oracle/docs/DirectMode.html
db.Open();
var cmd = db.CreateCommand("UserPermissions_u", CommandType.StoredProcedure);
cmd.DeriveParameters();
//tblParm.OracleDbType = OracleDbType.Table;
//passing "table" type proc parm example: https://forums.devart.com/viewtopic.php?t=22243
var obj = new OracleObject(OracleType.GetObjectType("UserPerm", db));
var tbl = new OracleTable(OracleType.GetObjectType("UserPerms", db));
obj["UserPermissionId"] = "sR1CKjKYSKvgU90GUgqq+w==";
obj["adv"] = 1;
tbl.Add(obj);
cmd.Parameters["IN_Email"].Value = "banderson#kingcounty.gov";
cmd.Parameters["IN_Permissions"].Value = tbl;
cmd.ExecuteNonQuery();
//"i can't believe it's not butter!" -me, just now =)
}
}
}
corresponding oracle db definitions:
create or replace type UserPerm as object ( UserPermissionId varchar2(24), std number(1), adv number(1) );
create or replace type UserPerms as table of UserPerm;
create or replace PROCEDURE UserPermissions_u (
IN_Email IN varchar2,
IN_Permissions IN UserPerms
) is
dummyvar number default 0;
begin
select count(*) into dummyvar from table(IN_Permissions);
end;
/
more elaborate shot at generically reflecting on an inbound object to hydrate the proc parms like the OP's request... take with caution, needs testing/bullet-proofing ... i'd love to get better alternatives if anybody cares to share
using System;
using System.Data;
using Devart.Data.Oracle;
using System.Linq;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections;
namespace ConsoleApp1
{
public class User
{
public string Email { get; set; }
public List<UserPermissionEffective> Permissions { get; set; }
}
public class UserPermissionEffective
{
public string UserPermissionId { get; set; }
public string Email { get; set; }
public bool Std { get; set; }
public bool Adv { get; set; }
public string Mod { get; set; }
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
var dto = new User { Email = "testy#mctesterson.com", Permissions = new List<UserPermissionEffective> {
new UserPermissionEffective { UserPermissionId = "1", Std = false, Adv = true },
new UserPermissionEffective { UserPermissionId = "2", Std = true, Adv = false }
} };
if (dto == null) return;
//good docs:
//direct connection: https://www.devart.com/dotconnect/oracle/docs/StoredProcedures-OracleCommand.html
//linux licensing: https://www.devart.com/dotconnect/oracle/docs/?LicensingStandard.html
var dbstring = Environment.GetEnvironmentVariable("dbstring");
using OracleConnection db = new OracleConnection(dbstring);
db.ConnectionString = "license key=trial:Devart.Data.Oracle.key;" + db.ConnectionString;
db.Direct = true; //nugget: crucial!! https://www.devart.com/dotconnect/oracle/docs/DirectMode.html
db.Open();
var cmd = db.CreateCommand("UserPermissions_u", CommandType.StoredProcedure);
cmd.DeriveParameters();
//regex gets everything following the last underscore. e.g. INOUT_PARMNAME yields PARMNAME
var regex = new Regex(#"([^_\W]+)$", RegexOptions.Compiled);
//get the inboud model's root properties
var dtoProps = dto.GetType().GetProperties();
//loop over all parms assigning model properties values by name
//going by parms as the driver versus object properties to favor destination over source
//since we often ignore some superfluous inbound properties
foreach (OracleParameter parm in cmd.Parameters)
{
var cleanParmName = regex.Match(parm.ParameterName).Value.ToUpper();
var dtoPropInfo = dtoProps.FirstOrDefault(prop => prop.Name.ToUpper() == cleanParmName);
//if table type, then drill into the nested list
if (parm.OracleDbType == OracleDbType.Table)
{
//the type we're assigning from must be a list
//https://stackoverflow.com/questions/4115968/how-to-tell-whether-a-type-is-a-list-or-array-or-ienumerable-or/4115970#4115970
Assert.IsTrue(typeof(IEnumerable).IsAssignableFrom(dtoPropInfo.PropertyType));
var listProperty = (dtoPropInfo.GetValue(dto) as IEnumerable<Object>).ToArray();
//don't bother further logic if the list is empty
if (listProperty.Length == 0) return;
//get the oracle table & item Udt's to be instanced and hydrated from the inbound dto
var tableUdt = OracleType.GetObjectType(parm.ObjectTypeName, db);
var itemUdt = OracleType.GetObjectType(tableUdt.ItemObjectType.Name, db);
var dbList = new OracleTable(tableUdt);
//and the internal list item objects
var subPropInfos = dtoPropInfo.PropertyType.GenericTypeArguments[0].GetProperties().ToDictionary(i=>i.Name.ToUpper(), i=>i);
//for every item passed in...
foreach (var dtoSubItem in listProperty) {
//create db objects for every row of data we want to send
var dbObj = new OracleObject(itemUdt);
//and map the properties from the inbound dto sub items to oracle items by name
//using reflection to enumerate the properties by name
foreach (OracleAttribute field in itemUdt.Attributes)
{
var val = subPropInfos[field.Name.ToUpper()].GetValue(dtoSubItem);
//small tweak to map inbound booleans to 1's & 0's on the db since oracle doesn't support boolean!?!
var isDbBool = field.DbType == OracleDbType.Integer && field.Precision == 1;
dbObj[field] = isDbBool ? ((bool)val ? 1 : 0) : val;
}
//lastly add the db obj to the db table
dbList.Add(dbObj);
}
parm.Value = dbList;
}
else {
parm.Value = dtoPropInfo.GetValue(dto);
}
}
cmd.ExecuteNonQuery();
}
}
}
Please refer following link to setup ODAC
Setup Ref and use follwing link to get the ODAC
using Oracle.DataAccess.Client;
using Oracle.DataAccess.Types;
using System;
using System.Data;
namespace Strace_CustomTypes
{
class Program
{
static void Main(string[] args)
{
// Setup Ref - https://o7planning.org/en/10509/connecting-to-oracle-database-using-csharp-without-oracle-client
// ODAC 64bit ODAC122010Xcopy_x64.zip - https://www.oracle.com/technetwork/database/windows/downloads/index-090165.html
// .Net Framework 4
// 'Connection string' to connect directly to Oracle.
string connString = "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=0.0.0.0)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=SIT)));Password=PASSWORD;User ID=USERID";
OracleConnection straceOracleDBConn = new OracleConnection(connString);
OracleCommand cmd = new OracleCommand("PKG_TEMP.TEST_ARRAY", straceOracleDBConn);
cmd.CommandType = CommandType.StoredProcedure;
try
{
straceOracleDBConn.Open();
CustomVarray pScanResult = new CustomVarray();
pScanResult.Array = new string[] { "hello", "world" };
OracleParameter param = new OracleParameter();
param.OracleDbType = OracleDbType.Array;
param.Direction = ParameterDirection.Input;
param.UdtTypeName = "USERID.VARCHAR2_ARRAY";
param.Value = pScanResult;
cmd.Parameters.Add(param);
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message} {Environment.NewLine} {ex.StackTrace}");
}
finally
{
straceOracleDBConn.Close();
cmd.Dispose();
straceOracleDBConn.Dispose();
}
Console.WriteLine("Press any key to exit");
Console.ReadLine();
}
}
//Ref https://www.codeproject.com/Articles/33829/How-to-use-Oracle-11g-ODP-NET-UDT-in-an-Oracle-Sto
public class CustomVarray : IOracleCustomType, INullable
{
[OracleArrayMapping()]
public string[] Array;
private OracleUdtStatus[] m_statusArray;
public OracleUdtStatus[] StatusArray
{
get
{
return this.m_statusArray;
}
set
{
this.m_statusArray = value;
}
}
private bool m_bIsNull;
public bool IsNull
{
get
{
return m_bIsNull;
}
}
public static CustomVarray Null
{
get
{
CustomVarray obj = new CustomVarray();
obj.m_bIsNull = true;
return obj;
}
}
public void FromCustomObject(OracleConnection con, IntPtr pUdt)
{
OracleUdt.SetValue(con, pUdt, 0, Array, m_statusArray);
}
public void ToCustomObject(OracleConnection con, IntPtr pUdt)
{
object objectStatusArray = null;
Array = (string[])OracleUdt.GetValue(con, pUdt, 0, out objectStatusArray);
m_statusArray = (OracleUdtStatus[])objectStatusArray;
}
}
[OracleCustomTypeMapping("USERID.VARCHAR2_ARRAY")]
public class CustomVarrayFactory : IOracleArrayTypeFactory, IOracleCustomTypeFactory
{
public Array CreateArray(int numElems)
{
return new string[numElems];
}
public IOracleCustomType CreateObject()
{
return new CustomVarray();
}
public Array CreateStatusArray(int numElems)
{
return new OracleUdtStatus[numElems];
}
}
}
im reprogramming the tf2 tradebot by JesseCar 96 for CS:go. Im trying to get the items stored in a list array and I can't figure out how to access it. The instantiation and item data works like this:
public class TradeUserAssets : IEquatable<TradeUserAssets>, IComparable<TradeUserAssets>
{
/// <summary>Inventory type</summary>
public long contextid { get; private set; }
/// <summary>itemid</summary>
public ulong assetid { get; private set; }
public int appid { get; private set; }
public int amount { get; private set; }
public TradeUserAssets(int appid, long contextid, ulong assetid, int amount = 1)
{
this.appid = appid;
this.contextid = contextid;
this.assetid = assetid;
this.amount = amount;
}}
It is instantiated through:
private List<TradeUserAssets> otherOfferedItems;
otherOfferedItems = new List<TradeUserAssets>();
But when I use this foreach loop to get the data
foreach (long contextid in Trade.OtherOfferedItems.ToString())
{
Console.WriteLine(contextid);
}
The bot crashes. I've tried using a for loop and using the index number after .ToString()[i] like this but I can't get any of the data out. If I do anything other than .ToString() after OtherOfferedItems it won't build. Any help is really appreciated!
EDIT: This is my userHandler.cs
string tradeid;
if (myItems.Count == 0)
{
offer.Accept(out tradeid);
Log.Success("Accepted trade offer successfully : Trade ID: " + tradeid);
foreach (asset in Trade.OtherOfferedItems)
{
Console.WriteLine(asset);
}
using (var connection = new MySqlConnection("Server=localhost;Database=skindump;Uid=USERNAME;Pwd=PASSWORD;"))
{
var time = DateTime.Now.TimeOfDay;
var date = DateTime.Now;
date.ToString();
connection.Open();
MySqlCommand cmd = new MySqlCommand("INSERT INTO trades (tradeID, tradeUsed, tradeDate, steamID) VALUES (" + tradeid + ", 1, '" + date + "', '" + OtherSID.ConvertToUInt64() + "');");
cmd.Connection = connection;
cmd.ExecuteNonQuery();
Console.WriteLine("ServerVersion: {0}", connection.ServerVersion);
Console.WriteLine(date);
connection.Close();
}
}
Line 201 is the open squiggly bracket after the mysql connection (var connection = new mysql connection etc...). When I comment out the foreach loop it runs fine without crashing so thats definitely the issue.
You probably want to access a property inside of each object of your list. But what you are doing is just converting the Trade.OtherOfferedItems list to a string and then looping over that. You probably want to do something like this instead:
foreach (TradeUserAssets asset in Tade.OtherOfferedItems)
{
Console.WriteLine(asset.contextid);
}
You're trying to access the contextid by using ToString(), which is the wrong way to do it.
Instead, iterate through the collection of TradeUserAssets, and access the property inside the loop.
foreach (var asset in Trade.OtherOfferedItems)
{
Console.WriteLine(asset.contextid);
}
The reason your current code compiles is because it's returning the ascii value for each character in the string that ToString() returns (which is just the class name of the collection). Even if it didn't crash the app at runtime, it wouldn't be what you want.
So I have a basic web service that is grabbing data from a MSSQL database. It works as it's supposed to in terms of SQL connections and queries however, I would like to know if it's possible to have the data displayed a little more elegantly. My goal is to return one name on each line but the web service just spits it all out between string tags and all on one line. I'm EXTREMELY new to C# and ASP.NET and am amazed I made it this far. I just need to format the data in a more pleasant way if possible.
Here is my Code
namespace CustomerService
{
/// <summary>
/// Summary description for Service1
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class Service1 : System.Web.Services.WebService
{
[WebMethod]
//create new webMethod to get names
public string getNames()
{
int size = DataHelper.name().Count();
string[] names = new string[size];
names = DataHelper.name();
return toString(names);
}
//take array passed in and convert to one single string
public string toString(string[] names)
{
int size = names.Count();
string[] nameArray = new string[size];
nameArray = names;
string output = "";
for (int x = 0; x < size; x++)
{
output = output + "\n" + nameArray[x];
}
return output;
}
}
}
And then the DataHelper service:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data;
using System.Data.SqlClient;
namespace CustomerService
{
public class DataHelper
{
//create method to get names from customer DB
public static string[] name()
{
string currentName ="";
string[] names = new string[100];
double checkingBal;
double savingsBal;
double cdBal;
double mmBal;
//create connection
SqlConnection conn = new SqlConnection(#"Data Source=STE2074;Initial Catalog=ATMWeb;Persist Security Info=True;User ID=stp;Password=stp48329");
//create command to get names
string sqlString = "SELECT * FROM tblUsers";
SqlCommand cmd = new SqlCommand(sqlString, conn);
conn.Open();
int x = 0;
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
currentName = reader["firstName"].ToString();
names[x] = currentName;
x++;
}
//close connections
conn.Close();
reader.Close();
return names;
}
}
}
And this is the final result:
<?xml version="1.0" encoding="UTF-8"?>
<string xmlns="http://tempuri.org/"> Stephen lisa steve kyle s Lisa steven Customer chelsea jon karen jessica meagan </string>
I would like a new line after each name...is this possible?
Create a class and return that.
PUBLIC Class MyOldStringNames
{
property string NameHolder { get; set; }
}
End Class
List<MyOldStringNames> myListOfOldStringNames = new List<MyOldStringNames>();
For Each item in names
MyOldStringNames myItemToAdd = new MyOldStringNames() { NameHolder = item.toString() };
myListOfOldStringNames.add(myItemToAdd);
End
return myListOfOldStringNames;
Of course your method would then be something like this. :
public List<MyOldStringNames> getNames()
{
int size = DataHelper.name().Count();
string[] names = new string[size];
names = DataHelper.name();
//Do stuff above
//return data as shown above;
}
Regarding this previous stackoverflow question:
DRY CLR table-valued functions
It seems it only runs in single thread mode. To test this I modified the code slightly to prepend the Name field with the current thread number. All of the returned results had the same thread number assigned. Is this action by design? Is there anyway to get it to multithread? Thanks.
private class ResultRow
// This class holds a row which we want to return.
{
public SqlInt32 CustId;
public SqlString Name;
public ResultRow(SqlInt32 custId_, SqlString name_)
{
int mythread = Thread.CurrentThread.ManagedThreadId;
CustId = custId_;
Name = "[" + mythread.ToString() + "] " + name_;
}
}
EDITED per Marc's question:
Here's the full piece of code. It returns 3470 records in 7 seconds.
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Runtime.InteropServices;
using System.Text;
using System.Collections.Generic;
using System.Collections;
using System.Threading;
namespace CS_CLR_TVF
{
public partial class UserDefinedFunctions
{
// This class holds a row which we want to return.
private class ResultRow
{
public SqlString fldProductName;
public ResultRow(SqlString product_)
{
int mythread = Thread.CurrentThread.ManagedThreadId;
fldProductName = "[" + mythread.ToString() + "] " + product_;
}
}
[SqlFunction(DataAccess = DataAccessKind.Read, FillRowMethodName = "Test_FillRow", TableDefinition = "fldProductName nvarchar(1024)")]
public static IEnumerable xudf_cs_tvf(SqlString strSearchClue)
{
ArrayList results = new ArrayList();
using (SqlConnection connection = new SqlConnection("context connection=true"))
{
connection.Open();
string s1;
using (SqlCommand select = new SqlCommand("SELECT fldProductName FROM tblProducts", connection))
{
using (SqlDataReader reader = select.ExecuteReader())
{
while (reader.Read())
{
s1 = reader.GetSqlString(0).ToString();
// do a substring compare, if "match" grab the row
int idx = s1.IndexOf(strSearchClue.ToString());
if (idx > -1) results.Add(new ResultRow(reader.GetSqlString(0)));
}
}
}
}
return results;
}
// This function takes a row and tells SQL Server what variables we want to
// return from it and what types it contains.
public static void Test_FillRow(object resultsObj, out SqlString fldProductName)
{
ResultRow selectResults = (ResultRow)resultsObj;
fldProductName = selectResults.fldProductName;
}
}
}
Pretty straight forward internal select statement:
SELECT fldProductName FROM tblProducts
.
.
.
.
Here's a version implemented as a scalar UDF and it does do multithreading. It returns 3470 records in <1 second.
[Microsoft.SqlServer.Server.SqlFunction]
public static long xudf_csfake(SqlString strSearchClue, SqlString strStringtoSearch)
{
string s1 = strStringtoSearch.ToString();
// do a substring compare, if "match" grab the row
int idx = s1.IndexOf(strSearchClue.ToString());
if (idx > -1) return 1;
return 0;
}
Here is it's external select statement:
SELECT fldProductName FROM tblProducts WHERE (dbo.xudf_csfake('METAL' ,fldProductName) = 1)
So I seem to getting the opposite of what the article indicates.