我有一个ChainableSelect组件,可以选择它,并基于它生成另一个ChainableSelect并持续运行。 我注意到如果我将选择更改为大约7〜10次 内存消耗开始增加而没有停止导致笔记本电脑冻结
重现行为的步骤:
使用此版本的ASP.NET Core'3.0'版本
运行此代码
public class ChainableSelect<TEntity> : InputableBaseComponent where TEntity : DbModel
{
[Parameter]
public IChainableSelectDataFetcher<TEntity> DataFetcher { get; set; }
[Parameter]
public string FilterText { get; set; } = "";
[Parameter]
public bool IsBusy { get; set; }
[Parameter]
public bool FilterChanged { get; set; } = true;
[Parameter]
public string ParentPropertyName { get; set; }
[Parameter]
public TEntity ParentEntity { get; set; }
TEntity _localParentEntity;
IViewProperty _localViewProperty;
ReadOnlyObservableCollection<TEntity> _entities { get; set; }
bool _updateEntities = true;
protected override void OnInitialized()
{
base.OnInitialized();
_localViewProperty = ViewPropertyFactory.CreateEmptyProperty("", null);
}
protected override async Task OnParametersSetAsync()
{
await base.OnParametersSetAsync();
if (DataFetcher != null)
{
if (_localParentEntity != ParentEntity || ParentEntity == null || FilterChanged)
{
_entities = await DataFetcher.GetDescendents(ParentEntity, ParentPropertyName);
_updateEntities = false;
}
_localViewProperty.PropertyValue = null;
FilterChanged = true;
StateHasChanged();
}
_localParentEntity = ParentEntity;
}
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
if (_entities == null)
return;
if (_entities.Count == 0)
return;
var seq = 0;
base.BuildRenderTree(builder);
builder.OpenElement(seq, "input");
builder.AddAttribute(seq, "class", "form-control form-control-sm dropdown-input bg-white");
builder.AddAttribute(seq, "value", _localViewProperty.PropertyValue ?? "--SELECT--");
builder.AddAttribute(seq, "readonly", "readonly");
builder.AddAttribute(seq, "onfocus", "document.getElementById('" + Id + "').style.zIndex=1000");
builder.CloseElement();
builder.OpenElement(++seq, "div");
builder.AddAttribute(seq, "class", "dropdown-div border");
builder.AddAttribute(seq, "onmouseover", "document.getElementById('" + Id + "').style.zIndex=1000");
builder.AddAttribute(seq, "onmouseout", "document.getElementById('" + Id + "').style.zIndex=1");
builder.OpenElement(++seq, "div");
builder.AddAttribute(seq, "class", "p-1 m-1 bg-white");
builder.OpenElement(++seq, "input");
builder.AddAttribute(seq, "class", "form-control form-control-sm");
builder.AddAttribute(seq, "oninput", new Action<ChangeEventArgs>(OnFilterChanged));
builder.CloseElement();
builder.OpenElement(++seq, "hr");
builder.CloseElement();
builder.CloseElement();
builder.OpenElement(++seq, "a");
builder.AddAttribute(seq, "onclick", new Action(() => OnSelect(null)));
builder.AddContent(seq, "None");
builder.CloseElement();
if (_entities != null)
foreach (var _entity in _entities)
{
if (!_entity.ToString().Contains(FilterText, StringComparison.InvariantCultureIgnoreCase))
continue;
builder.OpenElement(++seq, "a");
builder.AddAttribute(seq, "onclick", new Action(() => OnSelect(_entity)));
if (_entity == _localViewProperty.PropertyValue)
builder.AddAttribute(seq, "class", "selected");
builder.AddContent(seq, _entity.ToString());
builder.CloseElement();
}
builder.CloseElement();
builder.CloseElement();
FilterChanged = false;
if (_localViewProperty.PropertyValue != null && _localViewProperty.PropertyValue != ParentEntity)
{
builder.OpenComponent<ChainableSelect<TEntity>>(++seq);
builder.AddAttribute(seq, "Parameters", Parameters);
builder.AddAttribute(seq, "ParentEntity", ViewProperty.PropertyValue);
builder.AddAttribute(seq, "ParentPropertyName", ParentPropertyName);
builder.CloseComponent();
}
}
private async void OnSelect(TEntity entity)
{
ViewProperty.PropertyValue = entity;
_localViewProperty.PropertyValue = entity;
FilterChanged = true;
_updateEntities = false;
StateHasChanged();
}
protected override bool ShouldRender()
{
return !IsBusy && FilterChanged;
}
CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
private async void OnFilterChanged(ChangeEventArgs e)
{
_cancellationTokenSource.Cancel();
_cancellationTokenSource = new CancellationTokenSource();
var _cancelationToken = _cancellationTokenSource.Token;
IsBusy = true;
await Task.Delay(200, _cancelationToken).ContinueWith(t =>
{
if (!_cancelationToken.IsCancellationRequested)
{
FilterText = e.Value.ToString();
FilterChanged = true;
}
IsBusy = false;
InvokeAsync(StateHasChanged);
});
}
}
ChainableDataFetcher
public class ChainableEntityDataFetcher<TEntity> : IChainableSelectDataFetcher<TEntity> where TEntity : DbModel
{
public Func<TEntity, string, Task<ReadOnlyObservableCollection<TEntity>>> GetDescendents { get; set; }
private readonly IDataAccessLayer<TEntity> _dataAccessLayer;
private PropertyInfo _parentProperty;
public ChainableEntityDataFetcher(IDataAccessLayer<TEntity> dataAccessLayer)
{
_dataAccessLayer = dataAccessLayer;
GetDescendents = DataFetcherAsync;
}
public void Dispose()
{
_dataAccessLayer.Dispose();
GetDescendents = null;
_parentProperty = null;
}
async Task<ReadOnlyObservableCollection<TEntity>> DataFetcherAsync(TEntity parentEntity,string parentPropertyName)
{
var _comparableValue = parentEntity == null ? " is NULL" : " = " + parentEntity.DbModelId.ToString();
var _dataList = _dataAccessLayer.GetEntities(new Dapper.CommandDefinition(String.Format("select * from {0} where {1}{2}",
_dataAccessLayer.TableName,
parentPropertyName,
_comparableValue)));
ObservableCollection<TEntity> _entities = new ObservableCollection<TEntity>(_dataList);
ReadOnlyObservableCollection<TEntity> _readOnlyEntites = new ReadOnlyObservableCollection<TEntity>(_entities);
return _readOnlyEntites;
}
}
DataAccessLayer
public class DataAccessLayer<TEntity> : IDataAccessLayer<TEntity> where TEntity : DbModel
{
public IDbConnectionBuilder DbConnectionBuilder { get; }
private readonly string _tableName;
...
public ICollection<TEntity> GetEntities(CommandDefinition command)
{
using (var _connection = DbConnectionBuilder.GetDbConnection())
{
_connection.Open();
return _connection.Query<TEntity>(command).ToList();
}
}
...
答案 0 :(得分:0)
我发现了问题。
async StateHasChanged
只是不要在异步操作中使用StateHasChanges