F# internal visibility changes Record constructor behavior - f#

I'm caling an API using Flurl.
//# models.fs
module models =
type Ticker = { Ask :decimal; Bid :decimal; Last: decimal; High :decimal; Timestamp :int; }
//# Client.fs
namespace MyLibrary
// ... some code
url.GetJsonAsync<models.Ticker>()
This works and I can access the ticker.Ask property.
The class models.Ticker is visible from another C# project and the construcor is this:
public Ticker(decimal ask, decimal bid, decimal last, decimal high, int timestamp);
I don't want expose the models module and the Ticker class/record so I changed the visibility to internal:
# models.fs
module internal models =
type Ticker = { Ask :decimal; Bid :decimal; Last: decimal; High :decimal; Timestamp :int; }
The code still "compile" but when I run it I have this exception:
Newtonsoft.Json.JsonSerializationException: Unable to find a constructor to use for type MyProject.models+Ticker. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'high', line 1, position 8.
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject(JsonReader reader, JsonObjectContract objectContract, JsonProperty containerMember, JsonProperty containerProperty, String id, Boolean& createdFromNonDefaultCreator)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonSerializer.Deserialize[T](JsonReader reader)
at Flurl.Http.Configuration.NewtonsoftJsonSerializer.Deserialize[T](Stream stream)
at Flurl.Http.HttpResponseMessageExtensions.ReceiveJson[T](Task`1 response)} System.Exception {Newtonsoft.Json.JsonSerializationException}*
This is the full code example, I'm new with F#, maybe needed to explain the problem:
open Flurl.Http
// open mynamespace where is models
type IClient =
abstract member GetAsk : decimal
type MyClient() =
let getTicker () =
let url = "https://www.bitstamp.net/api/v2/ticker/XRPUSD"
url.GetJsonAsync<models.Ticker>()
interface IClient with
member __.GetAsk =
let ticker = getTicker().Result
ticker.Ask
I'm using the Client from a C# project.
I imagine the constructor of Ticker changed because of the visibility.
But, why?
How can I maintain the Ticker class hidden and make the code works as expected?
[Update: use a class]
Using a class
type internal Ticker(ask:decimal, bid :decimal, last: decimal, high :decimal, timestamp :int) =
member this.Ask = ask
does the job as expected.
If internal it is not visible externally but it is "usable" by the JsonDeserializer.
I'm still confused for the behavior when I use a Record.

Related

TypeError while attempting to sort a list of custom objects

I'm new to Dart and tried to get a class to implement List using the answers here, and tried to sort a list of these objects using the docs here. I deleted most of my code in an effort to post a MWE:
import 'dart:collection';
class Transaction<E> extends ListBase<E>{
DateTime when;
Transaction(this.when);
List innerList = new List();
int get length => innerList.length;
void set length(int length){
innerList.length = length;
}
void operator[]=(int index, E value){
innerList[index] = value;
}
E operator [](int index) => innerList[index];
void add(E value) => innerList.add(value);
void addAll(Iterable<E> all) => innerList.addAll(all);
}
class Forecaster{
var transactions;
Forecaster(){
this.transactions = new List<dynamic>();
}
void tabulate(){
transactions.sort((a,b) => a.when.compareTo(b.when)); //problem line?
for(var d in transactions){
d.asMap().forEach((index,content){
int len = content.toStringAsFixed(2).length;
});
}
}
void forecast(var forWhen){
var first = new Transaction(DateTime.now());
first.addAll([5,9]);
transactions.add(first);
}
}
void main(){
Forecaster myTest = new Forecaster();
var dub = myTest;
dub..forecast(DateTime.now())
..tabulate();
}
Running with the problem line results in an exception (Uncaught exception: TypeError: Closure 'Forecaster_tabulate_closure': type '(dynamic, dynamic) => dynamic' is not a subtype of type '(dynamic, dynamic) => int') I don't understand. If I comment out the problem line, the TypeError goes away. Is the TypeError because I did something wrong when defining Transaction? I'm attempting this with DartPad.
I'm new to Dart too, so my explanation might not be 100% on the money, but I believe, yes, the main issue is the type assignment of transactions. I think because you initialize it as a var, it is having trouble deducing the type of a.when, which is means it also doesn't know the type of a.when.compareTo(), and assumes dynamic. You are feeding the output of compareTo into List.sort() which is expecting an int from the anonymous function. Thus the error that it wanted an int but got dynamic.
The easiest way to address this is to initialize transactions with a more explicit type rather than as var:
List<Transaction> transactions;
Forecaster(){
this.transactions = new List<Transaction>();
}
Also, to confirm that it is an issue with it not being able to infer the return type of compareTo, I tried leaving your code as-is, but explicitly casting the result as int, and that also worked:
transactions.sort((a,b){
return (a.when.compareTo(b.when) as int);
});
Note: code like the above and using lots of dynamics and vars is in general not great practice with Dart - you lose a lot of the benefits of having a typed language. You might also notice that when you type in an IDE, you don't get methods auto-suggested when you do stuff like this - for example, until I changed transactions to an explicit type of list, typing a.when did not trigger autocomplete, and my IDE thought the type was dynamic, not DateTime.
Your problem is with the types. The code:
var transactions;
Forecaster(){
this.transactions = new List<dynamic>();
}
void tabulate(){
transactions.sort((a,b) => a.when.compareTo(b.when));
first declares transactions to have type dynamic.
Then you call sort on that with an argument which is inferred to have type dynamic Function(dynamic, dynamic) (because there is no clue available to say otherwise in the type of transactions).
However, the actual run-time type of transactions is List<Transaction>, and that requires a function argument of type int Function(Transaction, Transaction). The type dynamic Function(dynamic, dynamic) is not a sub-type of int Function(Transaction, Transaction) (the return type has to be a subtype of int for that to be the case) so you get a run-time error.
If you change transactions to have type List<Transaction>, then the type inference will have a clue when it gets to the function literal. It will infer that (a, b) => a.when.compareTo(b.when) in a context expecting int Function(Transaction, Transaction) will have that type.
Even if you just change transactions to List<dynamic>, it will still work, it will just make a.when.compareTo(b.when) be dynamic invocations.

F# Referencing Types

I am working on a project where the F# code will be consumed by other .NET projects - so I am using classes. I created a code file like this:
namespace StockApplication
open System
type Stock =
{Symbol: String;
DayOpen: Decimal;
Price: Decimal;
}
member x.GetChange () =
x.Price - x.DayOpen
member x.GetPercentChange() =
Math.Round(x.GetChange()/x.DayOpen,4)
This works fine when I consume it from some unit tests written in C#. For example:
[TestMethod]
public void CreateStock_ReturnsValidInstance()
{
Stock stock = new Stock("TEST", 10, 10.25M);
Assert.IsNotNull(stock);
}
I then went to create another file with another class. This class uses the 1st class so I made sure it was below the original class in VS2012. When I created the next class, I can see it available via intellisense.
namespace StockApplication
open System
type StockTicker() =
member x.GetStock () =
StockApplication.Stock
However, every attept to either new it or refer it gives me the same error:
Error 1 The value, constructor, namespace or type 'Stock' is not
defined
Does anyone have any insight on why I can just simply new up a class that I created in F# in another F# file?
Thanks in advance.
Your C# test having Stock stock = new Stock("TEST", 10, 10.25M); that was compiled without a problem prompts to believe that F# constructor for the Stock should look the same. But this is not true and, perhaps, was the source of your confusion.
Your original
type Stock =
{Symbol: String;
DayOpen: Decimal;
Price: Decimal; }
is of F# type Record indeed, not an ordinary class. The following excerpt from MSDN applies:
Record construction also differs from class construction. In a record type, you cannot define a constructor.
Meaning that
let stock = Stock("ABC"; 10M; 10M)
will produce error FS0039: The value or constructor 'Stock' is not defined while
let stock = { Symbol = "ABC"; DayOpen = 10M; Price = 10M; }
will successfully create a record instance.
In order to build an instance of type Stock in your second F# type StockTicker you should use record construction syntax, something like
member x.GetStock () = { Symbol = "MSFT"; DayOpen = 32M; Price = 32.5M; }
which compiles without any problems.
When it comes to interop use of F# record from C# the latter follows the syntax that you applied in your test method.
OK, after digging into this reference (MSDN was 0 help) here, I found the answer.
Here is the syntax for the Stock class:
namespace StockApplication
open System
type Stock = class
val Symbol: String
val DayOpen: Decimal
val Price: Decimal
new (symbol, dayOpen, price) =
{
Symbol = symbol;
DayOpen = dayOpen;
Price = price
}
member x.GetChange () =
x.Price - x.DayOpen
member x.GetPercentChange() =
Math.Round(x.GetChange()/x.DayOpen,4)
end
And here is the syntax for the consuming class:
namespace StockApplication
type StockTicker() =
member x.GetStock () =
let y = new Stock("AET",1m,1m)
y.DayOpen

MVC Web API errors out with The 'ObjectContent`1' type failed to serialize the response body, Incorrect number of arguments for constructor

The full error is:
{
Message: "An error has occurred.",
ExceptionMessage: "The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'.",
ExceptionType: "System.InvalidOperationException",
StackTrace: null,
InnerException: {
Message: "An error has occurred.",
ExceptionMessage: "Incorrect number of arguments for constructor",
ExceptionType: "System.ArgumentException",
StackTrace: " at System.Linq.Expressions.Expression.ValidateArgumentCount(MethodBase method, ExpressionType nodeKind, Int32 count, ParameterInfo[] pis) at System.Linq.Expressions.Expression.ValidateArgumentTypes(MethodBase method, ExpressionType nodeKind, ReadOnlyCollection`1& arguments) at System.Linq.Expressions.Expression.New(ConstructorInfo constructor, IEnumerable`1 arguments) at System.Data.Objects.ELinq.InitializerMetadata.ProjectionNewMetadata.Emit(Translator translator, List`1 propertyTranslatorResults) at System.Data.Common.Internal.Materialization.Translator.HandleLinqRecord(RecordColumnMap columnMap, InitializerMetadata initializerMetadata) at System.Data.Common.Internal.Materialization.Translator.Visit(RecordColumnMap columnMap, TranslatorArg arg) at System.Data.Query.InternalTrees.RecordColumnMap.Accept[TResultType,TArgType](ColumnMapVisitorWithResults`2 visitor, TArgType arg) at System.Data.Common.Internal.Materialization.Translator.ProcessCollectionColumnMap(CollectionColumnMap columnMap, TranslatorArg arg, ColumnMap discriminatorColumnMap, Object discriminatorValue) at System.Data.Common.Internal.Materialization.Translator.Visit(SimpleCollectionColumnMap columnMap, TranslatorArg arg) at System.Data.Query.InternalTrees.SimpleCollectionColumnMap.Accept[TResultType,TArgType](ColumnMapVisitorWithResults`2 visitor, TArgType arg) at System.Data.Common.Internal.Materialization.Translator.TranslateColumnMap[TRequestedType](QueryCacheManager queryCacheManager, ColumnMap columnMap, MetadataWorkspace workspace, SpanIndex spanIndex, MergeOption mergeOption, Boolean valueLayer) at System.Data.Common.Internal.Materialization.ShaperFactory.TypedShaperFactoryCreator`1.TypedCreate(QueryCacheManager cacheManager, ColumnMap columnMap, MetadataWorkspace metadata, SpanIndex spanInfo, MergeOption mergeOption, Boolean valueLayer) at System.Data.Common.Internal.Materialization.ShaperFactory.Create(Type elementType, QueryCacheManager cacheManager, ColumnMap columnMap, MetadataWorkspace metadata, SpanIndex spanInfo, MergeOption mergeOption, Boolean valueLayer) at System.Data.Objects.Internal.ObjectQueryExecutionPlan.Prepare(ObjectContext context, DbQueryCommandTree tree, Type elementType, MergeOption mergeOption, Span span, ReadOnlyCollection`1 compiledQueryParameters, AliasGenerator aliasGenerator) at System.Data.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption) at System.Data.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption) at System.Data.Objects.ObjectQuery`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator() at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source) at Newtonsoft.Json.Serialization.JsonArrayContract.CreateWrapper(Object list) in c:\Dev\Releases\Working\Newtonsoft.Json\Src\Newtonsoft.Json\Serialization\JsonArrayContract.cs:line 108 at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty) in c:\Dev\Releases\Working\Newtonsoft.Json\Src\Newtonsoft.Json\Serialization\JsonSerializerInternalWriter.cs:line 128 at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) in c:\Dev\Releases\Working\Newtonsoft.Json\Src\Newtonsoft.Json\Serialization\JsonSerializerInternalWriter.cs:line 342 at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty) in c:\Dev\Releases\Working\Newtonsoft.Json\Src\Newtonsoft.Json\Serialization\JsonSerializerInternalWriter.cs:line 123 at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value) in c:\Dev\Releases\Working\Newtonsoft.Json\Src\Newtonsoft.Json\Serialization\JsonSerializerInternalWriter.cs:line 58 at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value) in c:\Dev\Releases\Working\Newtonsoft.Json\Src\Newtonsoft.Json\JsonSerializer.cs:line 608 at System.Net.Http.Formatting.JsonMediaTypeFormatter.<>c__DisplayClassd.<WriteToStreamAsync>b__c() at System.Threading.Tasks.TaskHelpers.RunSynchronously(Action action, CancellationToken token)"
}
}
This seems to be isolated to my computer since our server and other coworkers can run the same code fine.
The only lead I've had is that I just installed Visual Studio 2012 Express side by side with VS 2010 which I'm running it in. Both of them come up with the same error though.
What is more perplexing is that the json.net library is being referenced locally and has not changed.
Only one of the web api controllers throws the error (contacts is an IQueryable that has been filter in numerous ways before this but works in the debugger until the render):
var r =
contacts
.Skip(offset).Take(count)
.Join(
crm.ContactViews,
c => c.Id,
cv => cv.Id,
(c, cv) => new {
contact = c,
view = cv,
userInfo = c.ContactUsers.Select(u => new {
name = u.User.UserName,
id = u.UserId
}).FirstOrDefault(),
lastCalled = crm.CallLogs
.Where(x => x.ContactId == c.Id)
.OrderByDescending(x => x.CallEnd)
.Select(log => new { date = log.CallStart, username = crm.Users.FirstOrDefault(x => x.UserId == log.Caller).UserName })
.FirstOrDefault()
}
)
.AsEnumerable()
.Select(c => new
{
id = c.contact.Id,
firstName = c.contact.FirstName,
lastName = c.contact.LastName,
dateCreated = formatDate(c.contact.DateCreated),
score = c.contact.Score,
companyName = c.view.Company,
phone = c.view.Phone,
email = c.view.Email,
street = c.view.Street,
street2 = c.view.Street2,
city = c.view.City,
state = c.view.State,
zip = c.view.Zip,
assignedUserName = c.userInfo != null ? c.userInfo.name : null,
assignedUserId = c.userInfo != null ? c.userInfo.id : (Guid?)null,
dateLastCalled = c.lastCalled != null ? formatDate(c.lastCalled.date) : null,
lastCalledBy = c.lastCalled != null ? c.lastCalled.username : null
});
Through the normal process of elimination I narrowed the problem down to the join statement using an anonymous object:
var r =
contacts
.Skip(offset).Take(count)
.Join(
crm.ContactViews,
c => c.Id,
cv => cv.Id,
(c, cv) => new ContactJoin(){
contact = c,
view = cv,
test = c.Score // <-- this line
}
)
.AsEnumerable()
.Select(c => new
{
id = c.contact.Id,
firstName = c.contact.FirstName,
lastName = c.contact.LastName,
score = c.test,
});
Removing the test = c.Score line will fix the error. More testing revealed that "Incorrect number of arguments for constructor" error was being thrown from the anonymous object so the workaround is to make the join strongly typed:
.Join(
crm.ContactViews,
c => c.Id,
cv => cv.Id,
(c, cv) => new ContactJoin(){
contact = c,
view = cv,
userInfo = c.ContactUsers.Select(u => new UserInfo(){
name = u.User.UserName,
id = u.UserId
}).FirstOrDefault(),
lastCalled = crm.CallLogs
.Where(x => x.ContactId == c.Id)
.OrderByDescending(x => x.CallEnd)
.Select(log => new LastCall(){
date = log.CallStart,
username = crm.Users.FirstOrDefault(x => x.UserId == log.Caller).UserName
})
.FirstOrDefault()
}
)
I'd still like to find the root of the problem for maintainabilities sake.
After upgrading to .NET 4.5, I found that a projection like this failed due to the order of the properties in the anonymous object.
(t1, t2) => new { Thing1 = t1, Thing2 = t2, Thing1Prop1 = t1.Prop1, Thing2Prop1 = t2.Prop1 };
Switching the order to
(t1, t2) => new { Thing1Prop1 = t1.Prop1, Thing2Prop1 = t2.Prop1, Thing1 = t1, Thing2 = t2 };
fixed this for me, but I haven't yet experimented with other scenarios. This seems a BREAKING, annoying change.
I found out the following:
I had to add this.Configuration.ProxyCreationEnabled = false to a DbContext based class.
Effectively this disables a huge optimization for EF 6.x.
Otherwise, this code would not run and returned the same error you had.
(ApiController based class)
public IHttpActionResult Surveys(int filterscount, int groupscount,
int pagenum, int pagesize, int recordstartindex, int recordendindex)
{
var data = _dbContext.Surveys.OrderByDescending(o => o.ID)
.Skip(pagenum * pagesize)
.Take(pagesize)
.ToList();
return Ok(data);
}

what type does my object query return?

Public Function List_category() As Myobj
Dim query = From subcat In _dataContext.subcategories, _
cat In _dataContext.categories _
Where subcat.CategoryID = cat.CategoryID _
Select New Myobj() With { _
.SubcatId = subcat.SubCategoryID, _
.SubcatName = subcat.SubCategoryName, _
.CatId = cat.CategoryID, _
.CatName = cat.CategoryName _
}
return ?????????
End Function
Public Class Myobj
Private m_SubcatId As Integer
Private m_SubcatName As String
Private m_catId As Integer
Private m_catName As String
Public Property SubcatId() As Integer
Get
Return m_SubcatId
End Get
Private Set(ByVal value As Integer)
m_SubcatId = value
End Set
End Property
Public Property SubcatName() As String
Get
Return m_SubcatName
End Get
Private Set(ByVal value As String)
m_SubcatName = value
End Set
End Property
Public Property CatId() As Integer
Get
Return m_catId
End Get
Private Set(ByVal value As Integer)
m_catId = value
End Set
End Property
Public Property CatName() As String
Get
Return m_catName
End Get
Private Set(ByVal value As String)
m_catName = value
End Set
End Property
End Class
doesnt works!!!!
It Says 'Set' accessor of property 'SubcatName' is not accessible.
You can create a custom type and modify your select to instantiate that for the return. Take a look here: Linq To Sql return from function as IQueryable<T>
Compiler is just telling you that you have declared Private Set on SubcatName and yet ypou are trying to assing a value to it after New Myobj().
For a first run you can declare a POD class (plain old data - just public data, no methods or properties) and once you see it runnung you can go tweaking it, adding methods etc.
If it's really important that all properties are read-only you'll need to try making your querying method a static member of the same class.
Also, there is a way to return anonymous type and cast it back to equivalent anonymous type declared at the receiving side. Got to move on to C# though :-)
Example (read article):
// Method that returns anonymous type as object
object ReturnAnonymous()
{
return new { City="Prague", Name="Tomas" };
}
// Application entry-point
void Main()
{
// Get instance of anonymous type with 'City' and 'Name' properties
object o = ReturnAnonymous();
// This call to 'Cast' method converts first parameter (object) to the
// same type as the type of second parameter - which is in this case
// anonymous type with 'City' and 'Name' properties
var typed = Cast(o, new { City="", Name="" });
Console.WriteLine("Name={0}, City={1}", typed.Name, typed.City);
}
// Cast method - thanks to type inference when calling methods it
// is possible to cast object to type without knowing the type name
T Cast<T>(object obj, T type)
{
return (T)obj;
}

ASP.NET MVC: Cannot convert ObjectQuery -> Linq.IQueryable

i must be doing something wrong or maybe i don't get this. I'm trying to fill 'PropAllMenus' (that has a bunch of properties) with data coming from my entity framework. However, when doing the conversion trough a function i get a 'InvaldCastExeption' in my 'WeekMenuRepository'. Here is the code:
PropAllMenus
Public Class PropAllMenus
Private _MenuID As Integer
Public Property MenuID() As Integer
Get
Return _MenuID
End Get
Set(ByVal value As Integer)
_MenuID = value
End Set
End Property
Private _Name As String
Public Property Name() As String
Get
Return _Name
End Get
Set(ByVal value As String)
_Name = value
End Set
End Property
Private _DaypartID As Integer
Public Property DaypartID() As Integer
Get
Return _DaypartID
End Get
Set(ByVal value As Integer)
_DaypartID = value
End Set
End Property
End Class
WeekMenuRepository
Private _db As New EDMWeekmenuEntities()
Public Function ListAllMenus() As IQueryable(Of PropAllMenus) Implements IWeekMenuRepository.ListAllMenus
Dim result = From p In _db.Menus _
Select p
Return result
End Function
HomeController
Dim DoThings As New WeekMenuRepository()
Function Index() As ActionResult
Return View(DoThings.ListAllMenus().ToList)
End Function
PropAllMenus isn't an entity type, so the EF can't implicitly convert an entity type like Menu to it.
In other words, your function result type is IQueryable(Of PropAllMenus), but your query is returning something like ObjectQuery(Of Menu). If you could change your query to return ObjectQuery(Of PropAllMenus) then the implicit cast (when you call Return) to IQueryable would work.
You need to do something like:
Dim result = From p In _db.Menus _
Select New PropAllMenus With
{
.MenuId = p.MenuId,
.Name = p.Name,
// etc.
}
By the way, I noticed that you have asked 6 questions, but have never accepted any answers, nor uploaded anything. You should press the "up arrow" next to any answers which you find helpful, and press the "checkmark" icon next to the single most helpful answer for a question you've asked. It's a way of saying "thank you."

Resources