How to handle varchar(max) in t4 script? - sql-server-data-tools

I'm using .tt scripting in VS 2017(SSDT 2017) in the DB project. We create staging tables structures manually and then the T4 script generates final destination tables based on the staging table structure.
I have this code to get columns and their data types to create my final destination tables but it looks if one of the fields is defined as varchar(max) then generated field gets varchar data type and that is it.
Here is the sample of the script.
foreach (var col in table.GetReferenced(Table.Columns))
{
string columnText;
string columnName = col.Name.Parts[2];
// this attempts to limit to only columns from the source. there's gotta be a cleaner way.
if (!skipColumns.Contains(columnName))
{
int length = col.GetProperty<int>(Column.Length);
int precision = col.GetProperty<int>(Column.Precision);
int scale = col.GetProperty<int>(Column.Scale);
string suffix;
if (length != 0)
{
suffix = String.Format("({0})", length);
}
else if (precision != 0)
{
suffix = String.Format("({0},{1})", precision, scale);
}
else if (precision == 0 && scale != 0)
{
suffix = String.Format("({0})", scale);
}
else
{
suffix = "";
}
bool nullable = col.GetProperty<bool>(Column.Nullable);
string nullText = nullable ? "NULL" : "NOT NULL";
string dataType = col.GetReferenced(Column.DataType).FirstOrDefault().Name.ToString();
columnText = String.Format("[{0}] {1}{2} {3}", columnName, dataType, suffix, nullText);
WriteLine(" " + columnText + ",");
}
}
How to handle varchar(max) in t4 script?

Here the answer.
You would have to use the following and then compare it.
bool isMax = col.GetProperty<bool>(Column.IsMax);
...
if (isMax)
{
suffix = String.Format("({0})", "max");
}

Related

Financial Dimension after save is empty

I have financial dimension which contact values like BuildingID and ContractID.
When new building is created, dimension is properly filled with data. But, after that there is need for contract creation.
When contract is created there is value in financial dimension field for contractID.
But, when contract is saved, financial dimension for contract id disappear. When I check in DIMENSIONATTRIBUTEVALUESET table value for that ContractID dimension is null, there is only value for BuildingID.
I have this method for init dimensions:
void initDimensions()
{
DimensionDefault dimension;
PMGOrgDimension orgDimension;
CompanyId companyId;
PMEGround ground;
PMEBuilding building;
switch(pmcContract.EstateType)
{
case PMCEstateType::Ground :
ground = PMEGround::find(pmcContract.EstateId);
dimension = PMEObjectLegalEntity::find(ground.TableId, ground.RecId).DefaultDimension;
orgDimension = ground.OrgDimension;
companyId = ground.CompanyId;
break;
case PMCEstateType::Building :
building = PMEBuilding::find(pmcContract.EstateId);
dimension = PMEObjectLegalEntity::find(building.TableId, building.RecId).DefaultDimension;
orgDimension = building.OrgDimension;
companyId = building.CompanyId;
break;
default :
dimension = pmcContract.DefaultDimension;
orgDimension = pmcContract.OrgDimension;
companyId = pmcContract.CompanyId;
break;
}
pmcContract.DefaultDimension = dimension;
pmcContract.OrgDimension = orgDimension;
pmcContract.CompanyId = companyId;
}
Is there something what I missing ?
Try changing this line:
pmcContract.DefaultDimension = dimension;
To this:
pmcContract.DefaultDimension = DimensionDefaultingService::serviceMergeDefaultDimensions(pmcContract.DefaultDimension, dimension);
Issue is in this method:
static server public DimensionDefault tableDimension(Common _c, DimensionDefault _d)
{
DimensionAttribute dimensionAttribute;
DimensionAttributeValue dimensionAttributeValue;
DimensionAttributeSetItem dimensionAttributeSetItem;
DimensionAttributeValueSetStorage dimensionAttributeValueSetStorage;
DimensionDefault cDimensionDefault;
DimensionDefault ret;
;
ret = _d;
select firstonly RecId from dimensionAttribute
where dimensionAttribute.BackingEntityTableId == _c.TableId
join firstonly RecId from dimensionAttributeSetItem
where dimensionAttributeSetItem.DimensionAttributeSet == DimensionCache::getDimensionAttributeSetForLedger()
&& dimensionAttributeSetItem.DimensionAttribute == dimensionAttribute.RecId;
if (dimensionAttributeSetItem.RecId != 0)
{
dimensionAttributeValue = DimensionAttributeValue::findByDimensionAttributeAndEntityInst(dimensionAttribute.RecId, _c.RecId, false, true);
if (dimensionAttributeValue.RecId != 0)
{
dimensionAttributeValueSetStorage = new DimensionAttributeValueSetStorage();
dimensionAttributeValueSetStorage.addItemValues(dimensionAttributeValue.DimensionAttribute, dimensionAttributeValue.RecId, dimensionAttributeValue.HashKey);
cDimensionDefault = dimensionAttributeValueSetStorage.save();
if (cDimensionDefault != 0)
{
ret = LedgerDimensionDefaultFacade::serviceMergeDefaultDimensions(cDimensionDefault, _d);
}
}
}
return ret;
}
Merge is not working. It only takes values for _d. Not merging them.

