BuildCommands argument in Rob Conery's Massive - orm

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.

Related

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

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.

Mockito using spy object running actual code hence giving error

I have this bunch of code. First is a test class method testEndPoint(), I have also included the class to be tested EndPointClass.
When I run this testmethod, it tries to run the actual send http Call method instead of mocking or a dummy call, hence it gives a 500 not found error.
I know somewhere the code needs to be corrected not sure where.
Here's the code
public void testEndPoint(){
OutputObject output = new OutputObject();
EndPointClass epClass = new EndPointClass();
EndPointClass epClassSpy = Mockito.spy(epClass);
List<JacksonJsonProvider> providers = new ArrayList<JacksonJsonProvider>();
providers.add(mockCustomJacksonProvider);
WebClient client = WebClient.create("http://example.org/home",providers);
WebClientWrapper webClientWrapper = new WebClientWrapper(client);
WebClientWrapper spyWebClient = Mockito.spy(webClientWrapper);
Mockito.when(spyWebClient.invoke(Mockito.any(String.class),Mockito.any(Object.class),Mockito.eq(OutputObject.class))).thenReturn(output);
Mockito.when(epClassSpy.webCall(spyWebClient)).thenReturn(output);
OutputObject response = epClassSpy.sendRequest("ABC", "ABCToken");
}
public class EndPointClass{
public OutputObject sendRequest(String input, String authToken){
List<JacksonJsonProvider> providers = new ArrayList<JacksonJsonProvider>();
providers.add(downloadsJacksonProvider);
WebClient client = WebClient.create(olsDownloadUrl+path, providers);
if (null == timeOut) {
timeOut = 60000;
}
HTTPConduit http = (HTTPConduit) WebClient.getConfig(client).getConduit();
HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
httpClientPolicy.setConnectionTimeout(0);
httpClientPolicy.setReceiveTimeout(timeOut);
http.setClient(httpClientPolicy);
client.type("application/json");
client.accept("application/json");
client.header("x-auth-Token", authToken);
client.query("input", input);
OutputObject output = null;
WebClientWrapper clientWrapper = new WebClientWrapper(client);
output = webCall(clientWrapper);
return output;
}
public OutputObject webCall(WebClientWrapper clientWrapper) {
return clientWrapper.invoke(HttpMethod.GET, null, OutputObject.class);
}
}
From the official documentation
Important gotcha on spying real objects!
Sometimes it's impossible or impractical to use when(Object) for stubbing spies. Therefore when using spies please consider doReturn|Answer|Throw() family of methods for stubbing. Example:
List list = new LinkedList();
List spy = spy(list);
//Impossible: real method is called so spy.get(0) throws ndexOutOfBoundsException (the list is yet empty)
when(spy.get(0)).thenReturn("foo");
//You have to use doReturn() for stubbing
doReturn("foo").when(spy).get(0);
So just change your Mockito.when(...) to Mockito.doReturn(...) that should do the trick.

How to inject parameter with EF6 / Code First?

