.NET6/EF: The best overloaded method match for 'Microsoft.EntityFrameworkCore.DbSet<...>.Add(...)' has some invalid arguments - dynamic

I'm working on a generic code to add .NET 6 Entity Framework DbSet<...> records, deserialized from JSON strings. The original code is (much) more elaborated, below are just samples to demonstrated the issue - the following method:
public static void AddRecord(dynamic dbSet, Type entityType, string json)
{
var dataRecord = System.Text.Json.JsonSerializer.Deserialize(json, entityType);
dbSet.Add(dataRecord);
}
results in a run-time error at dbSet.Add(dataRecord) call:
"The best overloaded method match for
'Microsoft.EntityFrameworkCore.DbSet<Northwind.Models.Category>.Add
(Northwind.Models.Category)' has some invalid arguments"}
...
This exception was originally thrown at this call stack:
System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid2<T0, T1>
(System.Runtime.CompilerServices.CallSite, T0, T1)
if you call it, e.g., this way:
using (var ctx = ...)
{
...
var json = ...
...
AddRecord(ctx.Categories, typeof(Category), json);
}
I have intentionally, for clarity, used in the above code concrete dbSet (ctx.Categories) and compile time typedef (typeof(Category)) - in actual code these are run-time defined variables.
If you "unroll" the method code and write it this way:
using (var ctx = ...)
{
...
var json = ...
...
var dataRecord = System.Text.Json.JsonSerializer.Deserialize(json, typeof(Category));
ctx.Categories.Add(dataRecord);
}
you would still get the mentioned above run-time error for the .Add method.
But if you write:
var dataRecord = System.Text.Json.JsonSerializer.Deserialize<Category>(json);
ctx.Categories.Add(dataRecord);
or
var dataRecord = System.Text.Json.JsonSerializer.Deserialize(json);
ctx.Categories.Add((Category)dataRecord);
the code will work without any issues.
Finally, an attemp to use Convert.ChangeType doesn't help:
var dataRecord = System.Text.Json.JsonSerializer.Deserialize(json);
ctx.Categories.Add(Convert.ChangeType(dataRecord, typeof(Category)));
So, it looks like an explicit object type casting is compiled to and makes on rum-time some "special object interfaces arrangements", which dynamic object type casting doesn't?
[Update]
Okan Karadag's prompt answer below gave me a hint how to change AddRecord(...) method to workaround the subject issue:
public static void AddRecord(DbContext dbContext, Type entityType, string json)
{
var dataRecord = System.Text.Json.JsonSerializer.Deserialize(json, entityType);
dbContext.Add(dataRecord);
}
This method works flawlessly. Although it doesn't answer the subject question, why the original AddRecord(...) method
public static void AddRecord(dynamic dbSet, Type entityType, string json)
{
var dataRecord = System.Text.Json.JsonSerializer.Deserialize(json, entityType);
dbSet.Add(dataRecord);
}
results in 'The best overloaded method match for 'Microsoft.EntityFrameworkCore.DbSet<...>.Add(...)' has some invalid arguments...' runtime error at
dbSet.Add(dataRecord);
code line.

You can use generic for dynamic.
public void AddEntity<T>(string json) where T:class
{
var entity = JsonSerializer.Deserialize<T>(json);
ArgumentNullException.ThrowIfNull(entity);
dbContext.Add<T>(entity);
dbContext.SaveChanges();
}
You can see link for problem error.

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>

Passing a JSON object to worklight java adapter

