Determine is a FamilyInstance is visible in a View - c#

I have a FamilyInstance pFam and a Autodesk.Revit.DB.View pView. I want to know if pFam is visible in pView. I've tried using
if (pFam.IsHidden(pView)
continue;
Unfortunately, this only tells me if the element is supposed to be hidden, which isn't the case. But, the element is not visible in every View, and under that circumstance, I want (or rather don't want) something to happen. There is no Visible or IsVisible properties for FamilyInstances... Does anyone know a way to handle these situations?
Thanks!

I have found that the most reliable way of knowing whether an element is visible in a view is to use a FilteredElementCollector specific to that view. There are so many different ways of controlling the visibility of an element that it would be impractical to try to determine this any other way.
Below is the utility function I use to achieve this. Note this works for any element, and not just for family instances.
public static bool IsElementVisibleInView([NotNull] this View view, [NotNull] Element el)
{
if (view == null)
{
throw new ArgumentNullException(nameof(view));
}
if (el == null)
{
throw new ArgumentNullException(nameof(el));
}
// Obtain the element's document
Document doc = el.Document;
ElementId elId = el.Id;
// Create a FilterRule that searches for an element matching the given Id
FilterRule idRule = ParameterFilterRuleFactory.CreateEqualsRule(new ElementId(BuiltInParameter.ID_PARAM), elId);
var idFilter = new ElementParameterFilter(idRule);
// Use an ElementCategoryFilter to speed up the search, as ElementParameterFilter is a slow filter
Category cat = el.Category;
var catFilter = new ElementCategoryFilter(cat.Id);
// Use the constructor of FilteredElementCollector that accepts a view id as a parameter to only search that view
// Also use the WhereElementIsNotElementType filter to eliminate element types
FilteredElementCollector collector =
new FilteredElementCollector(doc, view.Id).WhereElementIsNotElementType().WherePasses(catFilter).WherePasses(idFilter);
// If the collector contains any items, then we know that the element is visible in the given view
return collector.Any();
}
The category filter is used to eliminate any element not of the desired category before using the slower parameter filter to find the desired element. It is probably possible to speed this up further with clever usage of filters, but I have found that it is plenty fast enough for me in practice.
If you don't have ReSharper, delete the [NotNull] annotations you see.

Related

Get symbol info from new node

I have a CSharpSyntaxRewriter that overrides VisitMemberAccessExpression, inside that method, I am calling MemberAccessExpressionSyntax.WithName(), but the node it returns has a different SyntaxTree compared to the original node, this is a problem since it means an error is thrown when calling SemanticModel.GetSymbolInfo(node).
Is there a way to change the Name of a MemberAccessExpressionSyntax but still have a SyntaxTree that works with SemanticModel.GetSymbolInfo(node)?
My code is:
public sealed override SyntaxNode VisitNode(SyntaxNode node)
{
var nodeSyntax = (MemberAccessExpressionSyntax) node;
if (nodeSyntax.Name.ToString() == OldModifier && !HasSymbol(nodeSyntax, out _))
{
if (ModifierType == ModifierType.Damage)
nodeSyntax = nodeSyntax.WithName(IdentifierName($"GetDamage({NewModifier})"));
else
nodeSyntax = nodeSyntax.WithName(IdentifierName($"GetCritChance({NewModifier})"));
nodeSyntax = nodeSyntax.WithLeadingTrivia(node.GetLeadingTrivia()).WithTrailingTrivia(node.GetTrailingTrivia());
}
return nodeSyntax;
}
(VisitNode is called from VisitMemberAccessExpression)
And here are images showing the difference in the SyntaxTree:
original:
After calling WithName:
There's a few ways to approach something like this:
Change Your Visit Method
Can you restructure your code to call VisitNode before you've done any changes to VisitMemberAccessExpression? For example can you go from:
var memberAccess = memberAccess.With...();
memberAccess = VisitNode(memberAccess);
return memberAccess;
to
var memberAccess = VisitNode(memberAccess);
memberAccess = memberAccess.With...();
return memberAccess;
Then it might mean that you are then visiting with the original node before you've done rewriting. Note this can still be tricky though if you are doing recursive stuff.
Use ReplaceNodes
There's a helper method you can use instead of a rewriter. This will call the function you pass to do rewriting at each step, but that function is handed both the original node in the original tree and the node after child nodes have been rewritten; you can use the original one to ask binding questions (since that's from the original tree) and consume the one that's been rewritten for recursive rewriting.
Break Your Problem into Two Steps
Do two walks: first get a list of all the places will need to update, add those SyntaxNodes to a HashSet or like, and then do the rewrite second where you are updating those places.