XtraGrid AutoFilterRow custom range filtering

I'm trying to improve the AutoFilterRow functionality for one of my columns. The column will always consist of a string that represents a range of values like this: "num1 - num2". I would like to allow end users to type a value into the cell in the AutoFilterRow and in this particular column and the rows whose sections have a range that includes the number they typed. For instance, if I had 3 rows and each of their section attributes were the following: "1 - 4", "1 - 6", and "4 - 6", and a user types "3" into the AutoFilterRow cell for this column, I would expect the rows containing "1 - 4" and "1 - 6".
I have already overwritten the CreateAutoFilterCriterion in MyGridView to allow for additional operators as suggested in several examples found on this site:
protected override CriteriaOperator CreateAutoFilterCriterion(GridColumn column, AutoFilterCondition condition, object _value, string strVal)
{
if ((column.ColumnType == typeof(double) || column.ColumnType == typeof(float) || column.ColumnType == typeof(int)) && strVal.Length > 0)
{
BinaryOperatorType type = BinaryOperatorType.Equal;
string operand = string.Empty;
if (strVal.Length > 1)
{
operand = strVal.Substring(0, 2);
if (operand.Equals(">="))
type = BinaryOperatorType.GreaterOrEqual;
else if (operand.Equals("<="))
type = BinaryOperatorType.LessOrEqual;
else if (operand.Equals("<>"))
type = BinaryOperatorType.NotEqual;
}
if (type == BinaryOperatorType.Equal)
{
operand = strVal.Substring(0, 1);
if (operand.Equals(">"))
type = BinaryOperatorType.Greater;
else if (operand.Equals("<"))
type = BinaryOperatorType.Less;
else if (operand.Equals("!") || operand.Equals("~"))
type = BinaryOperatorType.NotEqual;
}
if (type != BinaryOperatorType.Equal)
{
string val = strVal.Replace(operand, string.Empty);
try
{
if (!val.IsEmpty())
{
if (column.ColumnType == typeof(double))
{
var num = Double.Parse(val, NumberStyles.Number, column.RealColumnEdit.EditFormat.Format);
return new BinaryOperator(column.FieldName, num, type);
}
if (column.ColumnType == typeof(float))
{
var num = float.Parse(val, NumberStyles.Number, column.RealColumnEdit.EditFormat.Format);
return new BinaryOperator(column.FieldName, num, type);
}
else
{
var num = int.Parse(val, NumberStyles.Number, column.RealColumnEdit.EditFormat.Format);
return new BinaryOperator(column.FieldName, num, type);
}
}
// DateTime example:
// DateTime dt = DateTime.ParseExact(val, "d", column.RealColumnEdit.EditFormat.Format);
// return new BinaryOperator(column.FieldName, dt, type);
}
catch
{
return null;
}
}
}
//
// HERE IS WHERE I WANT TO ADD THE FUNCTIONALITY I'M SPEAKING OF
//
else if (column.FieldName == "SectionDisplayUnits")
{
try
{
if (!strVal.IsEmpty())
{
}
}
catch
{
return null;
}
}
return base.CreateAutoFilterCriterion(column, condition, _value, strVal);
}
How would I go about doing that? I figure I want to split each string with a call to Split(...) like this: cellString.Split(' - '). Then I would parse each string returned from the call to Split(...) into a number so that I could use inequality operators. But I'm just not sure how to go about doing this. Can I get some help? Thanks!
Update:
Please take a look here for a more in-depth discussion on this matter with myself and a knowledgeable DevExpress representative. I received a lot of help and I wanted to share this knowledge with whoever needs similar assistance. Here is the link.
Using C#, you would split the value into two parts, convert them to the number, and compare the value entered by the user with both values to ensure that it is greater or equal the first part and less or equal the second part.
In Criteria Language, the same functionality can be created using Function Operators. However, the expression will be a bit complex. Please try the following. It will work only if the format of values in the SectionDisplayUnits column is fixed, and the value always consists of two numbers delimited by "-".
string rangeDelimiter = "-";
return CriteriaOperator.Parse("toint(trim(substring(SectionDisplayUnits, 0, charindex(?, SectionDisplayUnits)))) <= ? && toint(trim(substring(SectionDisplayUnits, charindex(?, SectionDisplayUnits) + 1, len(SectionDisplayUnits) - charIndex(?, SectionDisplayUnits) - 1))) >= ?", rangeDelimiter, _value, rangeDelimiter, rangeDelimiter, _value);

