Replacing several nodes in the same tree, using SyntaxNode.ReplaceNode - c#

I'm currently working on three-way merging on syntax trees using Roslyn. I have a matching between all children on a a ClassDeclerationSyntax node, and want to perform a merge on the children, and then create a new tree based on that merge.
O is the input ClassDeclerationSyntax, and matching has three members (A, O, B) of the type MemberDeclerationSyntax.
var updated = O;
foreach (var m in matching)
{
if (m.A != null && m.B != null && m.O != null) {
var merge = Merge(m.A, m.O, m.B);
var oldUpdated = updated;
updated = updated.ReplaceNode(m.O, merge);
}
else if (m.A == null && m.O == null && m.B != null)
updated = updated.AddMembers(m.B);
else if (m.A != null && m.O == null && m.B == null)
updated = updated.AddMembers(m.A);
}
This does not work. In the second iteration ReplaceNode returns a completely unmodified node (oldUpdated == updated is true).
It seems that after the first iteration of the loop, all children have been reconstructed as new objects, and the original children-objects stored in my matching can no longer be found in the children list (updated.ChildNodes().Where(x => x == m.O) is empty).
What would a good way be to do this?

My current approach:
var updateMember = new Dictionary<MemberDeclarationSyntax, MemberDeclarationSyntax>();
var addMembers = new List<MemberDeclarationSyntax>();
foreach (var m in matching) {
if (m.A != null && m.B != null && m.O != null) {
var mergeChild = Merge(m.A, m.B, M.O);
updateMember.Add(m.O, child);
}
else if (m.A == null && m.O == null && m.B != null)
addMembers.Add(m.B);
else if (m.A != null && m.O == null && m.B == null)
addMembers.Add(m.A);
}
var merged = O.ReplaceNodes(updateMember.Keys.AsEnumerable(), (n1, n2) =>
{
return updateMember[n1];
}).AddMembers(addMembers.ToArray());

Related

Return the same type for two views from datacontext

I have created two views that return exactly the same columns from the same tables. The only difference between the two views is they filter on different parameters. I have added these into my .dbml file
(Picture of views in dbml) This has then auto generated two classes for these two views.
In my code depending on the value of the property Filter one of the two views is queried, either current or previous. I need these views to be returned as the same type. So that IOrderedQueryable<> items has one return type.
Currently they are returning as either clientOrdersQueryCurrent or clientOrdersQueryPrevious. If I set IOrderedQueryable<> items to either one of these and attempt to cast the other type then this causes a runtime error.
IOrderedQueryable<> items;
bool filterQuery = false;
if (!string.IsNullOrEmpty(OrderNumber) || DateFrom != null || Dateto != null || !string.IsNullOrEmpty(TrackingNumber) || UserId != null)
{
filterQuery = true;
}
if (Filter == "Current")
{
if (filterQuery)
{
items = dataContext.clientOrdersQueryCurrents.Where(o => o.client_id == currentClientIdProvider.GetCurrentClientId()
&& (string.IsNullOrEmpty(OrderNumber) || o.OrderNumber.Contains(OrderNumber))
&& (DateFrom == null || o.OrderPlaced >= DateFrom)
&& (Dateto == null || o.OrderPlaced <= Dateto)
&& (string.IsNullOrEmpty(TrackingNumber) || o.TrackingReference.Contains(TrackingNumber))
&& (UserId == null || o.UserId == UserId)).OrderByDescending(o => o.OrderPlaced);
}
else
{
items = dataContext.clientOrdersQueryCurrents.Where(o => o.client_id == currentClientIdProvider.GetCurrentClientId()).OrderByDescending(o => o.OrderPlaced);
}
}
else if (Filter == "Previous")
{
if (filterQuery)
{
items = dataContext.clientOrdersQueryPrevious.Where(o => o.client_id == currentClientIdProvider.GetCurrentClientId()
&& (string.IsNullOrEmpty(OrderNumber) || o.OrderNumber.Contains(OrderNumber))
&& (DateFrom == null || o.OrderPlaced >= DateFrom)
&& (Dateto == null || o.OrderPlaced <= Dateto)
&& (string.IsNullOrEmpty(TrackingNumber) || o.TrackingReference.Contains(TrackingNumber))
&& (UserId == null || o.UserId == UserId)).OrderByDescending(o => o.OrderPlaced);
}
else
{
items = dataContext.clientOrdersQueryPrevious.Where(o => o.client_id == currentClientIdProvider.GetCurrentClientId()).OrderByDescending(o => o.OrderPlaced);
}
}
else
{
//Default call - current orders
items = dataContext.clientOrdersQueryCurrents.Where(o => o.client_id == currentClientIdProvider.GetCurrentClientId()).OrderByDescending(o => o.OrderPlaced);
}
The only thing I can currently think of to resolve this is to create a class and have the query map the result to the class after it returns.
What is the best way to do this?
The ORM I am currently using is NHibernate.
Better to map to some common class
IOrderedQueryable<CommonItem> items;
items = dataContext.clientOrdersQueryCurrents.Select(e => new CommonItem{...});
...
items = dataContext.clientOrdersQueryPrevious.Select(e => new CommonItem{...});
It can be AutoMapper's ProjectTo method
items = dataContext.clientOrdersQueryPrevious.ProjectTo<CommonItem>();

