How to instantiate classes and their properties in F# - c#

Follow Code C#:
var body = new CustomerRequest
{
Method = "CREDIT_CARD",
CreditCard = new Creditcard
{
ExpirationMonth = "06",
ExpirationYear = "2022",
Number = "4012001037141112",
Cvc = "123"
}
};
I'm new to F#, I can not instantiate classes like C#, See the code below in F#:
let body = CustomerRequest
(
Method = "CREDIT_CARD" // Help here
)
I can not convert C # to F #

If you are doing idiomatic F# you would model this with Records instead of classes.
You could do it like this:
type CreditCard = {
ExpirationMonth: int;
//More
}
type CustomerRequest = {
Method: string;
CreditCard: CreditCard;
}
let req = {
Method = "Credit"
CreditCard = {
ExpirationMonth = 6
//More
}
}
The compiler has type-inference that means it can guess that req is a CustomerRequest by the fields you have in it, and the same for the CreditCard - you can hint the type if you really need to.
If you really are after classes - perhaps you have to interop with C# code, then you would do it like this:
type CreditCard2(expirationMonth:int) =
member this.ExpirationMonth = expirationMonth
type CustomerRequest2(method: string, creditCard: CreditCard2) =
member this.Method = method
member this.CreditCard = creditCard
let req2 = CustomerRequest2 ("Credit", CreditCard2 (5))

If you just want to use named arguments to set properties of the classes you are constructing: the syntax is quite similar to C#. As for the level of indentation, you need to align with that of the first argument.
let body =
CustomerRequest(
Method = "CREDIT_CARD",
CreditCard =
Creditcard(
ExpirationMonth = "06",
ExpirationYear = "2022",
Number = "4012001037141112",
Cvc = "123" ) )

I think this is what you are looking for:
CreditCard.fs
namespace StackOverflow
type CreditCard() =
member val ExpirationDate = "" with get, set
member val ExpirationYear = "" with get, set
member val Number = "" with get, set
member val Cvc = "" with get, set
type CustomerRequest() =
member val Method = "" with get, set
member val CreditCard = new CreditCard() with get, set
Program.fs
open System
open StackOverflow
[<EntryPoint>]
let main argv =
let body = new CustomerRequest(Method = "CREDIT_CARD",
CreditCard = CreditCard(
ExpirationDate = "06",
ExpirationYear = "2022",
Number = "4012001037141112",
Cvc = "123"
))
0
I do not know much about F# so maybe using Records like David Shaw uses is better design wise. But basically this will let you do it without a parameter constructor and without a mutable non property value in your type.

Related

C# Complex class construct Read/Set Value from JSON

I have a problem; I have various (too many) classes, that are linked as Parent/Child
I populate initially my top class while instantiate the others, as follows (works):
TopClass MyClass = new TopClass()
{
Headers = new someHeaders()
{
CorrID = "1234567890",
RequestID = "1234567890",
L_Token = "abcdefghijklmnopqrstuvwxyz"
},
Body = new someBody()
{
access = new AccessHeaders()
{
allPs = "allAcc",
availableAcc = "all"
},
CombinedServiceIndicator = false,
FrequencyPerDay = 10,
ValidUntil = "2020-12-31"
},
ResponseAttributes = new ConsentResponse()
{
myId = String.Empty,
myStatus = String.Empty,
myName = String.Empty,
_links_self_href = String.Empty,
status_href = String.Empty
}
};
The initials values that I populate above rarely change, but the Classes' properties change as the project goes on, and each class ends up having many properties.
I need to parse the properties and set values to any properties that match their name, but I can't seem to figure out why despite following official examples.
(I read the input data from a JSON string I use Newtonsoft - have tried everything out there)
I can't find a way to deserialize the JSON and assign any values to my properties without using the name of the property, i.e. without saying
MyClass.Body.access.allPs = "Hello"
but something like
var json = response.Content.ReadAsStringAsync().Result.ToString();
var a = new { serverTime = "", data = new object[] { } };
var c = new JsonSerializer();
or
if (myresponse.Attributes.GetType().GetTypeInfo().GetDeclaredProperty(key) != null)
myresponse.GetType().GetTypeInfo().GetDeclaredProperty(key).SetValue(myresponse, entry.Value);
//and how do I read the value, parse my TopClass and assign the value to correct property?
Could someone help me?
Values for the properties will get automatically assigned if the JSON is having the correct structure as per the class mentioned above.
Something like:
{
"Body":
"access:
{
"allPs" = "required value"
}
}
Then you can use:
var result = JsonConvert.DeserializeObject < TopClass > (json );

Cannot implicitly convert anonymous type to System.Collections.Generic.List

