Automapper Stackoverflow exception with Entity Framework - domain-driven-design

In our fairly complex DDD setup we have data models and domain models. Technically they are fairly similar with domain models usually having addition properties. We use Automapper to map between data and domain objects. The problem is that in some strange cases, on SaveAsync we are running into a Stackoverflow exception. Here is the code:
1) public virtual async Task SaveAsync(TDomain item)
2) {
3) TData data = null;
4) if (default(TID).Equals(item.ID))
5) {
6) data = mapper.Map<TData>(item);
7)
8) dataContext.Set<TData>().Add(data);
9) }
10) else
11) {
12) data = dataContext.Set<TData>().Single(x => x.ID.Equals(item.ID));
13) if (data == null)
14) throw new Exception($"Unable to find {typeof(TData)} with ID {item.ID} in the database.");
15)
16) mapper.Map(source: item, destination: data);
17) }
18) await dataContext.SaveChangesAsync(userIdentity.ID);
19)
20) // Update the IDs of the item and its children
21) var domainTest = mapper.Map<TDomain>(data);
22) mapper.Map(source: domainTest, destination: item);
23) }
As you can see on line 16, we are mapping to data from item without any problem. We added a test map on line 21 and that maps just fine. On line 22 is where the problem is SOMETIMES. In most cases this saves just fine and maps back to the TDomain item just fine... but when we try to save a change to an existing object that has a child that references the parent object we get this Stackoverflow exception.
I've been researching this for quite a while and I can see 2 options that could work: 1) add a MaxDepth to the config of this map so that this recursion goes only so far, but this feels very hacky and feels like I am masking the problem that could potentially become a maintenance nightmare some time in the future. The other option would be for the SaveAsync to return a new instance of the object (ie, line 21) this certainly feels way better than the MaxDepth way but still feels a bit hacky, like I am avoiding the real problem. Also, any values in the properties that exist only in the domain model would be wiped out this way. This would also be a fairly massive refactor affecting almost 20 apps, each having 5 to 20 saves... not that I'm complaining. Perhaps someone can point out what I can do to actually fix this problem or perhaps what I am missing here.
Just to be really clear, if you remove line 21 and replace line 22 with:
mapper.Map(source: data, destination: item);
You get the same Stackoverflow exception.

For now, I've added a bool is.... = true optional parameter to the save method, and I evaluate line 22 only if this param is true. In those cases where I go into this infinite recursion, I pass a false... I know this is really hacky so I am hoping someone responds with an actual solution... but this lets me keep going in my project

Related

Why would you use the spread operator to spread a variable onto itself?

In the Google Getting started with Node.js tutorial they perform the following operation
data = {...data};
in the code for sending data to Firestore.
You can see it on their Github, line 63.
As far as I can tell this doesn't do anything.
Is there a good reason for doing this?
Is it potentially future proofing, so that if you added your own data you'd be less likely to do something like data = {data, moreData}?
#Manu's answer details what the line of code is doing, but not why it's there.
I don't know exactly why the Google code example uses this approach, but I would guess at the following reason (and would do the same myself in this situation):
Because objects in JavaScript are passed by reference, it becomes necessary to rebuild the 'data' object from it's constituent parts to avoid the original data object being further modified by the ref.set(data) call on line 64 of the example code:
await ref.set(data);
For example, in MongoDB, when you pass an object into a write or update method, Mongo will actually modify the object to add extra properties such as the datetime it was insert into a collection or it's ID within the collection. I don't know for sure if Firestore does the same, but if it doesn't now, it's possible that it may in future. If it does, and if your original code that calls the update method from Google's example code goes on to further manipulate the data object that it originally passed, that object would now have extra properties on it that may cause unexpected problems. Therefore, it's prudent to rebuild the data object from the original object's properties to avoid contamination of the original object elsewhere in code.
I hope that makes sense - the more I think about it, the more I'm convinced that this must be the reason and it's actually a great learning point.
I include the full original function from Google's code here in case others come across this in future, since the code is subject to change (copied from https://github.com/GoogleCloudPlatform/nodejs-getting-started/blob/master/bookshelf/books/firestore.js at the time of writing this answer):
// Creates a new book or updates an existing book with new data.
async function update(id, data) {
let ref;
if (id === null) {
ref = db.collection(collection).doc();
} else {
ref = db.collection(collection).doc(id);
}
data.id = ref.id;
data = {...data};
await ref.set(data);
return data;
}
It's making a shallow copy of data; let's say you have a third-party function that mutates the input:
const foo = input => {
input['changed'] = true;
}
And you need to call it, but don't want to get your object modified, so instead of:
data = {life: 42}
foo(data)
// > data
// { life: 42, changed: true }
You may use the Spread Syntax:
data = {life: 42}
foo({...data})
// > data
// { life: 42 }
Not sure if this is the particular case with Firestone but the thing is: spreading an object you get a shallow copy of that obj.
===
Related: Object copy using Spread operator actually shallow or deep?