The best overloaded method match for int tryparse Error

I'm simply trying to do this, so later on when I save my values in the database they should be set to null incase the textfield is empty.
int? DeliveryAdrID = null;
int.TryParse(TextBoxDeliveryAdrID.Text, out DeliveryAdrID);
But I'm having an error parsing it along.
The above solution should later on make it possible to save empty textbox values in the database as "NULL" instead of 0.
The whole solution:
int parsedValue;
int? DeliveryAdrID = int.TryParse(TextBoxDeliveryAdrID.Text, out parsedValue) ? parsedValue : (int?)null;
int id = Convert.ToInt32(GridViewData.SelectedValue.ToString());
var data = tf.DBTable.Where(a => a.ID == id).FirstOrDefault();
if (data == null)
{
DBTable mt = new DBTable();
mt.Customer = TextBoxCustomer.Text;
mt.Country = TextBoxCountry.Text;
mt.DeliveryAdrID = parsedValue;
tf.DBTable.AddObject(mt);
tf.SaveChanges();
}
else
{
data.Customer = TextBoxCustomer.Text;
data.Country = TextBoxCountry.Text;
data.DeliveryAdrID = parsedValue;
tf.SaveChanges();
}
}
You cannot give a nullable int to int.TryParse. It must be an int. What you are trying to do can be accomplished like so:
int parsedValue;
int? DeliveryAdrID = int.TryParse(TextBoxDeliveryAdrID.Text, out parsedValue) ? parsedValue : (int?) null;

Dynamics AX 2009 X++ Selecting A Date Range