AutoMapper to copy EF connected object without line and lines of code

This is a bit of a long one, so get yourself a coffee (other quality libations are available).
In an effort to write as little code as possible, keep my apps simple, testable, maintainable and clean, and to turn apps around in quickly.
I'm using a really simple method I wrote to copy from MVC objects to EF objects to save me writing loads of code when I have objects with loads of properties. In fact, I don't care what the object is or how many properties it has. I just want to copy it without prepping loads of code in a map or somewhere.
Please don't start off on view models and all that, and quoting me the big book of Microsoft. All I'm looking for a little advice from my peers and the community in general about AutoMapper The example here is simple so you can see what I'm getting at.
What I didn't want to do and I've seen it lots, was:-
item ... //the original item populated from somewhere, MVC database, who cares, its got stuff in it
Item newItem = new Item();
newItem.prop1 = item.prop1;
newItem.prop2 = item.prop2;
newItem.prop3 = item.prop3;
newItem.prop4 = item.prop4;
//... you get the idea
or even this ...
Item newItem = new Item {
prop1 = item.prop1,
prop2 = item.prop2,
prop3 = item.prop3,
prop4 = item.prop4,
//... you get the idea
}
So I came up with this. A function called CopyObject that does excatly what I want it to do, so I don't have to care about any objects or how many properties it has, I write one line of code anywhere I need to that does all the work for me. See the example below
//show the item in a view, typically a bootstrap popup dialog
public IActionResult EditItem(int ID)
{
Item item = _dbContext.Items.Where(i => i.ID == ID).FirstOrDefault();
if (item == null)
item = new Item { ... property defaults ... };
return View(item);
}
//save the item from the view
[HttpPost]
public JsonResult EditItem(Item item)
{
Item newItem = _dbContext.Item.Where(i => item.ID).FirstOrDefault();
if (newItem == null)
{
newItem = newItem {... property defaults ... };
_dbContext.Items.Add(newItem);
}
//here is the fun part
CopyObject(item, newItem, ...ignore some properties);
_dbContext.SaveChanges();
return new JsonResult( new { result = "success", message = "Updated" });
}
CopyObject is my function, it does nothing clever except it uses reflection to copy properties from one object to another (EF)object without losing the connection to EF. CopyObject looks like this (below). I won't bore you with the implementation, but simply, it uses reflection to copy properties between any two objects.
At the moment it only copies from the top-level because that's all I need it to do right now, but it wouldn't be a massive stretch to get it to copy a hierarchy of stuff.
It doesn't actually care that the object types match, it doesn't care that the property types match. It only cares that if finds properties on each object with the same name, and it will then attempt to copy the values. You can also specify properties not to copy.
/// <summary>
/// Copies the values of the properties from one object to another object with the same properties if they exist.
/// This will try to copy from any two objects whether they are the same object type or not
/// </summary>
/// <param name="CopyFrom">The object to copy property data from</param>
/// <param name="CopyTo">The object to copy property data to</param>
/// <param name="DontCopy">A (string) list field names not to be copied</param>
/// <returns>True if at least one property was copied, otherwise false</returns>
public bool CopyObjects(object CopyFrom, object CopyTo, params string[] DontCopy) {...}
There is nothing wrong with my code it works perfectly fine just the way I need it to. I don't have any extra work to do when I start a new project no matter how many properties any of the objects have. I just import that
Anyway, because I'm not published or any kind of authority my code is getting frowned upon. I've been told AutoMapper can do the same thing but I can't seem to make it. I always get a disconnected object that I then have to do some tomfoolery to get it back into the EF and ultimately database.
So my question is. How do you acheive the same thing using Automapper without lots of code?. Remember my goal is not to have to write loads of code, in prep or in line.
AutoMapper could ignore properties(such as Name in following example) by using below code
public class MyProfile : Profile
{
public MyProfile ()
{
CreateMap<Item, Item>().ForMember(x => x.Name, opt => opt.Ignore()) ;
}
}
Your Action:
var newItem = _mapper.Map<Item, Item>(item);
Refer to
Ignore mapping one property with Automapper
https://medium.com/ps-its-huuti/how-to-get-started-with-automapper-and-asp-net-core-2-ecac60ef523f