I am trying to add an object to my Algolia database using a slightly different structure provided by the documentation so that I don't have to type out the Json object in string format, however I ran into an error stating
Cannot implicitly convert anonymous type to
System.Collections.Generic.List
I see the red error message for all of the key/values in the objs variable.
var songIndexHelper = HttpContext.Application.Get("SongIndexHelper") as IndexHelper<SongAlgoliaModel>;
List<JObject> objs = new List<JObject>();
objs = new
{
ApprovalFL = false,
FreeFL = album.FreeFL,
LicenseFL = album.LicenseFL,
AccountInfoID = album.AccountInfoID,
AlbumID = album.AlbumID,
SongID = song.SongID,
BPM = song.BPM,
AccountImageURL = album.AccountInfo.ImageURL,
AccountType = "Artist",
AlbumName = album.AlbumName,
Artist = artist,
FeaturedArtist = songArtistsList,
ImageURL = album.ImageURL,
iTunesURL = album.iTunesURL,
LabelName = album.LabelName,
Title = album.AlbumName,
UserID = album.AccountInfo.UserID,
UploadDate = song.UploadDate,
Duration = song.Duration,
objectID = song.SongID
};
songIndexHelper.AddObjects(objs);
Here's the reference to documentation: https://www.algolia.com/doc/api-reference/api-methods/add-objects/
Edit alternative method however, my formatting of LicenseFL is off
List<JObject> objs = new List<JObject>();
objs.Add(JObject.Parse(#"{""ApprovalFL"":false, ""FreeFL"":" + album.FreeFL + ",""LicenseFL"":" +album.LicenseFL+ "}"));
songIndexHelper.AddObjects(objs);
The Algolia docs are unfortunately focused on the use of JObject (and JSON strings) which makes it reasonably easy to make mistakes (e.g. invalid JSON).
This is an approach you might like to consider:
var anon = new
{
ApprovalFL = true,
// Any other properties here
objectID = song.SongID
};
var obj = JObject.FromObject(anon);
var objs = new List<JObject> { obj };
songIndexHelper.AddObjects(objs);
Now you get some level of safety due to the anon variable (e.g. don't have to worry about invalid JSON strings) but also easy interaction with the Algolia API (which is documented in terms of JObject).

TransactionSearch with body field condition Suitetalk

This is my case, I want to pull the Vendor Credit that have ApplyList.apply.doc (this is Vendor Bill ID) in a List of Vendor Bill ID.
I have to create a TransactionSearch, but I don't know how to apply a condition for this.
For now, I just know to create a search with Status, Type, but that's not enough.
Here is my code :
var transactionsSearch = new TransactionSearch
{
basic = new TransactionSearchBasic
{
//we only want credits with an "Open" status
billingStatus = new SearchBooleanField
{
searchValue = isOpen,
searchValueSpecified = true
},
//only search for those with a type of "_vendorCredit"
type = new SearchEnumMultiSelectField
{
#operator = SearchEnumMultiSelectFieldOperator.anyOf,
operatorSpecified = true,
searchValue = new[] { "_vendorCredit" }
},
}
};
I figured out the answer.
I have to use property Applied To Transaction in TransactionSearchBasic, that will be an array of Record Ref.

C# Reflection: replace all occurrence of property with value in text

I have a class like
public class MyClass {
public string FirstName {get; set;}
public string LastName {get; set;}
}
The case is, I have a string text like,
string str = "My Name is #MyClass.FirstName #MyClass.LastName";
what I want is to replace #MyClass.FirstName & #MyClass.LastName with values using reflection which are assigned to objects FirstName and LastName in class.
Any help please?
If you want to generate the string you can use Linq to enumerate the properties:
MyClass test = new MyClass {
FirstName = "John",
LastName = "Smith",
};
String result = "My Name is " + String.Join(" ", test
.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(property => property.CanRead) // Not necessary
.Select(property => property.GetValue(test)));
// My Name is John Smith
Console.Write(result);
In case you want to substitute within the string (kind of formatting), regular expressions can well be your choice in order to parse the string:
String original = "My Name is #MyClass.FirstName #MyClass.LastName";
String pattern = "#[A-Za-z0-9\\.]+";
String result = Regex.Replace(original, pattern, (MatchEvaluator) ((match) =>
test
.GetType()
.GetProperty(match.Value.Substring(match.Value.LastIndexOf('.') + 1))
.GetValue(test)
.ToString() // providing that null can't be returned
));
// My Name is John Smith
Console.Write(result);
Note, that in order to get instance (i. e. not static) property value you have to provide the instance (test in the code above):
.GetValue(test)
so #MyClass part in the string is useless, since we can get type directly from instance:
test.GetType()
Edit: in case that some properties can return null as value
String result = Regex.Replace(original, pattern, (MatchEvaluator) ((match) => {
Object v = test
.GetType()
.GetProperty(match.Value.Substring(match.Value.LastIndexOf('.') + 1))
.GetValue(test);
return v == null ? "NULL" : v.ToString();
}));
First of all, I'd advice against using reflection when other options such as string.Format is possible. Reflection can make your code less readable and harder to maintain. Either way, you could do it like this:
public void Main()
{
string str = "My Name is #MyClass.FirstName #MyClass.LastName";
var me = new MyClass { FirstName = "foo", LastName = "bar" };
ReflectionReplace(str, me);
}
public string ReflectionReplace<T>(string template, T obj)
{
foreach (var property in typeof(T).GetProperties())
{
var stringToReplace = "#" + typeof(T).Name + "." + property.Name;
var value = property.GetValue(obj);
if (value == null) value = "";
template = template.Replace(stringToReplace, value.ToString());
}
return template;
}
This should require no additional changes if you want to add a new property to your class and update your template string to include the new values. It should also handle any properties on any class.
Using Reflection you can achieve it as shown below
MyClass obj = new MyClass() { FirstName = "Praveen", LaseName = "Paulose" };
string str = "My Name is #MyClass.FirstName #MyClass.LastName";
string firstName = (string)obj.GetType().GetProperty("FirstName").GetValue(obj, null);
string lastName = (string)obj.GetType().GetProperty("LaseName").GetValue(obj, null);
str = str.Replace("#MyClass.FirstName", firstName);
str = str.Replace("#MyClass.LastName", lastName);
Console.WriteLine(str);
You are first finding the relevant Property using GetProperty and then its value using GetValue
UPDATE
Based on further clarification requested in the comment
You could use a regex to identify all placeholders in your string. i.e. #MyClass.Property. Once you have found them you can use Type.GetType to get the Type information and then use the code shown above to get the properties. However you will need the namespace to instantiate the types.
Although this is five years old, I found it useful.
Here is my adaptation of Dmitry Bychenko's that also dives into any JSON fields as well.
Only works on one level, but I'll make it recursive at some point.
Regex _replacementVarsRegex = new Regex(#"\{\{([\w\.]+)\}\}", RegexOptions.Compiled);
var result = "string with {{ObjectTypeName.PropertyName}} and json {{ObjectTypeName.JsonAsStringProperty.JsonProperty}}";
string ReplaceFor(object o) => _replacementVarsRegex.Replace(result, match =>
{
if (o == null) return match.Value;
var typeName = match.Value.Substring(0, match.Value.IndexOf('.')).TrimStart('{');
var type = o.GetType();
if (typeName != type.Name) return match.Value;
var name = match.Value.Substring(match.Value.LastIndexOf('.') + 1).TrimEnd('}');
var propertyInfo = type.GetProperty(name);
var value = propertyInfo?.GetValue(o);
var s = value?.ToString();
if (match.Value.Contains(".Metadata."))
{
var jsonProperty = type.GetProperty("Metadata");
var json = jsonProperty?.GetValue(o)?.ToString() ?? string.Empty;
if (!string.IsNullOrWhiteSpace(json))
{
// var dObj = JsonConvert.DeserializeObject(json);
var jObj = JObject.Parse(json);
var val = jObj[name].Value<string>();
s = val;
}
}
return s ?? match.Value;
});
result = ReplaceFor(object1);
result = ReplaceFor(object2);
//...
result = ReplaceFor(objectn);
//remove any not answered, if desired
result = _replacementVarsRegex.Replace(result, string.Empty);
improvement suggestions welcome!
Working on a project recently I ended up needing something similar. The requirement was to use a placeholder format that wouldn't naturally occur in our text #obj.property may have so #obj.property# was used. Also, handling of cascading tokens (tokens depending on tokens) and handling of data coming from IEnumerable, such as: ["Cat", "Dog", "Chicken"] needing to displayed in the text as "Cat, Dog, Chicken".
I've created a library and have a NuGet package to get this functionality into your code a little quicker.

literal error while working with c# asp.net/ wf

I made a drag-and-drop workflow, and got an Literal error while passing an object through the INargument. Any ideas how I can get around this error?
error: 'Literal': Literal only supports value types and the immutable type System.String. The type System.Object cannot be used as a literal.
I've seen some answers, but all the examples are in hard-coded workflows, and I don't want rewrite the whole workflow in hard-code.
WorkflowActivitycheckdb ActEmail = new WorkflowActivitycheckdb {
EmailList = AdminsToList,
EmailContent = tolist,
UserName = name,
UnApprovedS = UnApproved,
NumberOfUsers = tel,
NumberOfAdmins = tel2
};
WorkflowInvoker.Invoke(ActEmail);
please help.
WorkflowActivitycheckdb ActEmail = new WorkflowActivitycheckdb {
EmailList = new InArgument<Whateveryourobjectis>( x=> AdminsToList),
EmailContent = tolist,
UserName = name,
UnApprovedS = UnApproved,
NumberOfUsers = tel,
NumberOfAdmins = tel2
};
WorkflowInvoker.Invoke(ActEmail);
gl
You could try writing that particular line outside the object initializer.
Eg :
ActEmail.EmailList = AdminsToList;
Make the EmailList class immutable.

Categories

Resources