I've heavily customised the Project Transaction Report (projlisttransproj, and I'm displaying Resource ID. I would like to display the name of this resource instead (see insert method). I'm fairly new to x++ development so step by step would be greatly appreciated. I have had a look in projtrans class and found below, but found nothing referring to name... thanks!
/// <summary>
/// Retrieves the ID of the employee that is associated with this transaction depending on the
/// transaction type that is returned by the <c>ProjTrans.transType</c> method.
/// </summary>
/// <returns>
/// The <c>RecID</c> value of the employee that is associated with this transaction.
/// </returns>
/// <remarks>
/// For hour, cost, and revenue transactions, the employee ID will be returned. For all other
/// transactions, 0 will be returned.
/// </remarks>
public ResourceRecId projIdentResource()
{
ResourceRecId resourceRecId;
switch(this.transType())
{
case ProjTransType::Hour:
case ProjTransType::Cost:
case ProjTransType::Revenue:
resourceRecId = this.resource();
break;
default:
resourceRecId = 0;
}
return resourceRecId;
}
public void insertProjTransList()
{
tmpProjTransListExtension.clear();
tmpProjTransListExtension.VoucherInvoice = projTrans.voucherInvoice();
tmpProjTransListExtension.VoucherJournal = projTrans.voucherOriginal();
tmpProjTransListExtension.LinePropertyId = projTrans.linePropertyId();
tmpProjTransListExtension.ActivityNumber = projTrans.activityNumber();
tmpProjTransListExtension.CategoryId = projTrans.categoryId();
tmpProjTransListExtension.CostPrice = projTrans.costPrice();
tmpProjTransListExtension.CurrencyId = projTrans.currencyIdSales();
tmpProjTransListExtension.DefaultDimension = projTrans.defaultDimension();
tmpProjTransListExtension.SalesAmount = projTrans.transTurnoverMST();
tmpProjTransListExtension.CostAmount = projTrans.transCostMST();
tmpProjTransListExtension.ProjIdOrig = projTrans.projId();
tmpProjTransListExtension.ProjId = firstProjId;
tmpProjTransListExtension.Qty = projTrans.qty();
tmpProjTransListExtension.SalesPrice = projTrans.salesPrice();
tmpProjTransListExtension.TransDate = projTrans.transDate();
tmpProjTransListExtension.Txt = projTrans.txt();
tmpProjTransListExtension.TransType = projTrans.transType();
tmpProjTransListExtension.ProjId = firstProjId;
TmpProjTransListExtension.ProjName = firstProjName;
tmpProjTransListExtension.Type = ProjCategory::find(projTrans.categoryId()).CategoryType;
TmpProjTransListExtension.Resource = ProjTrans.resource(); //Want Name of resource not ID
tmpProjTransListExtension.insert();
}
You can get worker's name with HcmWorker::find(ProjTrans.resource()).name().
Related
I want to return a View with a specific path to the view like so, return View(~/Views/Home). The type to be returned is an IViewComponentResult.
However, when the site gets rendered, it tries to find the view under: Components/{controller-name}/~/Views/Home.
So I want to ask you guys, if you know a smart way to delete Components/{controller-name}/ from the path. My view component class is deriving from the ViewComponent class.
The problem regarding this is that right now, you have to have a "Components" folder with the name of the controller as a subfolder which contains a Default.cshtml file. I do not like to have all my components inside one folder. I hope you have a solution.
The convention happened in ViewViewComponentResult hence if you don't want the convention, you will have to implement your own IViewComponentResult
since mvc is open source, you could copy all the ViewViewComponentResult and then change the convention bit.
so essentially two things has to be done:
create your own IViewComponentResult - lets call it MyViewViewComponentResult
create a helper in your implemented ViewComponent to replace the original View() - purpose is to returns the custom MyViewViewComponentResult you created at step 1
create your own IViewComponentResult
the convention happened at ExecuteAsync:
public class ViewViewComponentResult : IViewComponentResult
{
// {0} is the component name, {1} is the view name.
private const string ViewPathFormat = "Components/{0}/{1}";
private const string DefaultViewName = "Default";
....
public async Task ExecuteAsync(ViewComponentContext context)
{
....
if (result == null || !result.Success)
{
// This will produce a string like:
//
// Components/Cart/Default
//
// The view engine will combine this with other path info to search paths like:
//
// Views/Shared/Components/Cart/Default.cshtml
// Views/Home/Components/Cart/Default.cshtml
// Areas/Blog/Views/Shared/Components/Cart/Default.cshtml
//
// This supports a controller or area providing an override for component views.
var viewName = isNullOrEmptyViewName ? DefaultViewName : ViewName;
var qualifiedViewName = string.Format(
CultureInfo.InvariantCulture,
ViewPathFormat,
context.ViewComponentDescriptor.ShortName,
viewName);
result = viewEngine.FindView(viewContext, qualifiedViewName, isMainPage: false);
}
....
}
.....
}
so change it to your need
create a helper in your implemented ViewComponent which replace the original View
so base on original
/// <summary>
/// Returns a result which will render the partial view with name <paramref name="viewName"/>.
/// </summary>
/// <param name="viewName">The name of the partial view to render.</param>
/// <param name="model">The model object for the view.</param>
/// <returns>A <see cref="ViewViewComponentResult"/>.</returns>
public ViewViewComponentResult View<TModel>(string viewName, TModel model)
{
var viewData = new ViewDataDictionary<TModel>(ViewData, model);
return new ViewViewComponentResult
{
ViewEngine = ViewEngine,
ViewName = viewName,
ViewData = viewData
};
}
you would do something like
/// <summary>
/// Returns a result which will render the partial view with name <paramref name="viewName"/>.
/// </summary>
/// <param name="viewName">The name of the partial view to render.</param>
/// <param name="model">The model object for the view.</param>
/// <returns>A <see cref="ViewViewComponentResult"/>.</returns>
public MyViewViewComponentResult MyView<TModel>(string viewName, TModel model)
{
var viewData = new ViewDataDictionary<TModel>(ViewData, model);
return new MyViewViewComponentResult
{
ViewEngine = ViewEngine,
ViewName = viewName,
ViewData = viewData
};
}
so in future you would call MyView() instead of View()
more info check out another SO: Change component view location in Asp.Net 5
I'm a bit new to programing a windows store app.So the question is how can I save an instance of a class in to an xml or binary file.I tried some code but it isn't working.
Hope that some one can steer me in the right direction .
You can serialize your instance by using this code
/// <summary>
/// Deserializes the XML.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="xml">The XML.</param>
/// <returns>The instance</returns>
public static T DeserializeXml<T>(this String xml)
{
var bytes = Encoding.UTF8.GetBytes(xml);
using (var stream = new MemoryStream(bytes))
{
var serializer = new DataContractSerializer(typeof(T));
return (T)serializer.ReadObject(stream);
}
}
/// <summary>
/// Serializes the specified instance.
/// </summary>
/// <param name="instance">The instance.</param>
/// <returns>Xml</returns>
public static String SerializeXml(this Object instance)
{
using (var stream = new MemoryStream())
{
var serializer = new DataContractSerializer(instance.GetType());
serializer.WriteObject(stream, instance);
stream.Position = 0;
using (var reader = new StreamReader(stream))
{
var result = "<?xml version='1.0' encoding='UTF-8' ?>";
result += reader.ReadToEnd();
return result;
}
}
}
Next step is to save the serialized instance text to a file.
var filename = "instance.txt";
var file = await ApplicationData.Current.LocalFolder.CreateFileAsync(filename, CreationCollisionOption.OpenIfExists);
var content = yourInstance.SerializeXml();
await FileIO.WriteTextAsync(file, content, Windows.Storage.Streams.UnicodeEncoding.Utf8);
Now there should be a file in your AppPackage-Local-Folder called instance.txt which contains the current instance serialized to xml.
You can use Windows.Storage to store any file, the usage is like IO operation. MSDN
IsolatedStorage is similar to this for Windows Phone apps.
I'm hoping to be able to allow optional parameters to be specified, so I can overload the Accumulate() method, can it be done?
I'd like to overload to allow a delimiter to specified, I've seen others where it must force a delimiter to be specified but this behaviour doesn't suit.
CREATE AGGREGATE dbo.Concatenate (#input nvarchar(max), <OPTIONAL PARAMETER HERE>)
RETURNS nvarchar(max)
For reference, here is the aggregate class code that contains the Accumulate() method I'm looking to overload:
using System;
using System.Data;
using Microsoft.SqlServer.Server;
using System.Data.SqlTypes;
using System.IO;
using System.Text;
namespace CLR.Utilities {
/// <summary>
/// <list type="references">
/// <reference>
/// <name>How to: Create and Run a CLR SQL Server Aggregate</name>
/// <link>http://msdn.microsoft.com/en-us/library/91e6taax(v=vs.90).aspx</link>
/// </reference>
/// <reference>
/// <name>SqlUserDefinedAggregateAttribute</name>
/// <link>http://msdn.microsoft.com/en-us/library/microsoft.sqlserver.server.sqluserdefinedaggregateattribute(v=vs.90).aspx</link>
/// </reference>
/// <reference>
/// <name>Invoking CLR User-Defined Aggregate Functions (Provides seed code for this function)</name>
/// <link>http://technet.microsoft.com/en-us/library/ms131056.aspx</link>
/// </reference>
/// </list>
/// </summary>
[Serializable]
[SqlUserDefinedAggregate(
Format.UserDefined, //use clr serialization to serialize the intermediate result
IsInvariantToNulls = true, //optimizer property
IsInvariantToDuplicates = false, //optimizer property
IsInvariantToOrder = false, //optimizer property
MaxByteSize = -1) //no maximum size in bytes of persisted value
]
public class Concatenate : IBinarySerialize {
/// <summary>
/// The variable that holds the intermediate result of the concatenation
/// </summary>
private StringBuilder intermediateResult;
/// <summary>
/// Initialize the internal data structures
/// </summary>
public void Init() {
this.intermediateResult = new StringBuilder();
}
/// <summary>
/// Accumulate the next value, not if the value is null
/// </summary>
/// <param name="value"></param>
public void Accumulate([SqlFacet(MaxSize = -1)] SqlString value) {
if (value.IsNull) {
return;
}
this.intermediateResult.Append(value.Value.Trim()).Append(',');
}
/// <summary>
/// Merge the partially computed aggregate with this aggregate.
/// </summary>
/// <param name="other"></param>
public void Merge(Concatenate other) {
this.intermediateResult.Append(other.intermediateResult);
}
/// <summary>
/// Called at the end of aggregation, to return the results of the aggregation.
/// </summary>
/// <returns></returns>
[return: SqlFacet(MaxSize = -1)]
public SqlString Terminate() {
string output = string.Empty;
//delete the trailing comma, if any
if (this.intermediateResult != null
&& this.intermediateResult.Length > 0) {
output = this.intermediateResult.ToString(0, this.intermediateResult.Length - 1).Trim();
}
return new SqlString(output);
}
public void Read(BinaryReader r) {
intermediateResult = new StringBuilder(r.ReadString());
}
public void Write(BinaryWriter w) {
w.Write(this.intermediateResult.ToString().Trim());
}
}
}
And here is the code for deployment that I'd like to modify also if optional parameters can be set:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Concatenate]') AND type = N'AF')
DROP AGGREGATE [dbo].[Concatenate]
GO
IF EXISTS (SELECT * FROM sys.assemblies asms WHERE asms.name = N'CLR.Utilities' and is_user_defined = 1)
DROP ASSEMBLY [CLR.Utilities]
GO
ALTER DATABASE [DatabaseName] SET TRUSTWORTHY ON
GO
CREATE ASSEMBLY [CLR.Utilities] FROM 'C:\Path\To\File\CLR.Utilities.dll' WITH PERMISSION_SET = UNSAFE
GO
CREATE AGGREGATE [dbo].[Concatenate] (#input nvarchar(max)) RETURNS nvarchar(max)
EXTERNAL NAME [CLR.Utilities].[CLR.Utilities.Concatenate]
GO
GRANT EXECUTE ON [dbo].[Concatenate] TO PUBLIC
GO
You could, however, make the parameter mandatory but optionally null. And, in your C# code test whether the value is null and act accordingly. Something like:
public void Accumulate([SqlFacet(MaxSize = -1)] SqlString value, SqlString delimiter) {
string _delimiter;
if (value.IsNull) {
return;
}
if (delimiter.IsNull) {
_delimiter = string.Empty;
}
else {
_delimiter = Delimiter.Value;
}
this.intermediateResult.Append(value.Value.Trim()).Append(_delimiter);
}
As far as I know, there's no way to make clr function or aggregate with optional parameters and that's sad.
There is a way, though it isn't nice. You simple add the delimiter to the end of the column you're concatenating with a char(0) between them. then in the clr, you can look for char(0) and grab the delimiter to use.
-- concat columns delimited by a comma
select grp,dbo.clrConcat(surname) as names
from people
-- concat column delimited by specified character
select grp,dbo.clrConcat(surname + char(0) + '|') as names
from people
Then in the clr code
''' <summary>
''' Initialize the internal data structures
''' </summary>
Public Sub Init()
Me.intermediateResult = New StringBuilder()
delimiter = ","
End Sub
''' <summary>
''' Accumulate the next value, not if the value is null
''' </summary>
''' <param name="value"></param>
Public Sub Accumulate(ByVal value As SqlString)
Dim Str As String
Dim Pos As Integer
If value.IsNull Then Return
Pos = value.Value.IndexOf(Chr(0))
If Pos >= 0 Then
Str = value.Value.Substring(0, Pos)
If Pos + 1 < value.Value.Length Then
delimiter = value.Value.Substring(Pos + 1, 1)
End If
Else
Str = value.Value
End If
Me.intermediateResult.Append(Str).Append(delimiter)
End Sub
PASSWORD() according to MySQL documentation is a double SHA1 algorithm.
In Win32 I was using this method:
public string GenerateMySQLHash(string key)
{
byte[] keyArray = Encoding.UTF8.GetBytes(key);
SHA1Managed enc = new SHA1Managed();
byte[] encodedKey = enc.ComputeHash(enc.ComputeHash(keyArray));
StringBuilder myBuilder = new StringBuilder(encodedKey.Length);
foreach (byte b in encodedKey)
myBuilder.Append(b.ToString("X2"));
return "*" + myBuilder.ToString();
}
SHA1Managed object is not available in the Metro .net framework because the security stuff is now in Windows.Security.Cryptography and not in System.Security.Cryptography.
In the documentation, I see this example to obtain the SHA1 from a string:
public String HashMsg(String strMsg)
{
// Convert the message string to binary data.
IBuffer buffUtf8Msg = CryptographicBuffer.ConvertStringToBinary(strMsg, BinaryStringEncoding.Utf8);
// Create a HashAlgorithmProvider object.
HashAlgorithmProvider objAlgProv = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha1);
// Hash the message.
IBuffer buffHash = objAlgProv.HashData(buffUtf8Msg);
// Verify that the hash length equals the length specified for the algorithm.
if (buffHash.Length != objAlgProv.HashLength)
{
throw new Exception("There was an error creating the hash");
}
// Convert the hash to a string (for display).
return CryptographicBuffer.EncodeToBase64String(buffHash);
}
But I need a double SHA1 algorithm. Any way to do this easy as in win32?
I finally found the solution :), I hope it help you:
/// <summary>
/// Reverse a string
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static string ReverseString(string s)
{
char[] arr = s.ToCharArray();
Array.Reverse(arr);
return new string(arr);
}
/// <summary>
/// MySQL PASSWORD encryption
/// </summary>
/// <param name="strMsg"></param>
/// <returns></returns>
public String HashMsg(String strMsg)
{
// Convert the message string to binary data.
IBuffer buffUtf8Msg = CryptographicBuffer.ConvertStringToBinary(strMsg, BinaryStringEncoding.Utf8);
// Create a HashAlgorithmProvider object.
HashAlgorithmProvider objAlgProv = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha1);
// Hash the message.
IBuffer buffHash = objAlgProv.HashData(objAlgProv.HashData(buffUtf8Msg));
// Verify that the hash length equals the length specified for the algorithm.
if (buffHash.Length != objAlgProv.HashLength)
{
throw new Exception("There was an error creating the hash");
}
byte[] arrByteNew;
CryptographicBuffer.CopyToByteArray(buffHash, out arrByteNew);
StringBuilder myBuilder = new StringBuilder(arrByteNew.Length);
foreach (var b in arrByteNew)
myBuilder.Append(b.ToString("X2"));
// Concat with the STRING REVERSED
String stringReversed = "*" + myBuilder.ToString() + ReverseString(strMsg);
buffUtf8Msg = CryptographicBuffer.ConvertStringToBinary(s3, BinaryStringEncoding.Utf8);
buffHash = objAlgProv.HashData(objAlgProv.HashData(buffUtf8Msg));
if (buffHash.Length != objAlgProv.HashLength)
{
throw new Exception("There was an error creating the hash");
}
CryptographicBuffer.CopyToByteArray(buffHash, out arrByteNew);
myBuilder = new StringBuilder(arrByteNew.Length);
foreach (var b in arrByteNew)
{
myBuilder.Append(b.ToString("X2"));
}
stringReversed = "*" + myBuilder.ToString();
return stringReversed;
}
I have an Enum defined in a C# dll with the [Flags] attribute:
[Flags]
[DataContract(Namespace = "MyApp")]
public enum MobileNotifications
{
/// <summary>
///
/// </summary>
[EnumMember]
None,
/// <summary>
///
/// </summary>
[EnumMember]
Msg1,
/// <summary>
///
/// </summary>
[EnumMember]
Msg2,
/// <summary>
///
/// </summary>
[EnumMember]
Msg3,
/// <summary>
///
/// </summary>
[EnumMember]
Msg4
}
When I call a method "GetFlags", it returns the value as a single Int value.
MobileNotifications GetNotifications()
{
return Msg1 | Msg4;
}
The method above returns 5 as a value. Is there a way for WCF to pass an enum flag that can be parsed for json?
This is a .NET 4.0 WCF service.
First, you need to define explicit enumeration constants in powers of two to avoid any overlapping of differnt value combinations (see http://msdn.microsoft.com/en-gb/library/vstudio/system.flagsattribute.aspx). In order to return that to a Json client in a meaningful way, I'd suggest to ToString() it which actually creates a comma-separated list of the actual member names (which you can then parse on the client side). Example:
[Flags]
public enum MobileNotifications
{
None = 0,
Msg1 = 1,
Msg2 = 2,
Msg3 = 4
}
var value = MobileNotifications.Msg1 | MobileNotifications.Msg3;
var asString = string.Format("{0}", value); // will create: "Msg1, Msg3"
Personally I would have another look at this design. What does 5 mean? It can be made up a couple of different combinations can't it - Msg 1 | Msg4, Msg 2| Msg 3 etc.. The same goes for the rest. Can the consumer differentiate between the combinations? If not I would suggest making this more explicit or actually changing the enum values so that the combinations are unique such as
None =0,
Msg1 = 1,
Msg2 = 2,
Msg3 = 4,
Msg4 = 8