Error, Must specify valid information for parsing in the string, when using EF Core Value Conversion - asp.net-core

Based on the EF Core docs (https://learn.microsoft.com/en-us/ef/core/modeling/value-conversions), I'm trying to use EF Core's new Value Conversion on an enumeration. I want to save the enumeration as a string in the SQL Database table.
Here's the entity and enumeration.
public enum InputSetType
{
TypeA, TypeB
}
public class MonthlyInputSet
{
public int Id { get; set; }
public InputSetType Type { get; set; }
}
Here is where I configure the MonthlyInputSet Entity:
public class MonthlyInputSetConfiguration : IEntityTypeConfiguration<MonthlyInputSet>
{
public void Configure(EntityTypeBuilder<MonthlyInputSet> builder)
{
builder.Property(mis => mis.Type).HasConversion(v => v.ToString(), v => (InputSetType)Enum.Parse(typeof(InputSetType), v));
}
}
So, I try to run a basic query to get this data and it fails. The query is:
var saved = await _context.MonthlyInputSets.Include(mis => mis.InsertedBy)
.Include(mis => mis.UpdatedBy)
.Include(mis => mis.MonthlyInputs)
.ThenInclude(mi => mi.EmissionsUnit)
.FirstOrDefaultAsync(mis => mis.Id == id);
But, an error is thrown on the first line of this query that says, "ArgumentException: Must specify valid information for parsing in the string." So my guess is that I have not properly configured the conversion of the string in the table to the enum in C#.
Full raw stack trace:
I verified that the correct string value is being returned from the database. It is not null and it is not a blank string. The string value returned matches a member of the enum perfectly.
public void Configure(EntityTypeBuilder<MonthlyInputSet> builder)
{
builder.Property(mis => mis.Type).HasConversion(
convertToProviderExpression: v => v.ToString(),
convertFromProviderExpression: v => Troubleshooting(v)
);
}
private InputSetType Troubleshooting(string v)
{
return (InputSetType)Enum.Parse(typeof(InputSetType), v);
}
these two images show that the text in every row in the database type column is identical to the 2nd member of the enum that is mapped to this field.

The only way I can reproduce this error is for v to be an empty string, ie "". Not a null.
This :
Enum.Parse(typeof(InputSetType),"xx")
Throws Requested value 'xx' was not found..
While this:
Enum.Parse(typeof(InputSetType),"")
Throws :
Must specify valid information for parsing in the string. (Parameter 'value')
It seems some rows contain an empty string instead of null.
You'll have to decide what to do with those values - are they bad data? Or should the application handle them and replace them with some default value?
If you decide to use a default value you could use String.IsEmptyorString.IsNullOrWhitespace` to handle them, eg:
v=> String.NullOrWhitespace(v)?InputSetType.Unknown:Enum.Parse(typeof(InputSetType),v)

Probably this exception is caused by invalid data in the database - data, that is not present in the enum, null, empty string or so on. This error is common in Enum.Parse() so I think it is related to this method.

Related

Expression Parameter is not displayed in generated Swagger documentation

I have a Controller that has has a parameter of type Expression<Foo, bool> with the name query however that parameter does not show up in the generated swagger.json file. Instead a lot (>1000) parameters that have names like these show up:
Body.CanReduce
ReturnType.IsGenericMethodParameter
Type.IsGenericType
I would like to tell SwaggerGen to have my parameter just shown up as a string. If it is possible using a Filter, that would be my preferred way, but an Attribute would be fine too.
I already tried using an IOperationFilter, but it did not work as operation.Parameters does not even seem to have a paramater with the name query.
private static readonly Type _expressionType = typeof(Expression);
public void Apply(Operation operation, OperationFilterContext context)
{
foreach (var parameter in context.ApiDescription.ActionDescriptor.Parameters)
{
if(_expressionType.IsAssignableFrom(parameter.ParameterType))
{
// The parameter is found ...
var expressionParameter = operation.Parameters.FirstOrDefault(p => p.Name == parameter.Name);
if (expressionParameter != null)
Debugger.Break(); // ... but is not in the operation.Parameters collection although the >1000 mentioned above are.
}
}
}
P.S. to anyone interested: I'm using a custom ModelBinder and System.Linq.Dynamic to parse a query string to an Expression<Foo, bool>

dbcontext.savechanges always saves default Value

I have a SQL-Azure database created with Entity Framework 6.1, Code-First.
The "datetime" field in my 'EmazeEvents' table was created like this:
datetime = c.DateTime(nullable: false, defaultValueSql: "GETUTCDATE()")
and defined like this in the code:
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[Index]
public DateTime datetime { get; set; }
I understand this means that in case this field is omitted in insertion, it will get by default the insertion date, which indeed it does.
However, I am having trouble inserting rows that set this field. Although I set the value of the appropriate variable, it still writes to the database the default date.
Some code extractions:
EmazeEvents is defined like this:
public class EmazeEvents:DbContext
{
public EmazeEvents()
: base("EmazeEvent")
{ }
public DbSet<EmazeEvent> events { get; set; }
}
}
What I do is:
context = new EmazeEvents();
EmazeEvent e = new EmazeEvent();
// e.datetime does get the correct date
e.datetime = DateTime.ParseExact("2014-05-31T00:00:06.8900000", "O", CultureInfo.InvariantCulture);
context.events.Add(e);
context.SaveChanges();
The record written to the database has the current date-time, ignoring the one in e.datetime.
I found out that the problem was with the definition of the 'datetime' field. When I removed the:
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
It started letting me write other values than the default.

Can you use RequestFactory's .with() method with named queries?

I'm trying to make a call to a database using RequestFactory with Hibernate/JPA, and I want to retrieve a list of entities with embedded entities returned as well. I know that the .with() method works for methods like .find(), but it doesn't seem to work with custom queries.
The current way I'm doing it is as follows:
I used a named query in the entity class for the query. (Primary Entity is Name, embedded entity is a Suffix entity called nameSuffix)
#NamedQueries({ #NamedQuery(name = "Query.name", query = "select * from NameTable") })
Then in the service class, the .list() method, which is what I'd like to call with RequestFactory, is as follows.
public List<Name> list() {
return emp.get().createNamedQuery("Query.name").getResultList();
}
Finally, this is how I make the call in my client side code:
NameRequest context = requestFactory.createNameRequest();
context.list().with("nameSuffix").fire(new Receiver<List<NameProxy>>(){
public void onSuccess(List<NameProxy> response) {
String suff = response.get(0).getNameSuffix().getText();
}
});
In the above code, it says that getNameSuffix() returns null, which would imply that .with("nameSuffix") does not work with the .list() call like it does with the standard .find() method.
Is there a way to build a call that would return a list of entities and their embedded entities using .with(), or do I need to do it another way? If I need to do it another way, has anyone figured out a good way of doing it?
I think you misunderstood what the method with() is thought for, unless you had a method getNameSuffix which returns the NameSuffixentity. This is what the documentation says about it:
When querying the server, RequestFactory does not automatically populate relations in the object graph. To do this, use the with() method on a request and specify the related property name as a String
So, what you have to pass to the method is a list of the name of the child entities you want to retrieve. I hope this example could be helpful:
class A {
String getS(){return "s-a"}
B getB(){return new B();}
}
class B {
String getS(){return "s-b";}
C getC(){return new C();}
}
class C {
String getS(){return "s-c";}
}
context.getA().fire(new Receiver<A>(){
public void onSuccess(A response) {
// return 's-a'
response.getS();
// trhows a NPE
response.getB().getS();
}
});
context.getA().with("b").fire(new Receiver<A>(){
public void onSuccess(A response) {
// return 's-a'
response.getS();
// return 's-b'
response.getB().getS();
// trhows a NPE
response.getB().getC().getS();
}
});
context.getA().with("b.c").fire(new Receiver<A>(){
public void onSuccess(A response) {
// return 's-a'
response.getS();
// return 's-b'
response.getB().getS();
// return 's-c'
response.getB().getC().getS();
}
});

Rest Sharp Execute<Int> fails due to cast exception

I have this:
public Int32 NumberOfLocationsForCompany(int companyId)
{
var response = _curl.ResetRequest()
.WithPath(LOCATION_URL)
.AddParam("companyId", companyId.ToString())
.RequestAsGet()
.ProcessRequest<Int32>();
return response;
}
that calls this at the end.
public T ProcessRequest<T>() where T : new()
{
var response = _client.Execute<T>(_request);
if (response.ErrorException != null)
{
throw response.ErrorException;
}
return response.Data;
}
but I get this error. I don't get why it's trying to map an int to a collection or why it's Int64 vs the 32 I specified.: Unable to cast object of type 'System.Int64' to type 'System.Collections.Generic.IDictionary`2[System.String,System.Object]'.
When I hit the api directly this is what I get back
<int xmlns="http://schemas.microsoft.com/2003/10/Serialization/">17</int>
I feel it's something I'm not understanding about Rest Sharp. I tell the execute method to expect an Int, it receives and int, but is trying to map it to a collection. Why and where does the collection come from?
I have noticed that when I look into the base response object's Content the appropriate result "17" is present, why can't Rest Sharp find it? and still where is it finding the Collection?
When looking at the response object I found the return value was in Content vs in Data. I found this to be true whenever I was not returning an object or list of objects.
So now when I'm expecting an int, string, bool, etc I use the following and cast the type of the return value:
public string ProcessRequestWithValue()
{
var response = _client.Execute(_request);
if (response.ErrorException != null)
{
throw response.ErrorException;
}
return response.Content;
}
Hope this helps!

Fluent subclasses - only first and last records are being cast into the correct types

I have a very strange issue that I cannot explain. I have my base mapping with this
//This will automatically cast the row into the correct object type based on the value in AccountType
DiscriminateSubClassesOnColumn<string>("AccountType")
.Formula(String.Format("CASE AccountType WHEN {0} THEN '{1}' WHEN {2} THEN '{3}' ELSE '{4}' END",
(int)PaymentMethodType.CheckingAccount,
typeof(ACH).Name,
(int)PaymentMethodType.SavingsAccount,
typeof(ACH).Name,
typeof(CreditCard).Name));
I have looked in the logs, I have executed the sql that nhibernate is generating, and all records have the same data. There is not difference in them that would denote why this should not work.
The base class is PaymentMethodBase. I have 2 subclasses, CreditCard and ACH, which inherit from PaymentMethodBase.
Then, I have this extension
public static string PaymentMethodName(this PaymentMethodBase paymentMethod)
{
if (paymentMethod is ACH)
{
var ach = (ACH)paymentMethod;
return String.Format("{0} {1}", ach.BankName, String.Format("XXXX{0}", ach.AccountNumber.Substring(ach.AccountNumber.Length - 4)));
}
if (paymentMethod is CreditCard)
{
var creditCard = (CreditCard)paymentMethod;
return String.Format("{0} {1}", creditCard.Name, creditCard.CreditCardNumber);
}
return "Unknown Payment Method";
}
Which I call like this.
public SelectList PaymentMethodsSelectList
{
get
{
var methods = (from p in PaymentMethods
where p != null
select new
{
id = p.PaymentMethodId,
name = p.PaymentMethodName()
}).OrderBy(x => x.name);
var results = methods.ToList();
results.Insert(0, new { id = (int)NewPaymentMethods.ACH, name = "<New eCheck Account...>" });
results.Insert(0, new { id = (int)NewPaymentMethods.CreditCard, name = "<New Credit Card...>" });
return new SelectList(results, "id", "name");
}
}
This code is used by 2 models. The collection of payment methods are all coming from the same object - a customer object. The collection is mapped like this.
HasMany<PaymentMethodBase>(x => x.PaymentMethods)
.KeyColumn("CustomerId")
.Where(y => y.AccountType < 10)
.Inverse()
.Cascade.All();
So, I get the customer 2 different ways. One is that I get the customer through another object (main site). The other has the object being pulled directly by id (in an iframe). The direct by id method works every time. The other method, where I get the customer through another object, causes only the first and last payment method to cast correctly. If there are more than 2, they are left in the base class and appear in the middle of the list after the sort.
I have tried changing the parent object to just mapping the id and then getting the customer record by the ID. Failure. There is something else in the way that is causing this to happen, but only on that one model.
I suspect that the issue here is Fetching issue.
Since the extension method is running on the .Net side rather than in the "NHibernate level" it cannot run properly when the collection of payment methods is not available.
Perhaps in the direct by Id methos you have Fetching set up for the payments whereas in the indirect method the automatic fetching goes only to the Customer object but stops short before fetching the Payment methods.
Try to instruct NHibernate to pre-fetch the Payment methods for you in the indirect method.
Something like:
Session.Query<SomeObject>.Where(.....).Fetch(x => x.Customer).ThenFetch(c => c.PaymentMethods)
The problem here appeared to be the usage of extension methods within a linq statement to return a value based on the type. The issue is that it would work some places but not others, probably due to some fetching issue, as suggested by Variant.
I solved this problem by making a property in my base class like this
public virtual string DisplayName { get { return "Unknown"; } }
Then I overrode the property in my child class and added the logic that was in the extension method for that type.
public override string DisplayName { get { return String.Format("{0} {1}", Name, AccountMask); } }