Object reference updating, update all collections property?

Introduction
I found a very weird situation, so essentially I've a collection property called Matches and an object property called Match.
The Matches collections contains a list of item of Match, like this:
private ObservableCollection<Match> _matches = new ObservableCollection<Match>();
public ObservableCollection<Match> Matches
{
get { return _matches; }
}
this collection is valorized when the application start, infact, the software take some data from an Internet site and then, with a scraper fill the collection with the correspond object model Match.
Where the bug start
The Matches collection is binded to a DataGrid. When the user click on an element (Match), available on the DataGrid, the code fire the event SelectionChanged, inside this event I create a copy of the Match clicked, so I can use this object inside all my application:
var match = controller.Matches.FirstOrDefault(c => c.MatchLink == ((Match)Matches.SelectedItem).MatchLink);
as you can see with the use of Linq, I check if the Match clicked by the user have the same link of a Match contained in the Matches collection, infact, each Match in the collection have a unique link like a GUID for example.
The bug
The Match object look like this:
private Match _match;
public Match Match
{
get { return _match; }
set
{
_match = value;
OnPropertyChanged();
}
}
and as I said it contains the Match clicked by the user. This object allow me to get the data from Internet only for this Match, from all methods inside my app. This working pretty well. Until now.
My application allow the user to apply some filters, essentially the user press a button and then the Match saved is updated with the property filled by the user, for example:
Match.League.Rounds = availableRounds;
this code cause the bug, but I'll try to explain the bug better later, I need to explain a bit what happen here.
Essentially the current Match saved in the application should update only the own property League.Rounds, this property is a list of Rounds available for this Match, the structure is very simple:
public class Round
{
public int Id { get; set; }
public string Name { get; set; }
}
the update working good but, the line Match.League.Rounds = availableRounds; update also all the property League.Rounds available in the objects collection Matches.
I don't understand why happen this, I've not created a reference of the object clicked:
var match = controller.Matches.FirstOrDefault(c => c.MatchLink == ((Match)Matches.SelectedItem).MatchLink);
Practice example of what's happening
before filters applied
Matches Collection
Match.Leagues.Rounds[0] contains Id 10 and Name foo
after filters applied
Matches Collection
Match.Leagues.Rounds[0] contains Id 11 and Name foofoo
but it should not be modified, only the Match should be modified.
but a new object. Someone could explain how to fix this? Best regards.
I've not created a reference of the object clicked
Yes, you have. This does not create a new Match object:
var match = controller.Matches.FirstOrDefault(c => c.MatchLink == ((Match)Matches.SelectedItem).MatchLink);
It gets a reference to the already existing Match object in the Matches collection.
If you want to create a new object, you should use the new operator:
var existing = controller.Matches.FirstOrDefault(c => c.MatchLink == ((Match)Matches.SelectedItem).MatchLink);
Match match = new Match();
//set all properties of the new object...
match.Prop1 = existing.Prop1;
Also note that you need to create new Round objects as well. You should consider implementing the IClonable interface.
The FirstOrDefault() method doesn't clone the object for you.

