我创建了一个XRMTool(WPF)来将数据从Dynamics CRM Online提取到XML文件。输出文件可能有一百万行,但我收到以下错误。
无法分配2147483647字节的托管内存缓冲区。可用内存量可能很低。
我可以添加一些过滤器并减少行数,但根据应用程序的要求,我必须一次性提取至少一个月的数据,这将是大约一百万行。
如何在WPF应用程序中增加内存缓冲区?
以下是我的代码
string fetchaudit = @"<fetch version='1.0' mapping='logical' output-format='xml-platform'>
<entity name='audit'>
<attribute name='action' />
<attribute name='auditid' />
<attribute name='callinguserid' />
<attribute name='objecttypecode' />
<attribute name='createdon' />
<attribute name='objectid' />
<attribute name='objectid' />
<attribute name='operation' />
<attribute name='regardingobjectid' />
<attribute name='transactionid' />
<attribute name='userid' />
<filter type='and'>
<condition attribute='createdon' operator='on-or-after' value='" + Fromdate + @"' />
<condition attribute='createdon' operator='on-or-before' value='" + Todate + @"' />
</filter>
</entity>
</fetch>";
while (true)
{
string xml = CreateXml(fetchaudit, pagingCookie, pageNumber, fetchCount);
RetrieveMultipleRequest fetchRequest1 = new RetrieveMultipleRequest
{
Query = new FetchExpression(xml) // Error occurs here when page number reaches 100
};
EntityCollection returnCollection = ((RetrieveMultipleResponse)_ctrl.CrmConnectionMgr.CrmSvc.Execute(fetchRequest1)).EntityCollection;
// var collection = _ctrl.CrmConnectionMgr.CrmSvc.GetEntityDataByFetchSearchEC(xml);
if (returnCollection != null)
{
if (returnCollection.Entities.Count >= 0)
Entities.AddRange(returnCollection.Entities);
}
if (returnCollection.MoreRecords)
{
pageNumber++;
pagingCookie = returnCollection.PagingCookie;
}
else
{
break;
}
} // end while
我在SDK中使用以下方法进行分页SampleCode \ CS \ GeneralProgramming \ Queries \ FetchPagingWithCookie.cs(https://msdn.microsoft.com/en-us/library/gg309717.aspx)
答案 0 :(得分:1)
很可能在第一个请求中没有发生异常,如果是,则减少返回行的数量。
您当前的问题是您将所有内容存储在内存中,直到检索结束
Entities.AddRange(returnCollection.Entities);
Entities
的集合正在快速增长,但由于在实际SDK检索时发生内存分配,因此添加失败。
要将查询结果序列化为具有DataContractSerializer
的单个集合,您可以使用下一个IEnumerable实现来按包读取数据(and more clarifications for the code):
public class XrmEnumerator<T> : IEnumerator<T> where T : Entity
{
private readonly Queue<T> _collected = new Queue<T>();
private IOrganizationService _service;
private QueryExpression _query;
private string _lastCookie;
private bool _moreRecords;
public T Current { get; private set; }
object IEnumerator.Current => Current;
public XrmEnumerator(IOrganizationService service, QueryExpression query)
{
_service = service;
_query = query;
if (query.PageInfo == null)
query.PageInfo = new PagingInfo
{
Count = 5000,
PageNumber = 1
};
FillThePack();
}
private void FillThePack()
{
var result = _service.RetrieveMultiple(_query);
_lastCookie = result.PagingCookie;
result.Entities.ToList().ForEach(e=>_collected.Enqueue(e.ToEntity<T>()));
_moreRecords = result.MoreRecords;
}
public void Dispose()
{
_service = null;
_query = null;
}
public bool MoveNext()
{
if(_collected.Count == 0)
{
if (!_moreRecords)
{
return false;
}
_query.PageInfo.PagingCookie = _lastCookie;
_query.PageInfo.PageNumber++;
FillThePack();
}
Current = _collected.Dequeue();
return true;
}
public void Reset()
{
_query.PageInfo.PagingCookie = string.Empty;
_query.PageInfo.PageNumber = 1;
}
}
public class XrmEnumerable<T> : IEnumerable<T> where T : Entity
{
private readonly XrmEnumerator<T> _enumerator;
public XrmEnumerable(IOrganizationService service, QueryExpression query)
{
_enumerator = new XrmEnumerator<T>(service, query);
}
public IEnumerator<T> GetEnumerator() => _enumerator;
IEnumerator IEnumerable.GetEnumerator()
{
return _enumerator;
}
public virtual void Add(T entity)
{
// do your code on item deserialization
}
}
它使用的是QueryExpression
而不是FetchXml
,但你可以使用easily convert it
使用样本:
var enumerable = new XrmEnumerable<Contact>(service, query);
var serializer = new DataContractSerializer(typeof(XrmEnumerable<Contact>));
using (var file = File.OpenWrite("contacts.xml"))
serializer.WriteObject(file, enumerable);
另外,您可以创建XrmEnumerable
的子类,并在Add
函数中执行逻辑