Npgsql 3.1.0-alpha5 does not allow passing null into a TIMESTAMP parameter for a function - npgsql

I've been experimenting with coreclr on linux using the beta Npgsql library (3.1.0-alpha5). When passing parameters to a function, the library throws errors anytime a TIMESTAMP parameter is null: "Parameter <blah> must be set". Below is a test case.
create sequence sq_category_id start 1 increment 1;
create table category (
id int not null default nextval('sq_category_id'),
name varchar(16) not null,
date timestamp with time zone not null default (now() at time zone 'utc'),
active bool not null default (true),
constraint pk_category_id primary key (id),
constraint uq_category_name unique (name)
);
.. and the function
-- update
create or replace function fn_category_update(
p_id integer,
p_name character varying(16),
p_date timestamp with time zone,
p_active boolean
) returns json as
$$
declare result json = json_build_object('id', null, 'name', null, 'date', null, 'active', null);
begin
update category set
name = case
when
p_name is not null and
p_name != name and
rtrim(ltrim(p_name)) != ''
then p_name
else name
end,
date = case
when
p_date is not null and
p_date != date
then p_date
else date
end,
active = case
when
p_active is not null and
p_active != active
then p_active
else active
end
where id = p_id returning json_build_object('id', id, 'name', name, 'date', date, 'active', active) into result;
return result;
exception when others then return result;
end
$$
language plpgsql;
Anyone have an idea of what I might be doing wrong?
**Edit, here is the c# code along with the exception stack trace (which doesn't provide much additional information).
using System;
namespace Landress.Co.Models.DataObjects
{
public class CategoryDataObject
{
public int? Id { get; set; }
public string Name { get; set; }
public DateTime? Date { get; set; }
public bool? Active { get; set; }
public CategoryDataObject(int? id = null, string name = null, DateTime? date = null, bool? active = null)
{
this.Id = id;
this.Name = name;
this.Date = date;
this.Active = active;
}
public CategoryDataObject()
{
this.Id = null;
this.Name = null;
this.Date = null;
this.Active = null;
}
}
}
The code which actually makes the call to the function:
public CategoryDataObject Update(CategoryDataObject item)
{
NpgsqlConnection conn = new NpgsqlConnection(Configuration.GetSection("Data:DefaultConnection").Value);
try
{
conn.Open();
NpgsqlCommand cmd = new NpgsqlCommand("fn_category_update", conn);
cmd.CommandType = CommandType.StoredProcedure;
var paramId = cmd.CreateParameter();
paramId.ParameterName = "p_id";
paramId.NpgsqlDbType = NpgsqlTypes.NpgsqlDbType.Integer;
paramId.IsNullable = false;
paramId.Value = item.Id;
cmd.Parameters.Add(paramId);
var paramName = cmd.CreateParameter();
paramName.ParameterName = "p_name";
paramName.NpgsqlDbType = NpgsqlTypes.NpgsqlDbType.Varchar;
paramName.Size = 16;
paramName.IsNullable = true;
paramName.Value = item.Name;
cmd.Parameters.Add(paramName);
var paramDate = cmd.CreateParameter();
paramDate.ParameterName = "p_date";
paramDate.NpgsqlDbType = NpgsqlTypes.NpgsqlDbType.Timestamp;
paramDate.IsNullable = true;
paramDate.Value = item.Date;
cmd.Parameters.Add(paramDate);
var paramActive = cmd.CreateParameter();
paramActive.ParameterName = "p_active";
paramActive.NpgsqlDbType = NpgsqlTypes.NpgsqlDbType.Boolean;
paramActive.IsNullable = true;
paramActive.Value = item.Active;
cmd.Parameters.Add(paramActive);
string jsonResult = String.Empty;
try
{
jsonResult = cmd.ExecuteScalar().ToString();
cmd.Dispose();
conn.Close();
conn.Dispose();
try
{
return JsonConvert.DeserializeObject<CategoryDataObject>(jsonResult);
}
catch (Exception ex)
{
Console.WriteLine(String.Format("Unable to deserialize json result returned from postgresql: {0}", ex.Message));
return new CategoryDataObject();
}
}
catch (Exception ex)
{
Console.WriteLine(String.Format("Unable to execute postgresql function \"fn_category_update\": {0}", ex.Message));
return new CategoryDataObject();
}
}
catch (Exception ex)
{
Console.WriteLine(String.Format("Unable to open a connection to the database: {0}", ex.Message));
return new CategoryDataObject();
}
}
and finally the full stack trace.
-- Stack Trace
at Npgsql.NpgsqlParameter.ValidateAndGetLength()
at Npgsql.NpgsqlCommand.ValidateAndCreateMessages(CommandBehavior behavior)
at Npgsql.NpgsqlCommand.ExecuteScalarInternal()
at Landress.Co.Models.Repositories.CategoryRepository.Update(CategoryDataObject item) in C:\Users\<userName>\Desktop\landress.co\src\landress.co\Models\Repositories\CategoryRepository.cs:line 73
-- Message
Parameter p_date must be set