Reflection, contravariance and polymorphism

I have a base class (abstract) with multiple implementations, and some of them contain collection properties of other implementations - like so:
class BigThing : BaseThing
{
/* other properties omitted for brevity */
List<SquareThing> Squares { get; set; }
List<LittleThing> SmallThings { get; set;}
/* etc. */
}
Now sometimes I get a BigThing and I need to map it to another BigThing, along with all of its collections of BaseThings. However, when this happens, I need to be able to tell if a BaseThing in a collection from the source BigThing is a new BaseThing, and thus should be Add()-ed to the destination BigThing's collection, or if it's an existing BaseThing that should be mapped to one of the BaseThings that already exist in the destination collection. Each implementation of BaseThing has a different set of matching criteria on which it should be evaluated for new-ness. I have the following generic extension method to evaluate this:
static void UpdateOrCreateThing<T>(this T candidate, ICollection<T> destinationEntities) where T : BaseThing
{
var thingToUpdate = destinationEntites.FirstOrDefault(candidate.ThingMatchingCriteria);
if (thingToUpdate == null) /* Create new thing and add to destinationEntities */
else /* Map thing */
}
Which works fine. However I think I am getting lost with the method that deals in BigThings. I want to make this method generic because there are a few different kinds of BigThings, and I don't want to have to write methods for each, and if I add collection properties I don't want to have to change my methods. I have written the following generic method that makes use of reflection, but it is not
void MapThing(T sourceThing, T destinationThing) where T : BaseThing
{
//Take care of first-level properties
Mapper.Map(sourceThing, destinationThing);
//Now find all properties which are collections
var collectionPropertyInfo = typeof(T).GetProperties().Where(p => typeof(ICollection).IsAssignableFrom(p.PropertyType));
//Get property values for source and destination
var sourceProperties = collectionPropertyInfo.Select(p => p.GetValue(sourceThing));
var destinationProperties = collectionPropertyInfo.Select(p => p.GetValue(destinationThing));
//Now loop through collection properties and call extension method on each item
for (int i = 0; i < collectionPropertyInfo.Count; i++)
{
//These casts make me suspicious, although they do work and the values are retained
var thisSourcePropertyCollection = sourceProperties[i] as ICollection;
var sourcePropertyCollectionAsThings = thisSourcePropertyCollection.Cast<BaseThing>();
//Repeat for destination properties
var thisDestinationPropertyCollection = destinationProperties[i] as ICollection;
var destinationPropertyCollectionAsThings = thisDestinationPropertyCollection.Cast<BaseThing>();
foreach (BaseThing thing in sourcePropertyCollectionAsThings)
{
thing.UpdateOrCreateThing(destinationPropertyCollectionAsThings);
}
}
}
This compiles and runs, and the extension method runs successfully (matching and mapping as expected), but the collection property values in destinationThing remain unchanged. I suspect I have lost the reference to the original destinationThing properties with all the casting and assigning to other variables and so on. Is my approach here fundamentally flawed? Am I missing a more obvious solution? Or is there some simple bug in my code that's leading to the incorrect behavior?
Without thinking too much, I'd say you have fallen to a inheritance abuse trap, and now trying to save yourself, you might want to consider how can you solve your problem while ditching the existing design which leads you to do such things at the first place. I know, this is painful, but it's an investment in future :-)
That said,
var destinationPropertyCollectionAsThings =
thisDestinationPropertyCollection.Cast<BaseThing>();
foreach (BaseThing thing in sourcePropertyCollectionAsThings)
{
thing.UpdateOrCreateThing(destinationPropertyCollectionAsThings);
}
You are losing your ICollection when you use Linq Cast operator that creates the new IEnumerable<BaseThing>. You can't use contravariance either, because ICollectiondoes not support it. If it would, you'd get away with as ICollection<BaseThing> which would be nice.
Instead, you have to build the generic method call dynamically, and invoke it. The simplest way is probably using dynamic keyword, and let the runtime figure out, as such:
thing.UpdateOrCreateThing((dynamic)thisDestinationPropertyCollection);