I'm trying to figure out how to inject a parameter into Entity Framework 6 when using MapToStoredProcedures. Is this even possible?
I just want to pass my currently logged in username from the application to the stored procedure, but I can't seem to figure out WHERE EF6 does the actual call.
EDIT : A bit more information
Ok, so WITHOUT MapToStoredProcedures (aka letting EF6 just use tables directly) I can do the following in my overridden SaveChangesAsync method:
For Each Entry As DbEntityEntry In Me.ChangeTracker.Entries().Where(Function(o) o.State = EntityState.Deleted)
If TypeOf Entry.Entity Is ISoftDelete Then
'Implements Soft Delete interface, so let's do what needs doing.
Select Case Entry.Entity.GetType()
Case GetType(OS)
Dim _thisOS As OS = TryCast(Entry.Entity, OS)
Using db As New AppRegistrationContext
_thisOS = Await db.OSSet.Include("OSType").FirstOrDefaultAsync(Function(o) o.ID = _thisOS.ID)
End Using
If Not _thisOS Is Nothing Then
Try
Entry.Reference("OSType").CurrentValue = _thisOS.OSType
Catch ex As Exception
Debug.Print(ex.ToString)
End Try
End If
Case GetType(Server)
Case Else
'Do nothing - only filling in extra information for those that we need to
End Select
'Set the archival bits
Entry.Property("Archive").CurrentValue = True
Entry.Property("ArchiveDate").CurrentValue = Date.Now
Entry.Property("ArchiveBy").CurrentValue = HttpContext.Current.User.Identity.Name.ToString()
'Mark it modified
Entry.State = EntityState.Modified
End If
Next
Return Await MyBase.SaveChangesAsync()
Alright, that works great with direct-table manipulation on EF's behalf.
What I want to do instead, is handle all of this in stored procedures - but I need to pass HttpContext.Current.User.Identity.Name.ToString() WITH my delete stored procedure to set the ArchiveBy parameter.
Hopefully this better illustrates what I am attempting to do.
Life could not be easier for you. Run something like the following:
In your repository add the following:
public void ExecuteSqlCommand(string sql, params object[] parameters)
{
DbContext.Database.ExecuteSqlCommand(sql, parameters);
}
and use it just like the following:
public void DoSomething(int officeId)
{
var sqlParam = new SqlParameter("p0", officeId);
var parameters = new object[] { sqlParam };
((GenericRepository)Repository).ExecuteSqlCommand("EXEC dbo.myProc #p0", parameters);
}
Or simply just call
DbContext.Database.ExecuteSqlCommand
as I showed above based on your needs.
Update 1 : You want a stored procedure to take care of the CRUD business :
Suppose your context is called : MyDbContext
Then declare something like the following in a partial MyDbContext class:
public partial class MyDbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder
.Entity<SomeCustomEntity>()
.MapToStoredProcedures(agent =>
{
agent.Insert(i => i.HasName("spr_MyInsert"));
agent.Update(u => u.HasName("spr_MyUpdate"));
agent.Delete(d => d.HasName("spr_MyDelete"));
});
}
}
Now every time you want to do some CRUD procedure you operation will be running through the stored procedure [The one you have mapped] and you don't need to worry about passing anything to the stored procedure :
using (var context = new MyDbContext())
{
context.SomeCustomEntity.Add(new SomeCustomEntity
{
Name = "Jack Something",
Phone = "999"
});
context.SaveChanges();
}

My Nintex Custom Action not reading in Workflow Variable in SingleLineInput

I've written a custom workflow action that takes in several values, mostly using the SingleLineInput control.
When I assign literal values, I have no issues, but when I try to assign a Workflow Variable, I don't get the actual value of the variable, I get the literal text - something like {WorkflowVariable:XmlValue} - assuming my variable was names XmlValue.
I'm not sure what I could possibly be doing wrong. Any ideas?
Here's code snippets:
The javascript for retrieving the value from the SingleLineInput
function TPAWriteConfig() {
configXml.selectSingleNode("/NWActionConfig/Parameters/Parameter[#Name='FieldValue']/PrimitiveValue/#Value").text = getRTEValue('<%=fieldValue.ClientID%>');
SaveErrorHandlingSection();
return true;
}
The server control:
<Nintex:ConfigurationProperty ID="ConfigurationProperty3" runat="server" FieldTitle="Field Value" RequiredField="True">
<TemplateControlArea>
<Nintex:SingleLineInput runat="server" id="fieldValue"></Nintex:SingleLineInput>
</TemplateControlArea>
</Nintex:ConfigurationProperty>
From my adapter class:
private const string FieldValueProperty = "FieldValue";
NWActionConfig config = new NWActionConfig(this);
config.Parameters[2] = new ActivityParameter();
config.Parameters[2].Name = FieldValueProperty;
config.Parameters[2].PrimitiveValue = new PrimitiveValue();
config.Parameters[2].PrimitiveValue.Value = string.Empty;
config.Parameters[2].PrimitiveValue.ValueType = SPFieldType.Text.ToString();
From the activity class:
public static DependencyProperty FieldValueProperty = DependencyProperty.Register("FieldValue", typeof (string),
typeof (
WriteOnePdfFieldActivity));
public string FieldValue
{
get { return (string) GetValue(FieldValueProperty); }
set { SetValue(FieldValueProperty, value); }
}
I feel a little silly answering my own question, but for the sake of anyone else having the same issues. Here's how it works:
If you're putting a literal value in the field, just use the value
If you're using any other kind of assignment, do a lookup based on the value.
The code below demonstrates:
var fieldValue = FieldValue.StartsWith("{") ? ctx.AddContextDataToString(FieldValue, true) : FieldValue;
This extract the value from the workflow context. Hope this helps.

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.)