Should have used System.DBNull instead of null to represent value passed to the function. Not an issue with the library at all.

Related

setValue of a Non-editable comboBox

Can someone share some sample codes on how to set the value of a non-editable Combobox? It is similar to this, but when I tried to insert it in my code. It returns a null value
Code
ObservableList<City> data = FXCollections.observableArrayList();
data = AddressGetWay.getCityByProvince("Batanes")
cmbCity.setItems(data); // set the items from the database
cmbCity.setConverter(new StringConverter<City>() {
#Override
public String toString(City object) {
return object.getCityName();
}
#Override
public City fromString(String string) {
return cmbCity.getItems().stream().filter(ap
-> ap.getCityName().equals(string)).findFirst().orElse(null);
}
});
cmbCity.valueProperty().addListener(
(ObservableValue<? extends City> observable, City oldValue, City newVal) -> {
if (newVal != null) {
//
}
}
);
// TODO: get the data stored in the database (Column City)
// and set the value of the ComboBox.
Predicate<City> predicate = city -> city.getCityName() == "Itbayat"; // Let's assume that the data stored in the database is "Itbayat"
Optional<City> opt = data.stream().filter(predicate).findAny();
cmbCity.getSelectionModel().select(opt.orElse(null)); // the ComboBox value should be "Itbayat".
I'm using a Singleton Class (correct me if I'm wrong) to retrieve the data from the database
public class AddressGetWay {
static Connection con; //connect to the database
static PreparedStatement pst = null;
static ResultSet rs = null;
public static ObservableList<City> getCityByProvince(String prov) {
ObservableList<City> listData = FXCollections.observableArrayList();
String sql = "SELECT pk_cit_id, cit_nm, zip_code FROM city_mun WHERE prov_code = (SELECT prov_code FROM provinces WHERE prov_nm = ?)";
try {
pst = con.prepareStatement(sql);
pst.setString(1, prov);
rs = pst.executeQuery();
while (rs.next()) {
listData.add(new City(
rs.getInt(1),
rs.getString(2),
rs.getInt(3)
));
}
} catch (SQLException ex) {
Logger.getLogger(AddressGetWay.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
pst.close();
rs.close();
} catch (SQLException ex) {
Logger.getLogger(AddressGetWay.class.getName()).log(Level.SEVERE, null, ex);
}
}
return listData;
}
}
This should be my desired Output
But I got this output using the above code
As for my naming conventions. I tried to do this if there's something I need to correct, feel free to pinpoint.
It seems that you are having issue on this code block,
ObservableList<City> data = AddressGetWay.getCityByProvince("Batanes");
//....
Predicate<City> predicate = city -> city.getCityName() == "Itbayat";
Optional<City> opt = data.stream().filter(predicate).findAny();
cmbCity.getSelectionModel().select(opt.orElse(null));
I suspect some loop-holes that is overcoming the null-value.
As you retrieving data through province Batanes i.e. ObservableList<City> data = AddressGetWay.getCityByProvince("Batanes"), maybe it's not retrieving any data.
Your predicate is comparing cityName Itbayat, which may not be getting matched with the retrieved data.
Please check the database value for the city table as to compare those data as well
AddressGateWay
public class AddressGateWay {
private static Connection connection;
private static PreparedStatement statement = null;
private static ResultSet result = null;
static {
connection = loadConnection();
}
public static ObservableList<City> getCityByProvince(String prov) {
ObservableList<City> listData = FXCollections.observableArrayList();
String sql = "SELECT pk_cit_id, cit_nm, zip_code FROM city_mun WHERE prov_code = (SELECT prov_code FROM provinces WHERE prov_nm = ?)";
try {
statement = connection.prepareStatement(sql);
statement.setString(1, prov);
result = statement.executeQuery();
while (result.next()) {
listData.add(new City(
result.getInt(1),
result.getString(2)
));
}
} catch (SQLException ex) {
Logger.getLogger(AddressGateWay.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
statement.close();
result.close();
} catch (SQLException ex) {
Logger.getLogger(AddressGateWay.class.getName()).log(Level.SEVERE, null, ex);
}
}
return listData;
}
// for editing
public static ObservableList<Address> selectedItem(int item) {
ObservableList<Address> listData = FXCollections.observableArrayList();
String sql = "SELECT prov_nm, city_nm FROM emp_address WHERE pk_address_id = ?";
try {
statement = connection.prepareStatement(sql);
statement.setString(1, item);
result = statement.executeQuery();
if (result.next()) {
listData.add(new Address(
result.getString(1),
result.getString(2)
));
}
} catch (SQLException ex) {
Logger.getLogger(AddressGateWay.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
statement.close();
result.close();
} catch (SQLException ex) {
Logger.getLogger(AddressGateWay.class.getName()).log(Level.SEVERE, null, ex);
}
}
return listData;
}
}
City
public class City {
private int id;
private String cityName;
public City(int id, String cityName) {
this.id = id;
this.cityName = cityName;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
}
Controller Logic Code
ObservableList<City> cities = AddressGateWay.getCityByProvince(provinceComboBox.getValue()); // assume the value is "Batanes"
cityComboBox.setItems(cities);
cityComboBox.setConverter(new StringConverter<City>() {
#Override
public String toString(City object) {
return object.getCityName();
}
#Override
public City fromString(String string) {
return cityComboBox.getItems().stream().filter(ap
-> ap.getCityName().equals(string)).findFirst().orElse(null);
}
});
cityComboBox.valueProperty().addListener(
(ObservableValue<? extends City> observable, City oldValue, City newVal) -> {
if (newVal != null) {
//
}
}
);
City city = cities
.stream()
.filter(c -> c.getCityName() == "Itbayat")
.findAny()
.orElse(null);
cityComboBox.getSelectionModel().select(city);
// summing up the below updates and your codes,
// I have set the value of cityComboBox by using . . .
Address ads = new Address();
ads = AddressGetWay.selectedItem(item); // the item to be edit
// assume the value from the database is "Itbayat"
Predicate<City> predicate = city -> city.getCityName() == ads.getCity().getCityName();
Optional<City> opt = cities.stream().filter(predicate).findAny();
cityComboBox.getSelectionModel().select(opt.orElse(null));
// Thank you, it finally work :)
Some key points based on the updated code
Refactoring code based on unnecessary code
Implementing basic Java naming Conventions
Missing connection initialization (need to load based on your database)
Inined code (for Predicate, and Optional)
EDIT
Province
public class Province {
private String provinceCode;
private String provinceName;
//Constructors
//Getters and Setters
}
Address
public class Address {
private City city;
private Province province;
//Constructors
//Getters and Setters
}

How to improve the coverage of this test class in Apex?

I have created a test class with 51% code coverage till line no 34.
Further, I tried to satisfy if condition but I couldn't. Now I am not getting how to do with 100% code coverage.
Here is the Apex class:
public class AssignProjectController {
public String CaseIds;
public String status {get;set;}
public List<Project__c> activeProjects {get;set;}
public String keyWordSearched {get;set;}
public Id projectId {get;set;}
public AssignProjectController (){
CaseIds = ApexPages.currentPage().getParameters().get('id');
}
public void getProjects(){
status = '';
String searchQuery = 'FIND \'' + keyWordSearched + '*\' IN ALL FIELDS RETURNING Project__c (id,Name,Description__c where Status__c =\'Active\')';
try{
List<List<Project__c >> searchList = search.query(searchQuery);
activeProjects = searchList[0];
if(activeProjects.size() == 0) status = 'No search result found.';
}catch(Exception ex){
system.debug('ex..'+ex.getMessage());
}}}
public PageReference assignProjectToCases(){
List<Case__c> customSettingList = Case__c.getall().values();
List<String> settingRecordTypeList = new List<String>();
for(Case__c caseObj:customSettingList){
settingRecordTypeList.add(caseObj.Name);
}
List<RecordType> recordTypeListData = [SELECT Id FROM RecordType WHERE SObjectType = 'Case' and Name In : settingRecordTypeList];
if(CaseIds != null){
List<String> caseIDList = new List<String>();
caseIDList = CaseIds.split(',');
if([Select id from Case where Id In : caseIDList and RecordType.Id NOT In : recordTypeListData].size() > 0){
status = 'failed';
}else{
List<Case> cases = [Select id,Project__c,RecordType.Name from Case where Id In : caseIDList and RecordType.Id In : recordTypeListData];
if(cases.size() > 0){
for(case caseOb: cases){
caseOb.Project__c = projectId ;
}
try{
update cases ;
status = 'Changes are scheduled';
}catch(Exception ex){
system.debug('AssignProjectController :::'+ex.getMessage());
status = 'Something Went Wrong';
}}}}
return null;
}}
Here is the test class- which I tried to resolve
#isTest public class TestAssignProjectController {
public static Project__c insertProject(){
Project__c proObj = new Project__c();
proObj.Name = 'testProject';
proObj.Status__c = 'Active';
proObj.Description__c = 'for testing';
proObj.Internal_Email_Alias__c = 'a#test.com';
return proObj;
}
public static Account getAccount(){
Account accoObj = new Account();
accoObj.Name = 'testAcc';
accoObj.Location__c = 'testLocation';
accoObj.Type = 'CM';
accoObj.BillingCountry = 'United States';
return accoObj;
}
public static Contact insertContact(Account accObj){
Contact conObj = new Contact();
conObj.FirstName = 'test';
conObj.LastName = 'testLastname';
conObj.AccountId = accObj.Id;
conObj.Email = 'abc#gmail.com';
return conObj;
}
public static Id getTechTypeId(){
return Schema.SObjectType.Case.getRecordTypeInfosByName().get('Tech ').getRecordTypeId();
}
public static Case insertCase(String conId, String proId){
Case caseObj = new Case();
caseObj.Project__c = proId;
caseObj.ContactId = conId;
caseObj.Status = 'Open';
caseObj.Inquiry_Type__c = 'All';
caseObj.Subject = 'TestSubject';
caseObj.Description = 'TestDescription';
caseObj.Case_Is_Reopened__c = false;
caseObj.RecordTypeId = getTechTypeId();
return caseObj;
}
public static testmethod void testMethodExecution(){
AssignController asigncon = new AssignController ();
Project__c proObj = insertProject();
insert proObj;
System.assertEquals(proObj.Status__c,'Active');
Account accObj = getAccount();
insert accObj;
System.assertNotEquals(accObj.Id,null);
Contact conObj = insertContact(accObj);
insert conObj;
System.assertNotEquals(conObj.Id,null);
Case caseObj = insertCase(conObj.Id, proObj.Id);
insert caseObj;
system.debug(caseObj);
//Set baseURL & case ID
PageReference pageRef = Page.Assign;
pageRef.getParameters().put('id',caseObj.id+',');
AssignController asigncon1 = new AssignController ();
asigncon1.getProjects();
asigncon1.assignProjectToCases();
}}
If you are referring if(cases.size() > 0) this statement, then surely there is problem of inserting the case. Make sure that insert caseObj; is working and inserts data in Salesforce backend.
If there is no data in case object, the test method cannot cover the if statement.

