ASP.NET FindControl memory leak - asp.net

we have discover recently that our ASP.NET application has a lot of lines like this:
Label lbXXX = (Label)FormView.FindControl("lbXXX");
same for TextBox, Panel, Image, DropDownList...
Could this be the reason of a memory leak?
Is as bad as I think?

This is not likely to be causing a memroy leak.
To find the cause of a memory leak you should be using a memory profiler and find out what is holding on to references that it shouldn't be.
The most common reason for memory leaks in .NET is event handlers that have not been unregistered, though this tends to not be a problem in ASP.NET due to its request per thread model.
If you suspect a memory leak (how have you determined that you do have one?), profile in order to find the reason - don't assume.

Memory leaks, impossible here. yeah excessive use of find control is not recommended. Since you observing lot of such incidents then better resolve them and clean your code. Performance is also hitting currently due find control.

It's unlikely that it causes memory issues. However, it doesn't come without cost.
As you can see below, the first action is EnsureChildControls which calls CreateChildControls if they are not created yet. That might cause your performance/memory issue.
Called by the ASP.NET page framework to notify server controls that
use composition-based implementation to create any child controls they
contain in preparation for posting back or rendering.
Then not FindControl would have this issue but your custom control(s).
This is the implementation (from ILSpy):
protected virtual Control FindControl(string id, int pathOffset)
{
this.EnsureChildControls();
if (!this.flags[128])
{
Control namingContainer = this.NamingContainer;
if (namingContainer != null)
{
return namingContainer.FindControl(id, pathOffset);
}
return null;
}
else
{
if (this.HasControls())
{
this.EnsureOccasionalFields();
if (this._occasionalFields.NamedControls == null)
{
this.EnsureNamedControlsTable();
}
}
if (this._occasionalFields == null || this._occasionalFields.NamedControls == null)
{
return null;
}
char[] anyOf = new char[]
{
'$',
':'
};
int num = id.IndexOfAny(anyOf, pathOffset);
string key;
if (num == -1)
{
key = id.Substring(pathOffset);
return this._occasionalFields.NamedControls[key] as Control;
}
key = id.Substring(pathOffset, num - pathOffset);
Control control = this._occasionalFields.NamedControls[key] as Control;
if (control == null)
{
return null;
}
return control.FindControl(id, num + 1);
}
}

Related

New item added to session on every request

I found this behaviour by accident, as I return the count of items in a session in an error message and found that some sessions had as many as 120 items in them (they should have 1!). On further investigation I found that every request seems to add an item into the session. They are all negative integers, like -710, -140 -528. I can't seem to see a pattern in what number comes up.
I have checked my code for any interactions with the Session object and as far as I can tell it is not me. I store one item in the session which is my own object which has a number of other properties on it. My session state is SQL server, and I am only serialising a certain set of values that need to be kept.
Has anyone seen anything like this or has any advice on where I can troubleshoot further?
Thank you in advance.
-- Edit, as requested - first where I count the items in the session - this is done in the page load event of my master page. I loop through so I could inspect using the debugger.
int itemCount = Session.Count;
for (int i = 0; i < itemCount; i++)
{
object o = Session[i];
}
-- here is where I add my custom object to the session. This is called at session start and in my master page. It runs on a "get, but if not there, create" principle.
HttpSessionState Session = HttpContext.Current.Session;
HttpRequest Request = HttpContext.Current.Request;
if (Session == null)
return null;
SessionData sessionData = (SessionData)Session[StaticNames.SESSION_NAME];
if (sessionData == null)
{
sessionData = new SessionData();
Session.Add(StaticNames.SESSION_NAME, sessionData);
}
I also have this to get the SessionData object from the session:
public SessionData(SerializationInfo info, StreamingContext ctxt)
{
this.IsManualLogin = (bool)info.GetValue("IsManualLogin", typeof(bool));
this.HasAskedUserForLocation = (bool)info.GetValue("HasAskedUserForLocation", typeof(bool));
// ... etc, more items for all users here
int? loginID = null;
try
{
loginID = info.GetInt32("LoginID");
}
catch
{
return;
}
this.LoginID = loginID.Value;
// ... etc, more items for logged in users only
}
There is also an equivalent method for adding this data to the SerializationInfo used for SqlSessionState.
Credit to the modest jadarnel27.
It turns out the Ajax Control Toolkit NoBot control adds an integer into your session on every request. My website has an auto 40 second refresh, similar to facebook, so this probably would have brought the whole thing crashing down at some point and I am lucky to find it now. Should anyone else consider using the NoBot control, be warned about this behaviour!

setting IsEditable=false for item disables save/close button but not save button?