Typescript Multi Dimensional Array's Values Not Updating (to null)

What I am Doing
I am trying to create a Sudoku solver and generator in Vue. Right now, I have the solving algorithm set up, and just need to generate new problems. I am generating problems by creating a completed Sudoku problem (complete with no bugs), then I have to remove nodes so that there is still only 1 solution to the problem.
The Problem
When I try to access a node from the multi-dimensional array that represents the board, and change it to null (what I am using to display a blank node), the board does not update that value. I am changing it with the following code: newGrid[pos[0]][pos[1]] = null; (where pos[0] is the row, pos[1] is the column , and newGrid is grid we want to mutate). Note that the array is an array with 9 arrays inside, and each of those arrays has 9 numbers (or null) which represent the values for that position in the grid. To elaborate on the bug, if I put a console.log(newGrid), there are normal looking values, and no null.
What I Know and Have Tried
I know it has to do with this specific line, and the fact that I am setting the value equal to null because changing null to another value (i.e. newGrid[pos[0]][pos[1]] = 0;) works and changes the array. The reason I don't just use a value other than null is: null renders and nothing and other values (0) render as something (null nodes should be blank), null is simple to understand in this situation (the logic is node has null, node has nothing, node is blank), and null is already implemented throughout my codebase.
Additionally, if I use console.log(newGrid[pos[0]][pos[1]]), null (the correct output) is outputted, even though console.log(newGrid) shows a number there, not null. Also, oddly enough, this works for one specific node. In row 1 (indexing starts at 0), column 8, null is set. Even though the input (completed) grid is always different, this node is always set to null. Edit: this bug had to do with the input grid already having null here, so it actually doesn't let any nulls be set.
To summarize: I expect an array with a null in a few positions I update, but I get a number instead. Also, there are no errors when the Typescript compiles to Javascript or during runtime.
Code
Given that I am not exactly sure where the problem may be (i.e. maybe I create the array wrong) I am including the minimum code with a pastebin link to the whole file (this is the full code). To restate, the goal of this function is to remove nodes from the list (by replacing them with null) in order to create a Sudoku puzzle with one solution. The code on Stack Overflow only includes some of the whole file, and the pastebin link includes the rest.
//global.d.ts
type Nullable<T> = T | null;
type Grid = Array<Array<number | null>>;
import { Solver } from './Solve';
// Inside the function that does the main work
const rowLen: number = grid.length;
const colLen: number = grid[0].length;
let newGrid: Grid = grid; // Grid is a argument for this function
let fullNodes = GetFirstFull(grid, colLen, rowLen);
let fullNodesLen: number = fullNodes.length;
// Some stuff that figures out how many solutions there are (we only want 1) is excluded
if (solutions != 1) {
fullNodesLen++;
rounds--;
} else {
newGrid[pos[0]][pos[1]] = null;
}
Note that if anything seems confusing check out the pastebin or ask. Thank you so much for taking the time to look at my problem!
Also, it isn't just 0 that works, undefined also makes it set correctly. So, this problem seems to be something with the null keyword...
EDIT:
Given that no one has responded yet, I assume: my problem is a bit hard, there isn't enough information, my post isn't good quality, or not enough people have seen it. To control the problem of not enough information, I would like to include the function that calls this function (just to see if that might be related).
generate(context: ActionContext<State, any>) {
let emptyArray = new Array(9);
for (let i = 0; i < 9; ++i)
emptyArray[i] = [null, null, null, null, null, null, null, null, null];
const fullGrid = Solver(emptyArray);
const puzzle = fullGrid ? Remover(fullGrid, 6) : state.gridLayout;
context.commit('resetBoard', puzzle);
},
Note: If you aren't familiar with Vuex, what context.commit does is changes the state (except it is changing a global state rather than a component state). Given that this function isn't refactored or very easy to read code in the first place, if you have any questions, please ask.
To solve other potential problems: I have been working on this, I have tried a lot of console.log()ing, changing the reference (newGrid) to a deepcopy, moving stuff out of the if statements, verifying code execution, and changing the way the point on the newGrid is set (i.e. by using newgrid.map() with logic to return that point as null). If you have any questions or I can help at all, please ask.

