I'm using a DB intercept in entity framework 6 (code first model) in order to implement a soft delete function, however intermittently I'm getting a exception thrown from SQL Server stating "Some part of your SQL statement is nested too deeply. Rewrite the query or break it up into smaller queries."
This is an example of the sql being generated, as you can see the SQL generated is pretty outrageous:
https://gist.github.com/junderhill/87caceac728809a8ca837b9d8b5189f3
The code for my EF Intercept is as follows:
public class SoftDeleteInterceptor : IDbCommandTreeInterceptor
{
public const string IsDeletedColumnName = "IsDeleted";
public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext)
{
if (interceptionContext.OriginalResult.DataSpace != DataSpace.SSpace)
{
return;
}
var queryCommand = interceptionContext.Result as DbQueryCommandTree;
if (queryCommand != null)
{
interceptionContext.Result = HandleQueryCommand(queryCommand);
}
}
private static DbCommandTree HandleQueryCommand(DbQueryCommandTree queryCommand)
{
var newQuery = queryCommand.Query.Accept(new SoftDeleteQueryVisitor());
return new DbQueryCommandTree(
queryCommand.MetadataWorkspace,
queryCommand.DataSpace,
newQuery);
}
public class SoftDeleteQueryVisitor : DefaultExpressionVisitor
{
public override DbExpression Visit(DbScanExpression expression)
{
var table = (EntityType)expression.Target.ElementType;
if (table.Properties.All(p => p.Name != IsDeletedColumnName))
{
return base.Visit(expression);
}
var binding = expression.Bind();
return binding.Filter(
binding.VariableType
.Variable(binding.VariableName)
.Property(IsDeletedColumnName)
.NotEqual(DbExpression.FromBoolean(true)));
}
}
Update:
This did work correctly. However, something was causing significant slow-downs on our servers over time. Every time we would reset the IIS app pool, it would be fast, but it appears EF is somehow caching or leaking memory and slowing it down. We pulled the interceptors out temporarily.
We hit this issue at work, and after a serious amount of time, here's a solution (has yet to be extensively tested; I'll update this with any updates if needed):
To start at the root, in SoftDeleteQueryVisitor, if you need to add the filter, you return return binding.Filter(...), which modifies the query. This in turn triggers TreeCreated. This walks the tree, finds the DbScanExpression again, and re-adds the filter. The obvious solution is to not add the filter if it already has it, then.
Looking at the structure of the tree, we find that the filter is a parent node of the scan:
+ queryCommand {DbQueryCommandTree
|_Parameters
| |_p__linq__0 : Edm.Guid
|_Query : Collection{Record['IDGUID'=Edm.Guid, 'Name'=Edm.String, 'FooField'=Edm.String, 'BarField'=Edm.Boolean, 'Group'=Edm.String, 'IsDeleted'=Edm.Boolean]}
|_Project
|_Input : 'Limit1'
| |_Limit
| |_Filter
| | |_Input : 'Extent1'
| | | |_Filter
| | | |_Input : 'Var_8'
| | | | |_Scan : CodeFirstDatabase.LeaderboardSuite
| | | |_Predicate
| | | |_
| | | |_Var(Var_8).IsDeleted
| | | |_<>
| | | |_True
| | |_Predicate
| | |_
| | |_Var(Extent1).IDGUID
| | |_=
| | |_#p__linq__0
| |_2
|_Projection
|_NewInstance : Record['IDGUID'=Edm.Guid, 'Name'=Edm.String, 'FooField'=Edm.String, 'BarField'=Edm.Boolean, 'Group'=Edm.String, 'IsDeleted'=Edm.Boolean]
|_Column : 'IDGUID'
| |_Var(Limit1).IDGUID
|_Column : 'Name'
| |_Var(Limit1).Name
|_Column : 'FooField'
| |_Var(Limit1).FooField
|_Column : 'BarField'
| |_Var(Limit1).BarField
|_Column : 'Group'
| |_Var(Limit1).Group
|_Column : 'IsDeleted'
|_Var(Limit1).IsDeleted} System.Data.Entity.Core.Common.CommandTrees.DbQueryCommandTree
However, once we realize we own both the visitor and the visitor's lifetime, we realize we can store state:
var newQuery = queryCommand.Query.Accept(new SoftDeleteQueryVisitor());
We also know that the Filter node will be hit before the Scan node because it is a parent of the Scan node. At this point, we have to consider two possibilities: There is no where clause on the query, and there is a where clause on the query.
At this point, I just walked the tree, determined if the query predicate used the IsDeleted field in an Equals or NotEquals clause, and didn't add the node there if so. I didn't bother checking the Or or Xor clauses because these are incredibly unlikely cases. They are easy to add, though, if you find you need them.
public class SoftDeleteQueryVisitor : DefaultExpressionVisitor
{
private readonly List<DbScanExpression> _scanExpressions = new List<DbScanExpression>();
public override DbExpression Visit(DbScanExpression expression)
{
var table = (EntityType)expression.Target.ElementType;
if (table.Properties.All(p => p.Name != InterceptorConstants.IsDeletedColumnName))
{
return base.Visit(expression);
}
if (_scanExpressions.Contains(expression))
{
return base.Visit(expression);
}
var binding = expression.Bind();
return binding.Filter(
binding.VariableType
.Variable(binding.VariableName)
.Property(InterceptorConstants.IsDeletedColumnName)
.NotEqual(DbExpression.FromBoolean(true)));
}
private bool HasField(DbScanExpression expression)
{
var table = (EntityType)expression.Target.ElementType;
return table.Properties.Any(p => p.Name == InterceptorConstants.IsDeletedColumnName);
}
private bool HasExpression(DbExpression predicate)
{
if (predicate is DbAndExpression andExpression)
{
return HasExpression(andExpression.Left) || HasExpression(andExpression.Right);
}
if (predicate is DbComparisonExpression comparisonExpression &&
(comparisonExpression.ExpressionKind == DbExpressionKind.NotEquals ||
comparisonExpression.ExpressionKind == DbExpressionKind.Equals))
{
if (comparisonExpression.Right is DbPropertyExpression rightPropertyExpression &&
rightPropertyExpression.Property.Name == InterceptorConstants.IsDeletedColumnName ||
comparisonExpression.Left is DbPropertyExpression leftPropertyExpression &&
leftPropertyExpression.Property.Name == InterceptorConstants.IsDeletedColumnName)
{
return true;
}
}
return false;
}
public override DbExpression Visit(DbFilterExpression expression)
{
if (expression.Input.Expression is DbScanExpression scanExpression)
{
if (HasField(scanExpression))
{
if (HasExpression(expression.Predicate))
{
_scanExpressions.Add(scanExpression);
}
}
}
return base.Visit(expression);
}
}
Related
j
X:/rstfrontend/packages/src/constants.js:4
1 | // #flow
2 |
3 | declare var SC_DISABLE_SPEEDY: ?boolean;
> 4 | declare var __VERSION__: string;
5 |
6 | export const SC_ATTR: string =
7 | (typeof process !== 'undefined' && (process.env.REACT_APP_SC_ATTR || process.env.SC_ATTR)) ||
Another problem is that it refers to a path that doesn't exist.
When clicked on View Compiled it links to main.chunk.
Maybe a bug with webpack?
j
http://localhost:3000/static/js/vendors~main.chunk.js:187248:30
187245 | n[r - 1] = arguments[r];
187246 | }
187247 |
> 187248 | throw false ? undefined : new Error(D.apply(void 0, [R[e]].concat(n)).trim());
| ^ 187249 | }
187250 |
187251 | var T = function () {
I have been creating an application with Symfony 4.2, I want to save all logs in database, I'm using MonologBundle.
Monolog.yml
monolog:
handlers:
main:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
channels: ["!event"]
console:
type: console
process_psr_3_messages: false
channels: ["!event", "!doctrine", "!console"]
I want to store data in database like this
How Can I do this with Symfony.
You can achieve that by creating a custom monolog channel (e.g. doctrine_channel) and handler (e.g. doctrine) to go with it. Update the example below as per your needs.
Monolog configuration
monolog:
channels: [doctrine_channel]
handlers:
main:
...
channels: [... !doctrine_channel]
console:
...
channels: [... !doctrine_channel]
doctrine:
type: service
channels: [doctrine_channel]
id: app.logger.doctrine_handler
Service configuration
services:
app.logger.doctrine_handler:
class: App\Logger\DoctrineHandler
arguments:
- "#doctrine.orm.entity_manager"
DoctrineHandler
namespace App\Logger;
use App\Entity\Log;
use Doctrine\ORM\EntityManagerInterface;
use Monolog\Handler\AbstractProcessingHandler;
class DoctrineHandler extends AbstractProcessingHandler
{
private $initialized;
private $entityManager;
private $channel = 'doctrine_channel';
public function __construct(EntityManagerInterface $entityManager)
{
parent::__construct();
$this->entityManager = $entityManager;
}
protected function write(array $record)
{
if (!$this->initialized) {
$this->initialize();
}
if ($this->channel != $record['channel']) {
return;
}
$log = new Log();
$log->setMessage($record['message']);
$log->setLevel($record['level_name']);
$this->entityManager->persist($log);
$this->entityManager->flush();
}
private function initialize()
{
$this->initialized = true;
}
}
Result
mysql> SELECT * FROM log;
+----+----------+-----------+---------------------+
| id | message | level | created_at |
+----+----------+-----------+---------------------+
| 1 | Welcome! | INFO | 2019-02-07 19:00:00 |
| 2 | Go back! | WARNING | 2019-02-07 19:00:05 |
| 3 | Help! | EMERGENCY | 2019-02-07 19:00:10 |
+----+----------+-----------+---------------------+
3 rows in set (0.00 sec)
Then inject #monolog.logger.doctrine_channel (type hinted LoggerInterface) to your service or wherever you want to log something. This should work! Now, it is up to you to refactor/enhance as you wish.
String title
String parent
id | title | parent |
1 | Clothes | 0 |
2 | Men | 1 |
3 | Shoes | 2 |
4 | Adidas | 3 |
how can i recursive loop in this situation
i need this
clothes
-men
--shoes
---Adidas
----may be too long
Implement a taglib method that takes care of the complex logic and outputs some (preferably simple) HTML that is reusable.
First let's define a simple Node for demo purposes:
class Node {
String title
List<Node> children
public boolean hasChildren() {
return children as Boolean
}
}
Define the whole "product tree":
def rootNodeDemo = new Node(title: 'clothes', children: [
new Node(title:'men', children: [
new Node(title: 'shoes', children: [
new Node(title: 'adidas')
])
]),
new Node(title: 'women', children: [
new Node(title: 'shoes', children: [
new Node(title: 'adidas')
])
])
])
Here's the taglib:
class NavigationTaglib {
static namespace = "navigation"
/**
* #param rootNode - the root node from where to start rendering.
*/
def productCategoryTree = { attrs, body ->
def rootNode = attrs?.remove('rootNode') // this is the clothes -root node or maybe even the root of everything (which probably needs to be omitted in HTML-rendering)
if(!rootNode) {
return
}
out << renderTreeHtml(rootNode).toString()
}
private StringBuilder renderTreeHtml(Node rootNode) {
StringBuilder treeHtml = new StringBuilder()
treeHtml << """<ul class="navi node-level-1">"""
treeHtml << renderTreeHtmlItems(rootNode, 2)
treeHtml << "</ul>"
}
private StringBuilder renderTreeHtmlItems(Node node, int level) {
StringBuilder treeHtml = new StringBuilder()
treeHtml << """<li>${node.title?.encodeAsHTML()}"""
if(node.hasChildren()) {
treeHtml << """<ul class="node-level-${level}">"""
node.children.each { Node child ->
treeHtml << renderTreeHtmlItems(child, level+1)
}
treeHtml << "</ul>"
}
treeHtml << "</li>"
}
}
In GSP (we assume that model has an entry named rootNodeDemo, which has the contents of rootNodeDemo defined earlier):
<navigation:productCategoryTree rootNode="${rootNodeDemo}" />
This will produce the following HTML, sorry, no pretty prints like new lines or tabs :(
<ul class="navi node-level-1"><li>clothes<ul class="node-level-2"><li>men<ul class="node-level-3"><li>shoes<ul class="node-level-4"><li>adidas</li></ul></li></ul></li><li>women<ul class="node-level-3"><li>shoes<ul class="node-level-4"><li>adidas</li></ul></li></ul></li></ul></li></ul>
Which, when rendered in a web browser, looks like something that's in the following JS-fiddle: http://jsfiddle.net/bq4wkyc3/
I have to display a TreeView in a WinForm, based on an complex Dictionary and I'm looking for the 'shortest' method to do it. I think it can be done in one LINQ query but I don't know how and I'm not even sure if it is possible.
Here an example of the entry dictionary :
Dictionary<String, String> dict = new Dictionary<String, String>()
{
{"aaa.bbb.ccc.ddd", "value1"},
{"aaa.bbb.ccc.eee", "value2"},
{"aaa.bbb.fff.ggg", "value3"},
{"aaa.hhh.iii.jjj", "value4"},
{"bbb.ddd", "value5"},
{"ccc", "value6"}
};
And I want to get back a TreeView like this :
|--+ aaa
| |--+ bbb
| | |--+ ccc
| | | |--- ddd = value1
| | | |--- eee = value2
| | |
| | |--+ fff
| | | |--- ggg = value3
| |
| |--+ hhh
| | |--+ iii
| | | |--- jjj = value4
|
|--+ bbb
| |--- ddd = value5
|
|--+ ccc = value6
And here what I have got now (I don't handle the value yet) :
List<String> list = new List<string>() {
"aaa.bbb.ccc.ddd",
"aaa.bbb.ccc.eee",
"aaa.bbb.fff.ddd",
"aaa.bbb.fff.ggg",
"aaa.ggg.fff.hhh",
"hhh.iii.jjj.kkk"
};
Action<TreeNode, String> traverse = null;
traverse = (node, chaine) =>
{
String[] tab = chaine.Split(new char[] { '.' }, 2);
TreeNode child = null;
if (node.Nodes.ContainsKey(tab[0]))
{
child = node.Nodes[tab[0]];
}
else
{
child = node.Nodes.Add(tab[0]); // + ((tab.Length > 1) ? " - " + tab[1] : ""));
child.Name = tab[0];
}
if (tab.Length > 1 && !String.IsNullOrEmpty(tab[1]))
traverse(child, tab[1]);
};
TreeNode test = this.treeView1.Nodes.Add("test");
list.ForEach(x => traverse(test, x));
I hope I'm clear enough in my explanation.
There is a fair bit of logic going on in your Action, so I doubt it can be done within a single LINQ query so that it would look something like
var query = from this in that
where this is t
select this
But what you could do is rewrite it a little bit, something like:
public void AnotherWay()
{
TreeNode parent = this.treeView1.Nodes.Add("test");
List<String> list = new List<String>()
{
"aaa.bbb.ccc.ddd",
"aaa.bbb.ccc.eee",
"aaa.bbb.fff.ddd",
"aaa.bbb.fff.ggg",
"aaa.ggg.fff.hhh",
"hhh.iii.jjj.kkk"
};
list.ForEach(x =>
{
TreeNode root = parent;
TreeNode child = null;
x.Split(new[] { '.' })
.ToList()
.ForEach(i =>
{
child = root.Nodes.ContainsKey(i) ?
root.Nodes[i] :
root.Nodes.Add(i);
child.Name = i;
root = child;
});
});
}
This code does exactly what you already posted, but is just smaller and IMO reads slightly clearer.
Generally I see an Action or Func that performs a fair bit of logic to be a code smell, especially if it is reused several times (in which case it should be extracted out into it's own method, but I digress).
In your case though it appears that the Action is only being used the once in the line list.ForEach(x => traverse(test, x)); and so the functionality can simply replace your call to the Action, as in my example above.
(However, if the complexity of the logic or the LOC increased then in this example I would be tempted to move the functionality out into it's own method for maintainability).
This approach also allows you to very easily cater for your second requirement of handling dictionary's with little modification, such as:
public void DictFunc()
{
TreeNode parent = this.treeView1.Nodes.Add("test");
Dictionary<String, String> dict = new Dictionary<String, String>()
{
{ "aaa.bbb.ccc.ddd", "Value1" },
{ "aaa.bbb.ccc.eee", "Value2" },
{ "aaa.bbb.fff.ddd", "Value3" },
{ "aaa.bbb.fff.ggg", "Value4" },
{ "aaa.ggg.fff.hhh", "Value5" },
{ "hhh.iii.jjj.kkk", "Value6" }
};
dict.ToList().ForEach(x =>
{
// For brevity, same as logic in example above.
// Plus the small amount (~three LOC) of additional logic
// required to handle the values.
});
}
I've left the dictionary value handling as an exercise for you, but the rest of the logic inside the ForEach is identical to that in my first example.
I am not sure what I am doing wrong, but my dataGrid won't populate.
It took me a bleeping hour to debug linq, but I figured out that it was indeed working...
foreach (XElement elm in xDocument.Element("ArrayOfPopulation").Elements("Population")) {
Console.Write(elm.Element("id").Value+ " | ");
Console.Write(elm.Element("name").Value+ " | ");
Console.WriteLine(elm.Element("description").Value);
}
The above code shows that I am infact getting values out of linq...
3 | CHF | FR Congestive Heart Failure
2 | COPD | FR Chronic Obstructive Pulmonary Disease
But down below my model is null? I can't figure out what it could be? It is not like I am getting null values. I don't think it has to do with wpf data binding, but I am stumped on what it could be.
try {
XElement elem = xDocument.Element("ArrayOfPopulation");
popModel =
xDocument
.Element("ArrayOfPopulation")
.Elements("Population")
.Select(template => new PopulationModel {
populationID = template.Element("id").Value,
PopName = template.Element("name").Value,
description = template.Element("description").Value,
populationType = template.Element("owner").Element("type").Value,
isActive = Boolean.Parse(template.Element("isActive").Value)
})as ObservableCollection<PopulationModel>;
}
I don't think you can cast to observable collection... You need to pass your collection to the constructor of ObservableCollection
try {
XElement elem = xDocument.Element("ArrayOfPopulation");
popModel =
xDocument
.Element("ArrayOfPopulation")
.Elements("Population")
.Select(template => new PopulationModel {
populationID = template.Element("id").Value,
PopName = template.Element("name").Value,
description = template.Element("description").Value,
populationType = template.Element("owner").Element("type").Value,
isActive = Boolean.Parse(template.Element("isActive").Value)
});
var popModelCollection = new ObservableCollection<PopulationModel>(popModel);
}