I developed a data extender class that acts on GetItem and CheckOutItem commands to do some business-specific validation to determine whether the user should have access to modify the item or not (basically if it's past the initial "author" task in workflow, no one can edit it. by default Tridion allows "reviewers" in workflow to edit the item, which is a no-no in our business).
I am relatively certain this worked at one point, but now does not. I'm exploring what might have changed, but I thought I'd ask here in case anyone has an idea.
If the item can't be modified, I'm setting the IsEditable attribute to false. This does in fact disable the Save and Close button and Save and New button, but for some reason the Save button is enabled. I don't quite understand why there could be a difference. (I'm looking to see if someone extended the save button somehow, but I don't see that being done). Any thoughts on how the Save button would enable when the others aren't?
thanks for any suggestions,
~Warner
public override XmlTextReader ProcessResponse(XmlTextReader reader, PipelineContext context)
{
using (new Tridion.Logging.Tracer())
{
string command = context.Parameters["command"].ToString();
if (command == CHECKOUT_COMMAND || command == GETITEM_COMMAND)
{
XmlDocument xmlDoc = ExtenderUtil.GetExtenderAsXmlDocument(reader);
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
nsmgr.AddNamespace("tcm", Constants.TcmNamespace);
try
{
//is this a page or component?
XmlNode thisItemNode = null;
thisItemNode = xmlDoc.SelectSingleNode("//tcm:Component", nsmgr) ?? xmlDoc.SelectSingleNode("//tcm:Page", nsmgr);
if (thisItemNode == null) return ExtenderUtil.GetExtenderAsXmlTextReader(xmlDoc);
// need to impersonate system admin in order to get workflow version of item later
Session sessionSystemAdmin = Util.SystemAdminSession;
XmlAttribute idAttribute = thisItemNode.Attributes.GetNamedItem("ID") as XmlAttribute;
//if ID attribute is null, we don't have the actual object being used (just a referenced item. so, we'll ignore it)
if (idAttribute != null)
{
string itemId = idAttribute.Value;
VersionedItem tridionObject = Util.ObtainValidTridionIdentifiableObject(sessionSystemAdmin, itemId) as VersionedItem;
//logic has been moved to separate method, just for maintainablility...
//the logic may change when workflow code is finished.
bool allowSave = IsItemValidForEdit(tridionObject, nsmgr);
if (!allowSave)
{
//not the WIP ("author") task... make item read-only
Logger.WriteVerbose("setting iseditable to false for item: " + itemId);
XmlAttribute isEditableAttribute = thisItemNode.Attributes.GetNamedItem("IsEditable") as XmlAttribute;
isEditableAttribute.Value = "false";
}
}
}
catch (Exception e)
{
Logger.WriteError("problem with get item data extender", ErrorCode.CMS_DATAEXTENDER_GETITEM_FAILURE, e);
}
return ExtenderUtil.GetExtenderAsXmlTextReader(xmlDoc);
}
else
{
return reader;
}
}
}
Most of the Tridion GUI probably bases the options it presents on the so-called Allowed Actions. This is a combination of the Allow and Deny attributes that are present in list-calls (if requested) and item XML.
So at the very least you will have to remove the CheckIn and Edit action from the Allow attribute (and probably add them to the Deny attribute). If you look at the Core Service documentation (or any other Tridion API documentation: these values haven't changed in a long time) you can find an Enum called Actions that hold the possible actions and their corresponding values. The Allow and Deny attributes are simply additions of these numbers.
The CheckIn action I mention is number 2, Edit is 2048.
Update:
I have a little command line program to decode the AllowedActions for me. To celebrate your question, I quickly converted it into a web page that you can find here. The main work horse is below and shows both how you can decode the number and how you can manipulate it. In this case it's all subtraction, but you can just as easily add an allowed action by adding a number to it.
var AllowedActionsEnum = {
AbortAction: 134217728,
ExecuteAction: 67108864,
FinishProcessAction: 33554432,
RestartActivityAction: 16777216,
FinishActivityAction: 8388608,
StartActivityAction: 4194304,
BlueprintManagedAction: 2097152,
WorkflowManagedAction: 1048576,
PermissionManagedAction: 524288,
EnableAction: 131072,
CopyAction: 65536,
CutAction: 32768,
DeleteAction: 16384,
ViewAction: 8192,
EditAction: 2048,
SearchAction: 1024,
RePublishAction: 512,
UnPublishAction: 256,
PublishAction: 128,
UnLocalizeAction: 64,
LocalizeAction: 32,
RollbackAction: 16,
HistoryListAction: 8,
UndoCheckOutAction: 4,
CheckInAction: 2,
CheckOutAction: 1
};
function decode() {
var original = left = parseInt(prompt('Specify Allow/Deny actions'));
var msg = "";
for (var action in AllowedActionsEnum) {
if (left >= AllowedActionsEnum[action]) {
msg += '\n' + action + ' ('+AllowedActionsEnum[action]+')';
left -= AllowedActionsEnum[action];
}
}
alert(original+msg);
}
The solution is to really look over the entire solution and be absolutely positive that nobody snuck something in recently that messes with the Save button and is magically enabling it behind the scenes. I've re-edited the code to show how I initially had it. And it does work. It will disable the save, save/close, save/new buttons and make all fields disabled. I'm sorry that I wasted Frank's time. Hopefully having this here for historical purposes may come in handy for someone else with similar requirements in the future.

Multiple TrackingParticipants not working, have funny side effects?

We are rying to use WF with multiple tracking participants which essentially listen to different queries - one for activity states, one for custom tracknig records which are a subclass of CustomTrackingRecord.
The problem is that we can use both TrackingParticipants indivisually, but not together - we never get our subclass from CustomTrackingRecord but A CustomTrackingRecord.
If I put bopth queries into one TrackingParticipant and then handle everythign in one, both work perfectly (which indicates teh error is not where we throw them).
The code in question for the combined one is:
public WorkflowServiceTrackingParticipant ()
{
this.TrackingProfile = new TrackingProfile()
{
ActivityDefinitionId = "*",
ImplementationVisibility = ImplementationVisibility.All,
Name = "WorkflowServiceTrackingProfile",
Queries = {
new CustomTrackingQuery() { Name = "*", ActivityName = "*" },
new ActivityStateQuery() {
States = {
ActivityStates.Canceled,
ActivityStates.Closed,
ActivityStates.Executing,
ActivityStates.Faulted
}
},
}
};
}
When using two TrackingParticipants we have two TrackingProfile (with different names) that each have one of the queries.
in the track method, when using both separate, the lines:
protected override void Track(TrackingRecord record, TimeSpan timeout)
{
Console.WriteLine("*** ActivityTracking: " + record.GetType());
if (record is ActivityBasedTrackingRecord)
{
System.Diagnostics.Debugger.Break();
}
never result in the debugger hitting, when using only the one to track our CustomTrackingRecord subclass (ActivityBasedTrackingRecord) then it works.
Anyone else knows about this? For now we have combined both TrackingParticipants into one, but this has the bad side effect that we can not dynamically expand the logging possibilities, which we would love to. Is this a known issue with WWF somewhere?
Version used: 4.0 Sp1 Feature Update 1.
I guess I encounterad the exact same problem.
This problem occurs due to the restrictions of the extension mechanism. There can be only one instance per extension type per workflow instance (according to Microsoft's documentation). Interesting enough though, one can add multiple instances of the same type to one workflow's extensions which - in case of TrackingParticipant derivates - causes weird behavior, because only one of their tracking profiles is used for all participants of the respective type, but all their overrides of the Track method are getting invoked.
There is a (imho) ugly workaround to this: derive a new participant class from TrackingParticipant for each task (task1, task2, logging ...)
Regards,
Jacob
I think that this problem isn't caused by extension mechanism, since DerivedParticipant 1 and DerivedParticipant 2 are not the same type(WF internals just use polymorphism on the base class).
I was running on the same issue, my Derived1 was tracking records that weren't described in its profile.
Derived1.TrackingProfile.Name was "Foo" and Derived2.TrackingProfile.Name was null
I changed the name from null to "Bar" and it worked as expected.
Here is a WF internal reference code, describing how is the Profile selected
// System.Activities.Tracking.RuntimeTrackingProfile.RuntimeTrackingProfileCache
public RuntimeTrackingProfile GetRuntimeTrackingProfile(TrackingProfile profile, Activity rootElement)
{
RuntimeTrackingProfile runtimeTrackingProfile = null;
HybridCollection<RuntimeTrackingProfile> hybridCollection = null;
lock (this.cache)
{
if (!this.cache.TryGetValue(rootElement, out hybridCollection))
{
runtimeTrackingProfile = new RuntimeTrackingProfile(profile, rootElement);
hybridCollection = new HybridCollection<RuntimeTrackingProfile>();
hybridCollection.Add(runtimeTrackingProfile);
this.cache.Add(rootElement, hybridCollection);
}
else
{
ReadOnlyCollection<RuntimeTrackingProfile> readOnlyCollection = hybridCollection.AsReadOnly();
foreach (RuntimeTrackingProfile current in readOnlyCollection)
{
if (string.CompareOrdinal(profile.Name, current.associatedProfile.Name) == 0 && string.CompareOrdinal(profile.ActivityDefinitionId, current.associatedProfile.ActivityDefinitionId) == 0)
{
runtimeTrackingProfile = current;
break;
}
}
if (runtimeTrackingProfile == null)
{
runtimeTrackingProfile = new RuntimeTrackingProfile(profile, rootElement);
hybridCollection.Add(runtimeTrackingProfile);
}
}
}
return runtimeTrackingProfile;
}

Is there anything wrong with this database class's execute query function?

So I have this old code being used, that runs simple ExecuteNonQuery command for database calls. I'm using DbConnection, DbTransaction and other System.Data.Common commands.
I seem to get a lot of Null Reference errors whenever I use the function in certain parts of the project, though it seems fine in other parts. I think it has to do with opening connections manually or some problem with calling it, but I'm wondering if the function itself is badly designed originally (shouldn't there be a way to fix any problems in the way it is called?)
I feel when transactions are involved, these null reference errors come up more often, I think the error I get is null exception at "_command = _db.GetStoredProcCommand(storedProcedure);" inside the following function. But that stored procedure does exist, so it makes no sense.
public List<OutputParameter> execute(String storedProcedure, StoredProcedureParameter[] sqlParameters)
{
try
{
List<OutputParameter> outputParameters = new List<OutputParameter>();
_command = _db.GetStoredProcCommand(storedProcedure);
for (int x = 0; x < sqlParameters.GetLength(0); x++)
{
if (sqlParameters[x] != null)
{
StoredProcedureParameter sqlParameter = sqlParameters[x];
String param = sqlParameter.ParameterName;
DbType dbType = sqlParameter.DbType;
object value = sqlParameter.Value;
if (sqlParameter.IsOutputParam)
{
_db.AddOutParameter(_command, param, dbType, 32);
OutputParameter outputParameter = new OutputParameter(param);
outputParameters.Add(outputParameter);
}
else
_db.AddInParameter(_command, param, dbType, value);
}
}
if (_transaction == null)
_db.ExecuteNonQuery(_command);
else
_db.ExecuteNonQuery(_command, _transaction);
foreach (OutputParameter op in outputParameters)
{
op.ParameterValue = _db.GetParameterValue(_command, op.ParameterName);
}
return outputParameters;
}
catch (SqlException sqle)
{
throw new DataAccessException(sqle.ToString());
}
catch (Exception e)
{
throw new DataAccessException(e.ToString());
}
}
Your _command variable appears to be a field and as such a shared member.
As such your code is very susceptible to multithreading issues (if two functions call this class with different stored procedures, what happens?).
A Command should also be closed and disposed of properly, which is not happening in your code, not explicitly anyways.
If you are getting a null reference exception in the line _command = _db.GetStoredProcCommand(storedProcedure); then the only thing that can be null there is _db. The storedProcedure is just a parameter and _command could happily be null without a problem.
Since you aren't actually doing anything in the code to make sure that _db exists and is valid, open, etc. then this is most likely the problem.

Finding duration of a video using directshowlib-2005

My asp.net(c#) method looks as follows:
static public bool GetVideoLength(string fileName, out double length)
{
DirectShowLib.FilterGraph graphFilter = new DirectShowLib.FilterGraph();
DirectShowLib.IGraphBuilder graphBuilder;
DirectShowLib.IMediaPosition mediaPos;
length = 0.0;
try
{
graphBuilder = (DirectShowLib.IGraphBuilder)graphFilter;
graphBuilder.RenderFile(fileName, null);
mediaPos = (DirectShowLib.IMediaPosition)graphBuilder;
mediaPos.get_Duration(out length);
return true;
}
catch
{
return false;
}
finally
{
mediaPos = null;
graphBuilder = null;
graphFilter = null;
}
}
I got the duration with the above method. But my problem is i can't delete the physical file
after my operation. I used
File.Delete(FilePath);
While performing this action i got an exception as follows:
"The process cannot access the file because it is being used by another process."
My Os is windows 7(IIS 7)
Any one please help me to sort this out?
I've got no experience in coding directshow apps in C#, but plenty of experience in C++.
DirectShow is based on a technology called COM - which uses reference counting to tell it when an object is in use.
It would use a COM object to represent the IGraphBuilder for example.
In C++, we would have to deconstruct the graph, by removing all its filters, then release the graph.
I understand that C# has its own garbage collection etc., but unless you explicitly release the objects you use, they'll remain in memory.
It seems from the code you've quoted, that the graph is still opened, even though playback may have finished. In that case, it'll hold a reference to the file which you've played back, which would explain why you can't delete it - e.g. there's a read lock on the file.
Hope this points you in the right direction!

Resources