How to read same child element types from an XML tree recursively?

I have a sample xml file in following format:-
Also, I have a Control class shown below:
class Control
{
private string id;
public string Id
{
get { return id; }
set { id = value; }
}
private string controlType;
public string ControlType
{
get { return controlType; }
set { controlType = value; }
}
private string searchProperties;
public string SearchProperties
{
get { return searchProperties; }
set { searchProperties = value; }
}
public List<Control> ChildrenControl = new List<Control>();
}
I need to read the XML file mentioned above and populate the code. I am not sure how to recursively do that. I was thinking to use Linq to XML, but not sure how to use it recursively in this case in which parent and child elements are of the same type. Can someone please help me with this problem?
Thanks,
Harit
Update:
Try the following. It uses Linq to XML and a recursive function to parse the controls from out of the XML document. It assumes the existence of your XML data in a file called "Controls.xml", and obviously your Control class. Its not the greatest of code, but it should get you started.
private void ParseControlsData()
{
var doc = XDocument.Load("Controls.xml");
var controls = from control in doc.Element("controls").Elements("control")
select CreateFromXElement(control);
var controlsList = controls.ToList();
Console.ReadLine();
}
private Control CreateFromXElement(XElement element)
{
var control = new Control()
{
Id = (string)element.Attribute("id"),
ControlType = (string)element.Attribute("controlType"),
SearchProperties = (string)element.Attribute("searchProperties")
};
var childrenElements = element.Element("childControls");
if (childrenElements != null)
{
var children = from child in childrenElements.Elements("control")
select CreateFromXElement(child);
control.ChildrenControl = children.ToList();
}
return control;
}
Notes:
In the ParseControlsData function, it uses query expression syntax to select the first element in the document called "controls" (your root), and then selects all sub-elements named "control". A very similar expression occurs inside the CreateFromXElement function, except it needs to find an element called "childControls".
There's no real error checking. You'll definitely need some.
Update:
Don't do this, it doesn't work for your example because you have values stored in attributes, and by default the DataContractSerializer + DataContractAttributes combination does not support that (without a whole bunch of extra work). Other options are Linq to XML (as you suggested) and using the XmlSerializer (which is similar to the DataContractSerializer, but uses its own set of attributes). I'll look into it further.
Previous Answer:
One way to turn an XML document into an Object Graph is to mark the classes you want to create from the XML with DataContract attributes, and to use the DataContractSerializer. All you need to do is make sure that the DataContract(Name = "X") and DataMember(Name = "Y") match the names of the elements inside your XML.
Have a look at my answer on XML Element Selection, which is performing the operation you want (taking existing XML and turning it into an Object Graph). You probably wont need to worry about the CDATA stuff that that user ran into, so your solution will probably be a bit simpler.
Also, have a look at my answer on How to catch/send XML doc with various sub arrays?, which is performing the reverse operation (that user wanted to create XML from an Object Graph).
If you can't tell, I'm a fan of the DataContractSerializer :)
You can use recursion using Func<> delegate, but you have to declare it before specifying the actual delegate logic:
var xDoc = XDocument.Load("Input.xml");
Func<XElement, List<Control>> childControlsQuery = null;
childControlsQuery =
x => (from c in x.Elements("control")
select new Control
{
Id = (string)c.Attribute("id"),
ControlType = (string)c.Attribute("controltype"),
SearchProperties = (string)c.Attribute("searchproperties"),
ChildrenControl = childControlsQuery(c.Element("childControls") ?? new XElement("childControls"))
}).ToList();
var controls = childControlsQuery(xDoc.Root);
You can remove ?? new XElement("childControls") if you're sure there is always childControls element, even when given control does not have any childs.
And if you're sure there is always only one main control, you can get it as:
var mainControl = controls.First();

Categories

Resources