I am creating an X++ report, and the requirement is that the user can multi-select on a form and when they click the report menu button the values are pulled in based on the selection.
So far this is easy enough, and I can pull in Str ranges i.e. order numbers, item id's etc, but I want to be able to pull in a date range based on selection.
I have used a method which several MorphX reports use, with use of 3 key methods in X++ reporting;
setQuerySortOrder
setQueryEnableDS
and the main key one which is;
setQueryRange
The code for setQuery Range is as follows;
private void setQueryRange(Common _common)
{
FormDataSource fds;
LogisticsControlTable logisticsTable;
QueryBuildDataSource qbdsLogisticsTable;
QueryBuildRange qbrVanRun;
str rangeVanRun;
QueryBuildRange qbrLogId;
str rangeLogId;
QueryBuildRange qbrExpStartDate;
str rangeExpStartDate;
set vanRunSet = new Set(Types::String);
set logIdSet = new Set(Types::String);
set expStartDate = new Set(Types::Date);
str addRange(str _range, str _value, QueryBuildDataSource _qbds, int _fieldNum, Set _set = null)
{
str ret = _range;
QueryBuildRange qbr;
;
if(_set && _set.in(_Value))
{
return ret;
}
if(strLen(ret) + strLen(_value) + 1 > 255)
{
qbr = _qbds.addRange(_fieldNum);
qbr.value(ret);
ret = '';
}
if(ret)
{
ret += ',';
}
if(_set)
{
_set.add(_value);
}
ret += _value;
return ret;
}
switch(_common.TableId)
{
case tableNum(LogisticsControlTable):
qbdsLogisticsTable = element.query().dataSourceTable(tableNum(LogisticsControlTable));
qbrVanRun = qbdsLogisticsTable.addRange(fieldNum(LogisticsControlTable, APMServiceCenterID));
qbdsLogisticsTable = element.query().dataSourceTable(tableNum(LogisticsControlTable));
qbrLogId = qbdsLogisticsTable.addRange(fieldNum(LogisticsControlTable, LogisticsId));
// qbdsLogisticsTable = element.query().dataSourceTable(tableNum(LogisticsControlTable));
// qbrExpStartDate = qbdsLogisticsTable.addRange(fieldNum(LogisticsControlTable, APMExpDateJobStart));
fds = _common.dataSource();
for(logisticsTable = fds.getFirst(true) ? fds.getFirst(true) : _common;
logisticsTable;
logisticsTable = fds.getNext())
{
rangeVanRun = addrange(rangeVanRun, logisticsTable.APMServiceCenterID, qbdsLogisticsTable, fieldNum(LogisticsControlTable, APMServiceCenterID), vanRunSet);
rangeLogID = addrange(rangeLogID, logisticsTable.LogisticsId, qbdsLogisticsTable, fieldNum(LogisticsControlTable, LogisticsId), logIdSet);
// rangeExpStartDate = addrange(rangeExpStartdate, logisticsTable.APMExpDateJobStart, qbdsLogisticsTable, fieldNum(LogisticsControlTable, APMExpDateJobStart), expStartDate);
}
qbrLogId.value(rangeLogID);
qbrVanRun.value(rangeVanRun);
break;
}
}
Use queryValue to format your dates correctly for the query:
set expStartDate = new Set(Types::String);
rangeExpStartDate = addrange(rangeExpStartdate, queryValue(logisticsTable.APMExpDateJobStart), qbdsLogisticsTable, fieldNum(LogisticsControlTable, APMExpDateJobStart), expStartDate);

queries in entity framework

i have written the following query and it is giving error Unable to cast object of type
System.Data.Objects.ObjectQuery'[ITClassifieds.Models.Viewsearch]' to type 'ITClassifieds.Models.Viewsearch'.
my code is as follows
if (zipcode.Contains(","))//opening of zipcode conatins comma
{
do
{
zipcode = zipcode.Replace(" ", " ");
zipcode = zipcode.Replace(", ", ",");
} while (zipcodecity.Contains(" "));
char[] separator = new char[] { ',' };
string[] temparray = zipcode.Split(separator);
var zipcd = (from u in db.ZipCodes1
where u.CityName == temparray[0] && u.StateAbbr == temparray[1] && u.CityType == "D"
select new Viewsearch
{
Zipcode = u.ZIPCode
}).Distinct();
Viewsearch vs = (Viewsearch)zipcd;
if (zipcd.Count() > 0)
{
zipcode = vs.Zipcode;
locations = "";
}
else
{
tempStr = "";
zipcode = "";
}
}
You need to do
If it will always exist:
Viewsearch vs = zipcd.First()
If not use, and then check for null before using
Viewsearch vs = zipcd.FirstOrDefault()
You could also use Single if there will always be 1 or None.
The Distinct method returns an enumerable collection (in your case, and ObjectQuery<T>, which may contain more than one element. You can't typecast that directly to an item in the collection, you need to use one of the IEnumerable methods to get it:
Viewsearch vs = zipcd.SingleOrDefault();
if ( vs != null )
{
zipcode = vs.Zipcode;
locations = String.Empty;
}
else
{
zipcode = String.Empty;
tempStr = String.Empty;
}
SingleOrDefault will throw an exception if there is more than one item in the collection; if that's a problem, you can also use FirstOrDefault to grab the first item, as one example.
Also, unrelated to your question, but you don't need the temporary array variable for your string separators. The parameter to the Split method is a params array so you can just call it like this:
string[] temparray = zipcode.Split(',');
Replace the zipcd query with:
var cityName = temparray[0];
var stateAbbr = temparray[1];
Viewsearch vs = new Viewsearch {
Zipcode = db.ZipCodes1.Where(u.CityName == cityName && u.StateAbbr == stateAbbr && u.CityType == "D").First().ZIPCode
};

Resources