I would like to pass a complete JSON object to a java adapter in worklight. This adapter will call multiple other remote resources to fulfill the request. I would like to pass the json structure instead of listing out all of the parameters for a number of reasons. Invoking the worklight procedure works well. I pass the following as the parameter:
{ "parm1": 1, "parm2" : "hello" }
Which the tool is fine with. When it calls my java code, I see a object type of JSObjectConverter$1 being passed. In java debug, I can see the values in the object, but I do not see any documentation on how to do this. If memory serves me, the $1 says that it is an anonymous inner class that is being passed. Is there a better way to pass a json object/structure in adapters?
Lets assume you have this in adapter code
function test(){
var jsonObject = { "param1": 1, "param2" : "hello" };
var param2value = com.mycode.MyClass.parseJsonObject(jsonObject);
return {
result: param2value
};
}
Doesn't really matter where you're getting jsonObject from, it may come as a param from client. Worklight uses Rhino JS engine, therefore com.mycode.MyClass.parseJsonObject() function will get jsonObject as a org.mozilla.javascript.NativeObject. You can easily get obj properties like this
package com.mycode;
import org.mozilla.javascript.NativeObject;
public class MyClass {
public static String parseJsonObject(NativeObject obj){
String param2 = (String) NativeObject.getProperty(obj, "param2");
return param2;
}
}
To better explain what I'm doing here, I wanted to be able to pass a javascript object into an adapter and have it return an updated javascript object. Looks like there are two ways. The first it what I answered above a few days ago with serializing and unserializing the javascript object. The other is using the ScriptableObject class. What I wanted in the end was to use the adapter framework as described to pass in the javascript object. In doing so, this is what the Adapter JS-impl code looks like:
function add2(a) {
return {
result: com.ibm.us.roberso.Calculator.add2(a)
};
The javascript code in the client application calling the above adapter. Note that I have a function to test passing the javascript object as a parameter to the adapter framework. See the invocationData.parameters below:
function runAdapterCode2() {
// x+y=z
var jsonObject = { "x": 1, "y" : 2, "z" : "?" };
var invocationData = {
adapter : "CalculatorAdapter",
procedure : 'add2',
parameters : [jsonObject]
};
var options = {
onSuccess : success2,
onFailure : failure,
invocationContext : { 'action' : 'add2 test' }
};
WL.Client.invokeProcedure(invocationData, options);
}
In runAdapterCode2(), the javascript object is passed as you would pass any parameter into the adapter. When worklight tries to execute the java method it will look for a method signature of either taking an Object or ScriptableObject (not a NativeObject). I used the java reflection api to verify the class and hierarchy being passed in. Using the static methods on ScriptableObject you can query and modify the value in the object. At the end of the method, you can have it return a Scriptable object. Doing this will give you a javascript object back in the invocationResults.result field. Below is the java code supporting this. Please note that a good chunk of the code is there as part of the investigation on what object type is really being passed. At the bottom of the method are the few lines really needed to work with the javascript object.
#SuppressWarnings({ "unused", "rawtypes" })
public static ScriptableObject add2(ScriptableObject obj) {
// code to determine object class being passed in and its heirarchy
String result = "";
Class objClass = obj.getClass();
result = "objClass = " + objClass.getName() + "\r\n";
result += "implements=";
Class[] interfaces = objClass.getInterfaces();
for (Class classInterface : interfaces) {
result += " " + classInterface.getName() ;
}
result += "\r\nsuperclasses=";
Class superClass = objClass.getSuperclass();
while(superClass != null) {
result += " " + superClass.getName();
superClass = superClass.getSuperclass();
}
// actual code working with the javascript object
String a = (String) ScriptableObject.getProperty((ScriptableObject)obj, "z");
ScriptableObject.putProperty((ScriptableObject)obj, "z", new Long(3));
return obj;
}
Note that for javascript object, a numeric value is a Long and not int. Strings are still Strings.
Summary
There are two ways to pass in a javascript object that I've found so far.
Convert to a string in javascript, pass string to java, and have it reconstitute into a JSONObject.
Pass the javascript object and use the ScriptableObject classes to manipulate on the java side.

The underlying connection was closed error while using .Include on EF objects

Following line of code gives me an error saying "The underlying connection was closed".
return this.repository.GetQuery<Countries>().Include(g => g.Cities).AsEnumerable().ToList();
But if I remove .Include(g => g.cities) it works fine.
this code is written in one of the operation in my WCF service, and I try to test it using WCF test client. I tried by calling this operation from MVC application also, and the same issue was occurring there too.
Also, i am using generic repository with entity framework
Repository code (only few important extract)
Constructor:
public GenericRepository(DbContext objectContext)
{
if (objectContext == null)
throw new ArgumentNullException("objectContext");
this._dbContext = objectContext;
this._dbContext.Configuration.LazyLoadingEnabled = false;
this._dbContext.Configuration.ProxyCreationEnabled = false;
}
GetQuery method:
public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class
{
var entityName = GetEntityName<TEntity>();
return ((IObjectContextAdapter)DbContext).ObjectContext.CreateQuery<TEntity>(entityName);
}
Attempt#1
Created following overloads in repository code:
public IQueryable<TEntity> GetQuery<TEntity>(params string[] includes) where TEntity : class
{
var entityName = GetEntityName<TEntity>();
IQueryable<TEntity> query = ((IObjectContextAdapter)DbContext).ObjectContext.CreateQuery<TEntity>(entityName);
foreach(string include in includes)
{
query = query.Include(include);
}
return query;
}
public IQueryable<TEntity> GetQuery<TEntity>(Expression<Func<TEntity, bool>> predicate, params string[] includes) where TEntity : class
{
return GetQuery<TEntity>(includes).Where(predicate);
}
WCF is now trying to execute following line of code:
return this.repository.GetQuery<Countries>("Cities").AsEnumerable().ToList()
But it still gives the same error of "The underlying connection was closed". I tested it in WCF test client. However, when I debug the repository code it shows the navigation object getting included in result, but the issue seems occurring while trying to pass the output to client (WCF test client, or any other client)
After looking at the code you've now posted, I can conclude that, indeed, your DbContext is being closed at the end of the GetQuery method, and is thus failing when you try to use include. What you might want to do to solve it is to have an optional params variable for the GetQuery method that will take in some properties to be included, and just do the include right in the GetQuery method itself.

BuildCommands argument in Rob Conery's Massive

I am using Rob Conery's Massive.
The method List<DbCommand> BuildCommands(params object[] things), according to the methods comments, is supposed to take objects that "can be POCOs, Anonymous, NameValueCollections, or Expandos". But this:
var x = new { Id = new Guid("0F66CDCF-C219-4510-B81A-674CE126DD8C"), Name = "x", DisplayName = "y" };
myTable.BuildCommands(x);
Results in an InvalidCastException. Which reasonable since in the Massive.cs a cast from the passed in anonymous type to an ExpandoObject is attempted.
Why does the comment state you can pass in anything? Is there some other way to build commands from non-ExpandoObjects?
Here's some more code:
public static void ThisFails()
{
DynamicModel myTable = new DynamicModel("myConnectionString", tableName: "dbo.MyTable", primaryKeyField: "Id");
var updateMe = new { Id = new Guid("DF9A2F1B-3556-4EAC-BF2B-40E6821F3394"), Name = "abcx", DisplayName = "x" };
var commands = myTable.BuildCommands(updateMe); // This fails
myTable.Execute(commands);
}
public static void ThisSucceeds()
{
DynamicModel myTable = new DynamicModel("myConnectionString", tableName: "dbo.MyTable", primaryKeyField: "Id");
dynamic updateMe = new ExpandoObject();
updateMe.Id = new Guid("DF9A2F1B-3556-4EAC-BF2B-40E6821F3394");
updateMe.Name = "abcx";
updateMe.DisplayName = "x";
var commands = myTable.BuildCommands(updateMe);
myTable.Execute(commands);
}
The code that fails results in:
Unable to cast object of type
'<>f__AnonymousType03[System.Guid,System.String,System.String]' to
type <br/>
'System.Collections.Generic.IDictionary2[System.String,System.Object]'.
It's thrown from the first line in your method
public virtual DbCommand CreateUpdateCommand(dynamic expando, object key)
{
var settings = (IDictionary<string, object>)expando;
...
To me it looks like there should be a call to your extension method ToExpando before CreateUpdateCommand is called?
I think this is why people make methods private and public :). You're not supposed to call BuildCommands directly (though the code you have here still should work). I have a feeling there might be a bug that was committed in a patch.
That said - I believe this will work if you call myTable.Update() or myTable.Insert().
This last part answers the question - in terms of a possible "issue" - let's take that to Github.

Can I stop my WCF generating ArrayOfString instead of string[] or List<string>

I am having a minor problem with WCF service proxies where the message contains List<string> as a parameter.
I am using the 'Add Service reference' in Visual Studio to generate a reference to my service.
// portion of my web service message
public List<SubscribeInfo> Subscribe { get; set; }
public List<string> Unsubscribe { get; set; }
These are the generated properties on my MsgIn for one of my web methods.
You can see it used ArrayOfString when I am using List<string>, and the other takes List<SubscribeInfo> - which matches my original C# object above.
[System.Runtime.Serialization.DataMemberAttribute(EmitDefaultValue=false)]
public System.Collections.Generic.List<DataAccess.MailingListWSReference.SubscribeInfo> Subscribe {
get {
return this.SubscribeField;
}
set {
if ((object.ReferenceEquals(this.SubscribeField, value) != true)) {
this.SubscribeField = value;
this.RaisePropertyChanged("Subscribe");
}
}
}
[System.Runtime.Serialization.DataMemberAttribute(EmitDefaultValue=false)]
publicDataAccess.MailingListWSReference.ArrayOfString Unsubscribe {
get {
return this.UnsubscribeField;
}
set {
if ((object.ReferenceEquals(this.UnsubscribeField, value) != true)) {
this.UnsubscribeField = value;
this.RaisePropertyChanged("Unsubscribe");
}
}
}
The ArrayOfString class generated looks like this. This is a class generated in my code - its not a .NET class. It actually generated me a class that inherits from List, but didn't have the 'decency' to create me any constructors.
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
[System.Runtime.Serialization.CollectionDataContractAttribute(Name="ArrayOfString", Namespace="http://www.example.com/", ItemName="string")]
[System.SerializableAttribute()]
public class ArrayOfString : System.Collections.Generic.List<string> {
}
The problem is that I often create my message like this :
client.UpdateMailingList(new UpdateMailingListMsgIn()
{
Email = model.Email,
Name = model.Name,
Source = Request.Url.ToString(),
Subscribe = subscribeTo.ToList(),
Unsubscribe = unsubscribeFrom.ToList()
});
I really like the clean look this gives me.
Now for the actual problem :
I cant assign a List<string> to the Unsubscribe property which is an ArrayOfString - even though it inherits from List. In fact I cant seem to find ANY way to assign it without extra statements.
I've tried the following :
new ArrayOfString(unsubscribeFrom.ToList()) - this constructor doesn't exist :-(
changing the type of the array used by the code generator - doesn't work - it always gives me ArrayOfString (!?)
try to cast List<string> to ArrayOfString - fails with 'unable to cast', even though it compiles just fine
create new ArrayOfString() and then AddRange(unsubscribeFrom.ToList()) - works, but I cant do it all in one statement
create a conversion function ToArrayOfString(List<string>), which works but isn't as clean as I want.
Its only doing this for string, which is annoying.
Am i missing something? Is there a way to tell it not to generate ArrayOfString - or some other trick to assign it ?
Any .NET object that implements a method named "Add" can be initialized just like arrays or dictionaries.
As ArrayOfString does implement an "Add" method, you can initialize it like this:
var a = new ArrayOfString { "string one", "string two" };
But, if you really want to initialize it based on another collection, you can write a extension method for that:
public static class U
{
public static T To<T>(this IEnumerable<string> strings)
where T : IList<string>, new()
{
var newList = new T();
foreach (var s in strings)
newList.Add(s);
return newList;
}
}
Usage:
client.UpdateMailingList(new UpdateMailingListMsgIn()
{
Email = model.Email,
Name = model.Name,
Source = Request.Url.ToString(),
Subscribe = subscribeTo.ToList(),
Unsubscribe = unsubscribeFrom.To<ArrayOfString>()
});
I prefer not to return generic types across a service boundary in the first place. Instead return Unsubscribe as a string[], and SubscriptionInfo as SubscriptionInfo[]. If necessary, an array can easily be converted to a generic list on the client, as follows:
Unsubscribe = new List<string>(unsubscribeFrom);
Subscribe = new List<SubscriptionInfo>(subscribeTo);
Too late but can help people in the future...
Use the svcutil and explicitly inform the command line util that you want the proxy class to be serialized by the XmlSerializer and not the DataContractSerializer (default). Here's the sample:
svcutil /out:c:\Path\Proxy.cs /config:c:\Path\Proxy.config /async /serializer:XmlSerializer /namespace:*,YourNamespace http://www.domain.com/service/serviceURL.asmx
Note that the web service is an ASP.NET web service ok?!
If you are using VS 2008 to consume service then there is an easy solution.
Click on the "Advanced..." button on the proxy dialog that is displayed when you add a Service Reference. In the Collection Type drop down you can select System.Generic.List. The methods returning List should now work properly.
(Hope this is what you were asking for, I'm a little tired and the question was a tad difficult for me to read.)