LINQ using Contains with entity

Am using ASP.NET MVC 5 to create an Application for billing, now i have thing function which receive a filter object with different variables, am having a problem with contains when i search, what am i doing wrong
public static List<Quote> getCustomerQuotes(QuoteFilter filter)
{
using (var db = new AppDBContext())
{
var q = db.Quotes.Where(u => u.entryDate > 0); ;
if (filter.type != null)
{
q = q.Where(u => u.quoteType == filter.type);
}
if (filter.only_permitable != null)
{
q = q.Where(u => !Values.NON_PERMITABLE_QUOTES.Contains(u.quoteType));
}
if (filter.quote_status != null)
q = q.Where(u => u.quote_status == (int)filter.quote_status);
if (filter.quotenumber != null)
{
q = q.Where(u => u.quote_number.Contains(filter.quotenumber));
}
if (filter.permitnumber != null)
q = q.Where(u => u.permit_number.Contains(filter.permitnumber));
if (filter.permit_status != null)
q = q.Where(u => u.permit_status == (int)filter.permit_status);
if (filter.quoteId != null)
q = q.Where(u => u.Id == (int)filter.quoteId);
if (filter.customer_id != null)
q = q.Where(u => u.customer_id == (int)filter.customer_id);
q = q.OrderByDescending(u => u.Id);
FileLogger.Log("getCustomerQuotes", q.ToString());
return q.ToList();
}
}
When i call the function and pass quotenumber, the contains doesnt search, it returns nothing
You have to evaluate your expression, before you apply the OrderByDescending.
q = q.Where(u => u.quote_number.Contains(filter.quotenumber)).ToList();
This should be happen also to the rest places.
Is quote number alpha-numeric? If yes, as Contains is case sensitive can you try comparison by first turning source and target to same case ? like
q = q.Where(u => u.quote_number.ToLower().Contains(filter.quotenumber.ToLower()));
Cheers
Ok, am answering my own question after finding a solution or i may call it a hack
public static List<Quote> getCustomerQuotes(QuoteFilter filter)
{
using (var db = new AppDBContext())
{
var q = db.Quotes.Where(u =>
(filter.type != null ? u.quoteType == filter.type : u.quoteType > 0) &&
(filter.only_permitable != null ? !Values.NON_PERMITABLE_QUOTES.Contains(u.quoteType) : u.permitType > 0) &&
(filter.quote_status != null ? u.quote_status == filter.quote_status : u.quote_status > -100) &&
(!string.IsNullOrEmpty(filter.quotenumber) ? u.quote_number.Contains(filter.quotenumber) || u.groupName.Contains(filter.quotenumber) : u.quoteType > 0) &&
(!string.IsNullOrEmpty(filter.permitnumber) ? u.permit_number.Contains(filter.permitnumber) || u.groupName.Contains(filter.permitnumber) : u.quoteType > 0) &&
(filter.permit_status != null ? u.permit_status == filter.permit_status : u.quoteType > 0) &&
(filter.quoteId != null ? u.Id == filter.quoteId : u.Id > 0) &&
(filter.customer_id != null ? u.customer_id == filter.customer_id : u.customer_id > -1)
).OrderByDescending(u => u.Id);
//FileLogger.Log("getCustomerQuotes", q.ToString());
return q.ToList();
}
}
i dont know why it didn't work the first time but now it works.