Dapper table valued parameter as a property?

I have a stored proc like this:
CREATE PROCEDURE [dbo].[Organisation_Insert]
#OrganisationXId uniqueidentifier
,#Enabled bit
,#Timezone nvarchar(50)
,#MinimumValue float
,#Rules ReminderRuleType READONLY ...
ReminderRuleType is a user defined type.
In my app I have this:
class OrganisationDTO
{
private readonly IOrganisationDocument _orgDoc;
public long OrganisationId { get { return _orgDoc.OrganisationId; } }
public Guid OrganisationXId { get { return _orgDoc.OrganisationXId; } }
public string TimeZone { get { return _orgDoc.TimeZone; } }
public bool Enabled { get { return _orgDoc.Enabled; } }
public decimal MinimumValue { get { return _orgDoc.MinimumValue; } }
public RuleTableValuedParameters Rules { get; private set; }
public OrganisationDTO(IOrganisationDocument orgDoc)
{
_orgDoc = orgDoc;
Rules = new RuleTableValuedParameters("#Rules", _orgDoc.Rules);
}
}
RuleTableValuedParameters implements SqlMapper.IDynamicParameters which has an AddParameters method.
When I execute the query, the #Rules parameter is never passed (using SQLProfiler). I can also see that AddParameters is never called.
Is this possible to do?
Thanks
Here's a simplified example based on your code that shows it working just fine; AddParameters is invoked correctly, and the values are conveyed to the stored procedure. As a side note: if you are using DataTable for your TVPs, the library supports that directly with no additional code needed.
public void SO29596645_TvpProperty()
{
try { connection.Execute("CREATE TYPE SO29596645_ReminderRuleType AS TABLE (id int NOT NULL)"); }
catch { }
connection.Execute(#"create proc #SO29596645_Proc (#Id int, #Rules SO29596645_ReminderRuleType READONLY)
as begin select #Id + ISNULL((select sum(id) from #Rules), 0); end");
var obj = new SO29596645_OrganisationDTO();
int val = connection.Query<int>("#SO29596645_Proc", obj.Rules, commandType: CommandType.StoredProcedure).Single();
// 4 + 9 + 7 = 20
val.IsEqualTo(20);
}
class SO29596645_RuleTableValuedParameters : Dapper.SqlMapper.IDynamicParameters {
private string parameterName;
public SO29596645_RuleTableValuedParameters(string parameterName)
{
this.parameterName = parameterName;
}
public void AddParameters(IDbCommand command, Dapper.SqlMapper.Identity identity)
{
Console.WriteLine("> AddParameters");
SqlCommand lazy = (SqlCommand)command;
lazy.Parameters.AddWithValue("Id", 7);
DataTable table = new DataTable {
Columns = {{"Id", typeof(int)}},
Rows = {{4}, {9}}
};
lazy.Parameters.AddWithValue("Rules", table);
Console.WriteLine("< AddParameters");
}
}
class SO29596645_OrganisationDTO
{
public SO29596645_RuleTableValuedParameters Rules { get; private set; }
public SO29596645_OrganisationDTO()
{
Rules = new SO29596645_RuleTableValuedParameters("#Rules");
}
}
Here's the full working DynamicParameter that I created:
public class OrganisationDynamicParameter : SqlMapper.IDynamicParameters
{
private readonly IOrganisation _orgModel;
public OrganisationDynamicParameter(IOrganisation orgModel)
{
_orgModel = orgModel;
}
public void AddParameters(IDbCommand command, SqlMapper.Identity identity)
{
SqlParameter p;
var sqlCommand = (SqlCommand)command;
sqlCommand.CommandType = CommandType.StoredProcedure;
p = sqlCommand.Parameters.Add("#OrganisationXId", SqlDbType.UniqueIdentifier);
p.Value = _orgModel.OrganisationXId;
p = sqlCommand.Parameters.Add("#Enabled", SqlDbType.Bit);
p.Value = _orgModel.Enabled;
p = sqlCommand.Parameters.Add("#Timezone", SqlDbType.NVarChar, 50);
p.Value = _orgModel.TimeZone;
p = sqlCommand.Parameters.Add("#MinimumValue", SqlDbType.Float);
p.Value = _orgModel.MinimumValue;
List<SqlDataRecord> ruleList = _orgModel.Rules.Select(MapRuleData).ToList();
if (ruleList.Count > 0)
{
p = sqlCommand.Parameters.Add("#Rules", SqlDbType.Structured);
p.Direction = ParameterDirection.Input;
p.TypeName = "ReminderRuleType";
p.Value = ruleList;
}
}
protected SqlDataRecord MapRuleData(IReminderRule value)
{
var rec = new SqlDataRecord(new[]
{
new SqlMetaData("RuleId", SqlDbType.BigInt),
new SqlMetaData("OrganisationId", SqlDbType.BigInt),
new SqlMetaData("Name", SqlDbType.NVarChar, 200),
new SqlMetaData("OffsetDays", SqlDbType.Int),
new SqlMetaData("SubjectTemplate", SqlDbType.NVarChar, -1),
new SqlMetaData("BodyTemplate", SqlDbType.NVarChar, -1)
});
rec.SetInt64(0, value.RuleId);
rec.SetInt64(1, value.OrganisationId);
rec.SetString(2, value.Name);
rec.SetInt32(3, value.OffsetDays);
rec.SetString(4, value.SubjectTemplate);
rec.SetString(5, value.BodyTemplate);
return rec;
}
}
I use this thusly:
public IOrganisation CreateOrganisation(IOrganisation organisation)
{
var dtoOrg = new OrganisationDynamicParameter(organisation);
return ExecuteSPReturningOrganisation("Organisation_Insert", dtoOrg);
}
protected IOrganisation ExecuteSPReturningOrganisation(string query, object parameters)
{
using (IDbConnection con = ConnectionFactory.CreateOpenConnection())
{
using (
SqlMapper.GridReader multi = con.QueryMultiple(query, parameters,
commandType: CommandType.StoredProcedure))
{
OrganisationModel org = multi.Read<OrganisationModel>().SingleOrDefault();
if (org != null)
{
org.Rules = multi.Read<ReminderRuleModel>().ToArray();
}
return org;
}
}
}
Cheers

Changing default value for null string to datetime conversion in sql server

I have a web from that has a few text boxes which are visible only under certain conditions. These textboxes are bound to calender extenders.However whenever not visible these pass null string to the database. Their corresponding datatype in the table is datetime. While storing these null strings they are converted to 1/1/1900 12:00:00 AM. How can i have a null value stored here instead ?
Do i need to change the data type to some thing else ??
I'm using datetime because i need to perform some comparison operations later.
Relevant code is here.
public class Asset
{
public string dbStatus { get; set; }
public string wtyEndDate { get; set; }
public string amcEndDate { get; set; }
public Asset()
{
dbStatus = default(String);
wtyEndDate = default(String);
amcEndDate = default(String);
}
}
protected void btnSaveAsset_Click(object sender, EventArgs e)
{
Asset a = new Asset();
a.wtyEndDate = txtWtyEndDate.Text;
a.amcEndDate = txtAmcEndDate.Text;
string msg = "";
if (a.dbStatus == "INSERT")
{
try
{
msg = (ab.EXE_Assets_Master(a).Rows.Count>0) ? "Record Inserted"; : "Error"
}
catch (Exception)
{
msg = "Record with this service tag already exists in the database";
}
}
}
protected DataTable _EXE_Asset_Details(Asset a)
{
Parameters.Clear();
AddParameter("#wtyEndDate", a.wtyEndDate);
AddParameter("#amcEndDate",a.amcEndDate);
AddParameter("#DBStatus", a.dbStatus);
return ExecuteDataSet("[Asset_Details]").Tables[0];
}
// Asset_Details
CREATE PROCEDURE [Asset_Details]
#wtyEndDate datetime = NULL,
#amcEndDate datetime =null,
#dbStatus nvarchar(50)
AS
IF(#DBStatus='INSERT')
BEGIN
INSERT INTO [assetsMaster]
([warrantyEndDate],[amcEndDate])
VALUES
( #wtyEndDate,#amcEndDate)
SELECT ##rowcount
END
Only add the optional DateTime parameters if they have data in them.
For example:
if(!string.IsNullOrWhiteSpace(a.wtyEndDate))
{
AddParameter("#wtyEndDate", a.wtyEndDate);
}

Why is my table-valued parameter empty when reaching the database?

I'm trying to test out the new table-valued parameter functionality of SQL 2008 by calling a stored procedure using ADO.NET, but I'm running into a problem where the parameter seems to contain no rows when it gets to the stored procedure. The UDT looks like this:
CREATE TYPE [dbo].[PersonType] AS TABLE(
[FirstName] [varchar](50) NULL,
[LastName] [varchar](50) NULL,
[Birthdate] [date] NULL)
The stored proc looks like this:
CREATE PROCEDURE [dbo].[AddPeople]
(#peopleToAdd dbo.PersonType READONLY)
AS
BEGIN
IF (SELECT COUNT(*) FROM #peopleToAdd) > 0
BEGIN
SELECT 'Has rows'
END
ELSE
BEGIN
SELECT 'Does NOT have rows'
END
END
And finally, the .NET code is this (brace yourself, it's a lot):
public class Program
{
static void Main(string[] args)
{
PersonCollection people =
new PersonCollection()
{
new Person
{
FirstName = "John",
LastName = "Doe",
Birthdate = new DateTime(1975, 12, 1)
},
new Person
{
FirstName = "Randall",
LastName = "Stevens",
Birthdate = new DateTime(1935, 7, 10)
}
};
using(SqlConnection conn = new SqlConnection("Data Source=localhost\\sqlexpress;Initial Catalog=TVPExample;Integrated Security=SSPI;"))
{
conn.Open();
SqlCommand cmd = new SqlCommand("AddPeople", conn);
SqlParameter parameter = cmd.Parameters.AddWithValue("#peopleToAdd", people);
parameter.SqlDbType = SqlDbType.Structured;
parameter.TypeName = "dbo.PersonType";
string result = cmd.ExecuteScalar().ToString();
Console.WriteLine(result);
}
}
}
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime Birthdate { get; set; }
}
public class PersonCollection : List<Person>, IEnumerable<SqlDataRecord>
{
#region Implementation of IEnumerable<SqlDataRecord>
IEnumerator<SqlDataRecord> IEnumerable<SqlDataRecord>.GetEnumerator()
{
SqlDataRecord rec = new SqlDataRecord(
new SqlMetaData("FirstName", SqlDbType.VarChar, 50),
new SqlMetaData("LastName", SqlDbType.VarChar, 50),
new SqlMetaData("Birthdate",SqlDbType.Date));
foreach (Person person in this)
{
rec.SetString(0, person.FirstName);
rec.SetString(1, person.LastName);
rec.SetDateTime(2, person.Birthdate);
yield return rec;
}
}
#endregion
}
I used this blog post as an example. I always get "Does NOT contain rows" as a result, but looking at the Visual Studio debugger shows that the collection I'm passing in contains the two values I put in there. Any ideas? What am I missing?
Add this:
cmd.CommandType = CommandType.StoredProcedure;

Resources