Array access not allowed on OpenFL movieclips

UDATED
How do I go about this?
I got this from Main.hx:
function onMouseOver(e:MouseEvent){
if(Std.is(e.currentTarget, MovieClip)){
initializer (cast e.currentTarget,["scaleX",1.5,"scaleY",1.5])
}
}
Then this is the pointed function in my Animation Class
//here if i set mc:Dynamic everything goes great! but when this one
function initializer(mc:MovieClip, vars:Array<Dynamic>){
var varsLength:Int = Math.round(vars.length/2);
for(m in 0...varsLength){
ini[m] = mc[vars[2*m]];
}
}
then when i compile it, an error appears:
Error: Array access is not allowed in flash.display.MovieClip
How do I resolve this?
EDIT:
vars: are properties of the MovieClip, for example when I pass these parameters:
initializer (mcClip1,["scaleX",1.5,"scaleY",1.5])
so:
vars = ["scaleX",1.5,"scaleY",1.5]
and:
ini[m] will store "scaleX" and "scaleY"`
X-Ref: https://groups.google.com/forum/#!topic/haxelang/_hkyt__Rrzw
In AS3, you can access fields of an object via their String name using [] (array access). This is called Reflection.
In Haxe, Reflection works differently - you need to make use of the Reflect API.
It's considered bad practice - it's not type-safe, which means the compiler can do very little to help you with error messages, and it's quite slow as well. This is why the usage makes it very explicit that Reflection is actually going on (while in AS3, this fact is somewhat hidden). Consider if there are other ways of solving this problem that don't require Reflection.
Now, to get back to your example, here's what it would look like in Haxe:
function onMouseOver(e:MouseEvent){
if (Std.is(e.currentTarget, MovieClip)) {
initializer(cast e.currentTarget, ["scaleX", 1.5, "scaleY", 1.5])
}
}
function initializer(mc:MovieClip, vars:Array<Dynamic>) {
for (m in 0...Std.int(vars.length / 2)) {
ini[m] = Reflect.getProperty(mc, vars[2*m]);
}
}
Btw, your loop was running for too long since you only use half of the values in the array - if you don't divide it by two like I did, you'll end up with [scaleX, scaleY, null, null] instead of the desired [scaleX, scaleY].

How can I use a string in a table?

I need to use a string value as a table, in order to restore points to a player when they reconnect to a game server.
This string value is their profile ID, which never changes and I need to put data inside the string value (Kills, deaths, head shots) in order to effectively restore these points. I have had a quick look on the internet but I have not found much because I don't know what this specific thing is actually called.
To make it easier, here's what I have so far:
if (not Omega.Playertable) then
Omega.Playertable = {};
System.LogAlways("Set static record table on first connect");
end
local ID = g_gameRules.game:GetProfileId(player.id);
if (not Omega.Playertable.ID) then
table.insert(Omega.Playertable, ID);
Omega.Playertable.g_gameRules.game:GetProfileId(player.id).Kills=0;
Omega.Playertable.g_gameRules.game:GetProfileId(player.id).Deaths=0;
Omega.Playertable.g_gameRules.game:GetProfileId(player.id).Headshots=0;
else
local Kills=Omega.Playertable.g_gameRules.game:GetProfileId(player.id).Kills;
local Deaths=Omega.Playertable.g_gameRules.game:GetProfileId(player.id).Deaths;
local Headshots=Omega.Playertable.g_gameRules.game:GetProfileId(player.id).Headshots;
g_gameRules.game:SetSynchedEntityValue(playerId, 101, Kills);
g_gameRules.game:SetSynchedEntityValue(playerId, 100, Deaths);
g_gameRules.game:SetSynchedEntityValue(playerId, 102, Headshots);
end
As you can see, I've tried adding their ID into the table and adding info based on this. I cannot get the system to read the 'ID' value that I set before, so I tried adding the code that gets the ID instead, and it doesn't work. The ID is unique to each player so I cannot use a simple number system for this.
Could someone point out to me what I have done wrong here? If I manage to fix the problem, I will answer my own question on here so that it can be helpful to other users.
It seems to me that you are using the wrong table indexing syntax.
Indexing a table by a variables value in Lua is done with the [] syntax.
Furthermore, in Lua Foo.bar is syntactic sugar for Foo["bar"] both formats are interchangeable, but the . variant has limitations on which characters you can use with it. For example Foo["\n.*#%!"] is a valid table index, but you certainly can't write this: Foo.\n.*#%!
Also table.insert(t, v) inserts v at the end of the array part of the table. That means if you do this
foo = {};
foo.X = "Some value";
table.insert(foo, "X");
This is what you get
{
X = "Some value"
[1] = "X"
}
That means, if I apply this to the code you gave us, this is what you probably had in mind:
if (not Omega.Playertable) then
Omega.Playertable = {};
System.LogAlways("Set static record table on first connect");
end
local ID = g_gameRules.game:GetProfileId(player.id);
if (not Omega.Playertable[ID]) then
Omega.Playertable[ID] = {};
Omega.Playertable[ID].Kills=0;
Omega.Playertable[ID].Deaths=0;
Omega.Playertable[ID].Headshots=0;
else
local Kills = Omega.Playertable[ID].Kills;
local Deaths = Omega.Playertable[ID].Deaths;
local Headshots = Omega.Playertable[ID].Headshots;
g_gameRules.game:SetSynchedEntityValue(playerId, 101, Kills);
g_gameRules.game:SetSynchedEntityValue(playerId, 100, Deaths);
g_gameRules.game:SetSynchedEntityValue(playerId, 102, Headshots);
end
Try this:
s="35638846.12.34.45"
id,kills,deaths,headshots=s:match("(.-)%.(.-)%.(.-)%.(.-)$")
print(id,kills,deaths,headshots)
But note that these values are strings. If you're using them as numbers, use tonumber to convert them.

How to set timeout for NHibernate LINQ statement

I am using Fluent NHibernate for my ORM. In doing so I am trying to use the NHibernate LINQ syntax to fetch a set of data with the power of LINQ. The code I have works and executes correctly with the exception being that a timeout is thrown if it takes longer than roughly 30 seconds to run. The question I have is how do I extend the default 30 second timeout for LINQ statements via NHibernate?
I have already seen the posts here, here, and here but the first two refer to setting the DataContext's Timeout property, which does not apply here, and the third refers to setting the timeout in XML, which also does not apply because I am using Fluent NHibernate to generate the XML on the fly. Not only that but the post is 2 years old and Fluent NHibernate has changed since.
With the ICriteria objects and even HQL I can specify the timeout, however that is not the goal here. I would like to know how to set that same timeout and use LINQ.
Example code:
using (var session = SessionFactory.OpenSession())
using (var transaction = session.BeginTransaction())
{
var query = (from mem in session.Query<Member>()
select mem);
query = query.Where({where statement});
int start = (currentPage - 1) * max);
if (start > 0)
query = query.Skip(start).Take(max);
else
query = query.Take(max);
var list = query.ToList();
transaction.Commit();
return list;
}
This code (where statement does not matter) works for all purposes except where a timeout occurs.
Any help is appreciated. Thanks in advance!
I ended up setting the command timeout for the Configuration for Fluent NHibernate. The downside to this is that it sets the timeout for ALL of my data access calls and not just the one.
Example code:
.ExposeConfiguration(c => c.SetProperty("command_timeout", (TimeSpan.FromMinutes(10).TotalSeconds).ToString()))
I found this suggestion from this website.
Nhibernate has extended the IQueryable and added a few methods https://github.com/nhibernate/nhibernate-core/blob/master/src/NHibernate/Linq/LinqExtensionMethods.cs
var query = (from c in Session.Query<Puppy>()).Timeout(12);
or
var query = (from c in Session.Query<Puppy>());
query.Timeout(456);
I've just spent fair amount of time fighting with this and hopefully this will save someone else some time.
You should use the .Timeout(120) method call at the very last moment to make sure it is used. TBH I'm not 100% sure on why this is but here are some examples:
WILL WORK
query = query.Where(x => x.Id = 123);
var result = query.Timeout(120).ToList();
DOESN'T WORK
query.Timeout(120);
query = query.Where(x => x.Id = 123);
var result = query.ToList();
If done like the second (DOESN'T WORK) example, it seems to fall back to the default System.Transaction.TransactionManager.DefaultTimeout.
Just in case anyone is still looking for this and finds this old thread too...
Query.Timeout is deprecated.
You should use WithOptions instead:
.WithOptions(o => o.SetTimeout(databaseTimeoutInSeconds))

Resources