Finding and comparing two objects, while avoiding NullReferenceException

I am trying to compare an original object to an updated one to find if they are different from each other. The compare logic should be as follows:
If neither object a nor b contains an object in SomeList, which has EType equal to EnumType.FooType, they should be evaluated as equals.
If only a xor b contains an object in SomeList, which has EType equal to EnumType.FooType, they should be evaluated as different.
If both objects contain an object as mentioned above, and the property Number is equal on both objects, then objects a and b should be evaluated as equals.
The following code solves the task, however it is long and bulky, so I ask if it can be shortened and made 'prettier'?
var a = original.SomeList.FirstOrDefault(p => p != null && p.EType == EnumType.FooEnum);
var b = updated.SomeList.FirstOrDefault(p => p != null && p.EType == EnumType.FooEnum);
var bEqual = false;
if (a == null && b == null)
bEqual = true;
else if (a != null && b != null)
bEqual = a.Number == b.Number;
return (a == null) ? (b == null) :
(b != null && a.Number == b.Number);
A better solution is to create a full set of equality functions and operators. This means implementing IEquatable and override Object.Equals(object). A total of four small functions would enable you to write:
return a == b;
MSDN has a good article about overriding equality functions.
How to: Define Value Equality for a Type (C# Programming Guide)
In C# 6.0 you can use the null propagation operator:
var a = original.SomeList.FirstOrDefault(p => p != null && p.EType == EnumType.FooEnum);
var b = updated.SomeList.FirstOrDefault(p => p != null && p.EType == EnumType.FooEnum);
return a?.Number == b?.Number;
Well straight off the bat you can do this
var a = original.SomeList.FirstOrDefault(p => p != null && p.EType == EnumType.FooEnum);
var b = updated.SomeList.FirstOrDefault(p => p != null && p.EType == EnumType.FooEnum);
var bEqual = false;
if (a == null && b == null)
bEqual = true;
else
bEqual = a.Number == b.Number;
You are already checking if they are null so if they are null then you are setting your bEqual to true; and if they aren't null then you can just perform the normal code below.
Now without testing your code I would think you could do something like this
if(original.SomeList.FirstOrDefault(p => p != null && p.EType == EnumType.FooEnum) != null && updated.SomeList.FirstOrDefault(p => p != null && p.EType == EnumType.FooEnum) != null)
bEqual = original.Number == updated.Number;
Something along those lines should be sufficient if I was at home I would test it for you and make sure but it should be close enough.
You can reduce it with conditional operator :
var a = original.SomeList.FirstOrDefault(p => p != null && p.EType == EnumType.FooEnum);
var b = updated.SomeList.FirstOrDefault(p => p != null && p.EType == EnumType.FooEnum);
bool bEqual = (a == null && b == null)? true :
(a != null && b != null)? a.Number == b.Number : false;

Change condition in LINQ by another condition

I have these two LINQ lines which are only different in one condition.
node.Image.Tag == null and node.Image.Tag != null
if (treeSelectedNode.Image.Tag == null)
{
radNode = tree.Find(node => node.Level == 0 && node.Image.Tag == null
&& node.Text.Equals(treeSelectedNode.Text));
}
else
{
radNode = tree.Find(node => node.Level == 0 && node.Image.Tag != null
&& node.Text.Equals(treeSelectedNode.Text));
}
Is there any way to create the condition before the LINQ line and then use it, so that I can remove the extra line?
I know I can do something like this:
radNode = treeSelectedNode.Image.Tag == null ? tree.Find(node => node.Level == 0
&& node.Image.Tag == null && node.Text.Equals(treeSelectedNode.Text)) :
tree.Find(node => node.Level == 0 && node.Image.Tag != null
&& node.Text.Equals(treeSelectedNode.Text));
But it's just not what I want.
Check for the the outcome of condition node.Image.Tag == null being the same as the outcome of treeSelected.Image.Tag == null:
radNode = tree.Find(node => node.Level == 0 && ((node.Image.Tag == null) == (treeSelectedNode.Image.Tag == null))
&& node.Text.Equals(treeSelectedNode.Text))
Update
Addressing #KhanTo's performance concern, in part:
Boolean selectedImgTagIsNull = treeSelected.Image.Tag == null;
radNode = tree.Find(node => node.Level == 0 && ((node.Image.Tag == null) == selectedImgTagIsNull)
&& node.Text.Equals(treeSelectedNode.Text))
However, I suspect that JIT optimization would have a high likelihood of resulting in the same thing even for my original code.
Maybe that oneliner will be ok for you?
radNode = tree.Find(node => node.Level == 0
&& ((treeSelectedNode.Image.Tag == nulL
&& node.Image.Tag == null)
|| (treeSelectedNode.Image.Tag != nulL
&& node.Image.Tag != null))
&& node.Text.Equals(treeSelectedNode.Text));
Technically you can build that kind of query using Expression, it need extra works, here is a simple example:
private static Expression<Func<T, bool>> AndCombined<T>(Expression<Func<T, bool>> exp1, Expression<Func<T, bool>> exp2)
{
ParameterExpression p = exp1.Parameters.Single();
return Expression.Lambda<Func<T, bool>>(Expression.And(exp1.Body, Expression.Invoke(exp2, p)), exp1.Parameters.Single());
}
static void Main(string[] args)
{
var b = new List<int>() { 30, 15, 5 };
Expression<Func<int, bool>> test1 = f => f > 10;
Expression<Func<int, bool>> test2 = f => f < 20;
var combinedAndQuery = AndCombined(test1, test2);
var reuslt1 = b.Find(new Predicate<int>(combinedAndQuery.Compile()));
Expression<Func<int, bool>> test3 = f => f < 40;
var combinedAndQuery2 = AndCombined(test1, test3);
var reuslt2 = b.Find(new Predicate<int>(combinedAndQuery2.Compile()));
}

Query Collection using composite key where key parts can match 'any'

Is there an appropriate collection or algorithm that would allow me to get a value using a composite key where when querying parts of the key could be null to mean match any value?
For example, if I have the class:
class Key
{
string p1{ get; }
string p2{ get; }
string p3{ get; }
public Key(string p1, string p2 , string p3)
{ this.p1 = p1; this.p2 = p2; this.p3=p3; }
}
If I then created three keys e.g.
new Key( "a","b","c")
new Key( "d","b","c")
new Key( "e","f","c")
I would like a collection or algorithm with out iterating to allow for the following key
new Key( null, "b","c") to return the values mapped to the first two keys,
new key( null,null,"c") to return the values mapped to all of the keys.
Is there any way to do this?
Probably this would do for lookup by any combination of three key components. Note that key for pair lookup (A+B) is created by simple concat for simplicity. Real key should be Tuple.
var keys = new[] { new Key("a", "b", c"), ... };
class Map
{
// ... skip members declaration here
public Map(IEnumerable<Keys> keys)
{
all = keys;
mapA = keys.ToLookup(k => k.A);
mapB = keys.ToLookup(k => k.B);
mapC = keys.ToLookup(k => k.C);
// should be keys.ToLookup(k => Tuple.Create(k.A, k.B))
mapAB = keys.ToLookup(k => k.A + k.B);
mapAC = keys.ToLookup(k => k.A + k.C);
mapBC = keys.ToLookup(k => k.B + k.C);
mapABC = keys.ToLookup(k => k.A + k.B + k.C);
}
public IEnumerable<Key> Find(Key k)
{
if(k.A == null && k.B == null && k.C == null) return all;
if(k.A != null && k.B == null && k.C == null) return mapA[k.A];
if(k.A == null && k.B != null && k.C == null) return mapB[k.B];
if(k.A == null && k.B == null && k.C != null) return mapC[k.C];
if(k.A != null && k.B != null && k.C == null) return mapAB[k.A+k.B];
if(k.A != null && k.B == null && k.C != null) return mapAC[k.A+k.C];
if(k.A == null && k.B != null && k.C != null) return mapBC[k.B+k.C];
return mapABC[k.A+k.B+k.C];
}
}

Categories

Resources