이 항목에서는 Sync Framework 동기화 커뮤니티에서 사용되는 추적 필터에 관리되는 언어를 사용하는 방법과 필터링된 일괄 변경 내용을 열거하는 방법을 설명합니다.
이 항목에서는 기본적인 C# 및 Microsoft .NET Framework 개념에 익숙하다고 가정합니다.
이 항목의 예제에서는 다음과 같은 Sync Framework 클래스 및 멤버를 중점적으로 설명합니다.
필터 추적 이해
동기화 커뮤니티의 모든 복제본이 커뮤니티에 사용되는 필터를 추적하는 것이 좋습니다. 필터링된 복제본이 필터 추적 복제본에서 필터링된 변경 내용 열거를 받은 경우에는 필터링된 복제본의 정보 크기가 작게 유지됩니다. 하지만 필터링된 복제본이 필터를 추적하지 않는 복제본으로부터 필터링된 변경 내용 열거를 받은 경우에는 정보 크기가 전송된 변경 내용의 수에 비례하여 커집니다.
필터 추적 공급자는 IFilterTrackingProvider 인터페이스를 구현합니다. Sync Framework에서는 이 인터페이스를 사용하여 원본 복제본과 대상 복제본 모두에 의해 추적되는 필터의 협상을 중재합니다.
원본 공급자는 동기화 중에 서로 추적되는 각 필터에 필터 메타데이터를 전송합니다. 필터 추적 공급자는 일반적으로 ISupportFilteredSync 인터페이스를 구현하여 추적 필터에 의해 필터링된 변경 내용을 열거할 수 있습니다.
대상 공급자는 변경 내용 적용자가 추적된 필터의 필터 키 맵과 필터 잊어버린 정보를 가져오고 업데이트하는 데 사용하는 IFilterTrackingNotifyingChangeApplierTarget 인터페이스를 구현합니다. 대상 공급자는 SaveItemChange 또는 SaveChangeWithChangeUnits 메서드에 전송되는 항목 및 변경 단위의 필터 변경 내용 메타데이터도 업데이트합니다.
빌드 요구 사항
.NET Framework 2.0 이상
Microsoft.Synchronization에 대한 참조
예제
이 항목의 예제 코드는 필터 변경 내용 메타데이터를 전송하고 원본 공급자 역할을 수행하여 필터링된 일괄 변경 내용을 열거할 수 있으며 대상 공급자 역할을 수행하여 필터 변경 내용 메타데이터를 적용하는 필터 추적 공급자를 구현하는 방법을 보여 줍니다. 이 예제의 복제본은 연락처 정보를 쉼표로 구분된 값의 목록으로 저장하는 텍스트 파일입니다. 동기화할 항목은 이 파일에 포함된 연락처입니다. 필터는 필터 문자열이 연락처의 주소 필드에서 발견되는 경우에만 연락처가 포함되도록 하는 문자열입니다.
추적된 필터 협상
추적된 필터를 협상하려면 Sync Framework에서 대상 공급자의 SpecifyTrackedFilters를 호출합니다. 이 예제에서는 대상 복제본에 의해 추적된 필터의 목록을 열거하고 각 필터를 원본 공급자에게 전송하고 원본 공급자도 필터를 추적하는 경우에만 서로 추적된 필터의 목록에 필터를 추가합니다.
public void SpecifyTrackedFilters(RequestTrackedFilterCallback filterTrackingRequestCallback)
{
foreach (AddressFilter filter in _ContactStore.TrackedFilters)
{
if (filterTrackingRequestCallback(filter))
{
// Add the filter to the list of mutually tracked filters only when the
// source provider also tracks the filter.
_filterKeyMap.AddFilter(filter);
}
}
}
대상 공급자에 의해 열거된 각 필터에 대해 Sync Framework 에서 원본 공급자의 TryAddTrackedFilter 메서드를 호출합니다. 이 예제에서는 지정한 필터가 원본 공급자에 의해 추적되는지 여부를 확인하고 해당되는 경우 서로 추적되는 필터 목록에 추가합니다.
public bool TryAddTrackedFilter(ISyncFilter filter)
{
bool isTracked = false;
foreach (AddressFilter addressFilter in _ContactStore.TrackedFilters)
{
// Add the filter to the list of mutually tracked filters only when it
// is identical to one of the filters of this replica.
if (addressFilter.IsIdentical(filter))
{
_filterKeyMap.AddFilter(addressFilter);
isTracked = true;
break;
}
}
return isTracked;
}
동기화에 사용되는 필터 협상
대상 복제본이 필터링되는 복제본인 경우 일반적으로 원본 공급자가 변경 내용을 열거하기 위해 사용하는 필터를 요청합니다. 원본 공급자는 TryAddFilter 메서드를 통해 이 요청을 수신합니다. 이 예제에서는 요청된 필터가 서로 추적되는 필터인지 여부를 확인하고 해당되는 경우 변경 내용을 열거할 때 사용하기 위해 저장합니다.
public bool TryAddFilter(object filter, FilteringType filteringType)
{
_filterForSync = null;
// The filter must be tracked by both replicas.
for (int filterKey = 0; filterKey < _filterKeyMap.Count; filterKey++)
{
if (_filterKeyMap[filterKey].IsIdentical((ISyncFilter)filter))
{
_filterForSync = (AddressFilter)_filterKeyMap[filterKey];
_filteringType = filteringType;
break;
}
}
return (null != _filterForSync);
}
필터 메타데이터 및 필터링된 변경 내용 보내기
원본 공급자는 일괄 변경 내용을 보낼 때 필터 메타데이터를 보냅니다. 이 예제에서는 필터가 동기화를 위해 사용되는지 여부에 따라 필터링된 일괄 변경 내용 또는 필터링되지 않은 일괄 변경 내용 중 하나를 보냅니다. 해당 항목이 추적 필터와 관련하여 변경될 때 일괄 변경 내용에 포함된 각 변경 내용에 필터 변경 내용 메타데이터가 연결됩니다. 각 추적된 필터에 대한 필터 잊어버린 정보도 전송됩니다. 동기화를 위해 필터가 사용되는 경우 항목은 필터에 있을 때 전송되고 필터에서 제외된 항목은 삭제할 항목으로 표시됩니다.
public override ChangeBatch GetChangeBatch(uint batchSize, SyncKnowledge destinationKnowledge, out object changeDataRetriever)
{
// Return this object as the IChangeDataRetriever object that is called to retrieve item data.
changeDataRetriever = this;
// The metadata storage service does not support filter tracking, so enumerate changes manually.
ChangeBatch changeBatch;
if (null == _filterForSync)
{
// No filter was specified for synchronization, so produce an unfiltered change batch.
changeBatch = new ChangeBatch(IdFormats, destinationKnowledge, _ContactStore.ContactReplicaMetadata.GetForgottenKnowledge());
}
else
{
// A filter was specified for synchronization, so produce a filtered change batch.
CustomFilterInfo filterInfo = new CustomFilterInfo(IdFormats, _filterForSync);
changeBatch = new ChangeBatch(IdFormats, destinationKnowledge, _filterForSync.FilterForgottenKnowledge, filterInfo);
}
// If the destination replica tracks filters that are tracked by the source replica,
// set the filter key map of the change batch.
if (0 < FilterKeyMap.Count)
{
// Add the filter key map to the change batch before any groups are started.
changeBatch.FilterKeyMap = FilterKeyMap;
}
// Get all the items from the metadata store.
IEnumerable<ItemMetadata> allItems = _ContactStore.ContactReplicaMetadata.GetAllItems(true);
// Convert the destination knowledge for use with local versions.
SyncKnowledge mappedDestKnowledge = _ContactStore.ContactReplicaMetadata.GetKnowledge().MapRemoteKnowledgeToLocal(destinationKnowledge);
// Build the list of items in the change batch.
List<ItemChange> itemChanges = new List<ItemChange>((int)batchSize);
uint cItemsInBatch = 0;
SyncId replicaId = _ContactStore.ContactReplicaMetadata.ReplicaId;
foreach (ItemMetadata itemMeta in allItems)
{
// Process all items if this is an unfiltered enumeration.
// Otherwise, only process an item that has been in the filter. An item has been in the filter if
// it is currently in the filter or if its move version in relation to the filter is a value
// other than (0,0).
if (null == _filterForSync || _ContactStore.HasBeenInFilter(itemMeta, _filterForSync))
{
// If a change is not contained in the destination knowledge, add it to the change batch.
if (!mappedDestKnowledge.Contains(replicaId, itemMeta.GlobalId, itemMeta.ChangeVersion))
{
ChangeKind kind;
if (itemMeta.IsDeleted)
{
kind = ChangeKind.Deleted;
}
// An item that has been in the filter but is not currently in the filter has
// recently moved out, so it must be marked as a ghost.
else if (null != _filterForSync
&& !_filterForSync.IsInFilter(_ContactStore.ContactList[itemMeta.GlobalId]))
{
kind = ChangeKind.Ghost;
}
else
{
kind = ChangeKind.Update;
}
ItemChange itemChange = new ItemChange(IdFormats, _ContactStore.ContactReplicaMetadata.ReplicaId,
itemMeta.GlobalId, kind, itemMeta.CreationVersion, itemMeta.ChangeVersion);
// Pass along any filter information for filters tracked by both the source and destination replicas.
_ContactStore.AddFilterChanges(_filterKeyMap, itemMeta, mappedDestKnowledge, itemChange);
// Add the item to the change list. Include ghosts only if the destination requested ghosts.
if (kind != ChangeKind.Ghost || (kind == ChangeKind.Ghost && FilteringType.CurrentItemsAndVersionsForMovedOutItems == _filteringType))
{
itemChanges.Add(itemChange);
}
cItemsInBatch++;
}
}
if (batchSize <= cItemsInBatch)
{
break;
}
}
// Add the list of items to the change batch object.
if (0 < itemChanges.Count)
{
changeBatch.BeginOrderedGroup(itemChanges[0].ItemId);
// Set the filter forgotten knowledge for each filter that the destination has requested.
for (int iFilter = 0; iFilter < FilterKeyMap.Count; iFilter++)
{
AddressFilter addressFilter = (AddressFilter)FilterKeyMap[iFilter];
changeBatch.SetFilterForgottenKnowledge((uint)iFilter, addressFilter.FilterForgottenKnowledge);
}
changeBatch.AddChanges(itemChanges);
// End the group of changes in the change batch. Pass the current source knowledge.
changeBatch.EndOrderedGroup(itemChanges[itemChanges.Count - 1].ItemId, _ContactStore.ContactReplicaMetadata.GetKnowledge());
// If all items were enumerated before the batch was filled, then this is the last batch.
if (cItemsInBatch < batchSize)
{
changeBatch.SetLastBatch();
}
}
else
{
throw new InvalidOperationException("GetChangeBatch called but there are no new changes to enumerate.");
}
return changeBatch;
}
서로 추적된 각 필터에 대해 필터 변경 내용 메타데이터가 검사됩니다. 변경 내용의 이동 버전이 대상 정보에 없을 때 필터 변경 내용 메타데이터가 변경 내용에 추가됩니다.
public void AddFilterChanges(FilterKeyMap filterKeyMap, ItemMetadata itemMeta, SyncKnowledge destKnowledge,
ItemChange itemChange)
{
for (int filterKey = 0; filterKey < filterKeyMap.Count; filterKey++)
{
// Find the filter in the list of all filters tracked by this replica.
int iFilter = 0;
for (; iFilter < _trackedFilters.Count; iFilter++)
{
if (filterKeyMap[filterKey].IsIdentical(_trackedFilters[iFilter]))
{
break;
}
}
// Get the filter information for the item and add it to the ItemChange object.
SyncVersion moveVersion = GetMoveVersion(itemMeta, iFilter);
// Only return a filter change if the destination knowledge does not contain the version of the
// last move that occurred in relation to the specified filter.
FilterChange filterChange = null;
if (!destKnowledge.Contains(ContactReplicaMetadata.ReplicaId, itemMeta.GlobalId, moveVersion))
{
filterChange = new FilterChange(GetIsInFilter(itemMeta, iFilter), moveVersion);
itemChange.AddFilterChange((uint)filterKey, filterChange);
}
}
}
필터 메타데이터 적용
변경 내용은 대상 공급자의 SaveItemChange 메서드를 호출하는 변경 내용 적용자를 사용하여 적용됩니다. 항목이 만들어지고 업데이트되면 이 예제는 연락처 저장소에 데이터를 만들거나 업데이트하고 동기화 메타데이터를 업데이트하며 필터 추적 메타데이터를 업데이트합니다. 필터 추적 메타데이터는 서로 추적되는 필터만이 아니라 대상 공급자에 의해 추적되는 모든 필터에 대해 업데이트됩니다. 필터 메타데이터를 원본 공급자가 전송한 경우 필터 메타데이터가 사용되지만 그렇지 않은 경우 변경 내용이 각 추적된 필터와 비교되어 적절한 필터 변경 내용 메타데이터가 설정됩니다.
public void UpdateContactFromSync(ItemChange itemChange, string changeData, FilterKeyMap providerFilterKeyMap)
{
if (!_ContactList.ContainsKey(itemChange.ItemId))
{
// The item does not exist, so create a new contact and add it to the contact and metadata store.
Contact contact = new Contact();
ItemMetadata itemMeta = _ContactReplicaMetadata.CreateItemMetadata(itemChange.ItemId,
itemChange.CreationVersion);
InitializeFilterTrackingFields(itemMeta);
_ContactList.Add(itemMeta.GlobalId, contact);
_ContactItemMetaList.Add(itemMeta.GlobalId, itemMeta);
}
_ContactList[itemChange.ItemId].FromString(changeData);
// Update the metadata for the item.
UpdateContactMetadataInternal(itemChange.ItemId, itemChange.ChangeVersion, itemChange, providerFilterKeyMap);
}
private void UpdateContactMetadataInternal(SyncId itemId, SyncVersion version, ItemChange itemChange, FilterKeyMap providerFilterKeyMap)
{
ItemMetadata itemMeta = _ContactItemMetaList[itemId];
// Set the value of all index fields in the metadata store.
itemMeta.SetCustomField(FirstNameField, _ContactList[itemId].FirstName);
itemMeta.SetCustomField(LastNameField, _ContactList[itemId].LastName);
itemMeta.SetCustomField(PhoneNumberField, _ContactList[itemId].PhoneNumber);
// Update the version metadata for the change unit.
itemMeta.ChangeVersion = version;
// Update the filter tracking metadata both for filter change metadata sent from the source provider and for
// any other filters tracked by this replica.
for (int iFilter = 0; iFilter < _trackedFilters.Count; iFilter++)
{
// Get filter change metadata from the source provider for this change, if it exists.
FilterChange filterChange = GetFilterChange(itemChange, iFilter, providerFilterKeyMap);
// If filter change metadata is present, use it to update the item metadata.
if (null != filterChange)
{
SetIsInFilter(itemMeta, iFilter, filterChange.IsMoveIn);
SetMoveVersion(itemMeta, iFilter, filterChange.MoveVersion);
}
// Otherwise, update the item metadata for other tracked filters.
else
{
UpdateFilterTrackingMetadata(itemMeta, iFilter, version);
}
}
}
// An item has been created or has changed, so update the filter tracking metadata for the item.
void UpdateFilterTrackingMetadata(ItemMetadata itemMeta, int iFilter, SyncVersion moveVersion)
{
// Determine whether the item is in the filter.
Contact contact = _ContactList[itemMeta.GlobalId];
bool isInFilter = _trackedFilters[iFilter].IsInFilter(contact);
// Determine whether the item was in the filter.
bool wasInFilter = GetIsInFilter(itemMeta, iFilter);
// If the filter membership has changed, update the filter tracking metadata.
if (isInFilter != wasInFilter)
{
SetIsInFilter(itemMeta, iFilter, isInFilter);
SetMoveVersion(itemMeta, iFilter, moveVersion);
}
}
변경 내용 적용자는 대상 공급자 IFilterTrackingNotifyingChangeApplierTarget 인터페이스의 메서드도 호출하여 필터 추적 메타데이터를 가져오고 적용합니다. 이 예에서는 요청된 개체를 반환하고 지정된 메타데이터를 저장합니다.
private FilterKeyMap _filterKeyMap;
public FilterKeyMap FilterKeyMap
{
get
{
return _filterKeyMap;
}
}
public ForgottenKnowledge GetFilterForgottenKnowledge(uint filterIndex)
{
if (filterIndex < _filterKeyMap.Count)
{
return ((AddressFilter)_filterKeyMap[(int)filterIndex]).FilterForgottenKnowledge;
}
else
{
throw new ArgumentOutOfRangeException("GetFilterForgottenKnowledge received and out-of-range index.");
}
}
public void SaveKnowledgeWithFilterForgottenKnowledge(SyncKnowledge syncKnowledge, ForgottenKnowledge forgottenKnowledge, ForgottenKnowledge[] filterForgottenKnowledge)
{
// First update the list of filter forgotten knowledge objects.
for (int iFilter = 0; iFilter < filterForgottenKnowledge.Length; iFilter++)
{
((AddressFilter)_filterKeyMap[iFilter]).FilterForgottenKnowledge = filterForgottenKnowledge[iFilter];
}
// Update the list of filters that are stored in the custom replica metadata.
AddressFilter.StoreFiltersInReplicaMetadata(_ContactStore.ContactReplicaMetadata, _ContactStore.TrackedFilters);
// Store the remaining knowledge objects.
StoreKnowledgeForScope(syncKnowledge, forgottenKnowledge);
}
필터 메타데이터 저장
복제본에 의해 추적되는 필터는 각 추적된 필터의 필터 잊어버린 정보와 함께 복제본에 저장되어야 합니다. 이 예제에서는 메타데이터를 저장하기 위해 Metadata Storage Service를 사용합니다. Metadata Storage Service는 사용자 지정 필터를 지원하지 않으므로 추적된 필터가 바이트 스트림으로 직렬화되어 메타데이터 저장소의 사용자 지정 복제본 메타데이터 필드에 저장됩니다.
class AddressFilter : ISyncFilter, ISyncFilterDeserializer
{
// For deserialization.
public AddressFilter()
{
_filter = null;
}
// A filter is a string that is compared against the Address field of a contact.
public AddressFilter(string filter)
{
_filter = filter;
}
public string Filter
{
get
{
return _filter;
}
}
// A contact is in the filter when the filter string is contained in the Address field of the contact.
public bool IsInFilter(Contact contact)
{
return contact.Address.Contains(_filter);
}
private string _filter;
public ForgottenKnowledge FilterForgottenKnowledge
{
get
{
return _filterForgottenKnowledge;
}
set
{
_filterForgottenKnowledge = value;
}
}
private ForgottenKnowledge _filterForgottenKnowledge;
#region ISyncFilter Members
// Two filters are identical when their filter strings are equal.
public bool IsIdentical(ISyncFilter otherFilter)
{
return _filter.Equals(((AddressFilter)otherFilter).Filter);
}
public byte[] Serialize()
{
MemoryStream memStream = new MemoryStream();
BinaryWriter biWriter = new BinaryWriter(memStream, Encoding.Unicode);
SerializeToBinaryWriter(biWriter);
return memStream.GetBuffer();
}
private void SerializeToBinaryWriter(BinaryWriter biWriter)
{
bool hasFilterForgottenKnowledge = (null != _filterForgottenKnowledge);
biWriter.Write(hasFilterForgottenKnowledge);
biWriter.Write(_filter);
if (null != _filterForgottenKnowledge)
{
byte[] serializedForgottenKnowledge = _filterForgottenKnowledge.Serialize();
biWriter.Write(serializedForgottenKnowledge.Length);
biWriter.Write(serializedForgottenKnowledge);
}
}
#endregion
#region ISyncFilterDeserializer Members
public ISyncFilter Deserialize(byte[] data)
{
MemoryStream memStream = new MemoryStream(data, 0, data.Length, false, true);
BinaryReader biReader = new BinaryReader(memStream, Encoding.Unicode);
DeserializeFromBinaryReader(biReader, memStream);
return this;
}
private void DeserializeFromBinaryReader(BinaryReader biReader, MemoryStream memStream)
{
bool hasFilterForgottenKnowledge = biReader.ReadBoolean();
_filter = biReader.ReadString();
if (hasFilterForgottenKnowledge)
{
int cbForgottenKnowledge = biReader.ReadInt32();
byte[] rawBuffer = biReader.ReadBytes(cbForgottenKnowledge);
_filterForgottenKnowledge = ForgottenKnowledge.Deserialize(ContactStore.ContactIdFormatGroup,
rawBuffer);
}
}
#endregion
// This implementation uses the metadata storage service to store metadata.
// The metadata storage service does not support custom filters, so store the filters
// that are tracked by a replica in the custom replica metadata field
// of the metadata store.
public static void StoreFiltersInReplicaMetadata(ReplicaMetadata repMeta, List<AddressFilter> filters)
{
MemoryStream memStream = new MemoryStream();
BinaryWriter biWriter = new BinaryWriter(memStream, Encoding.Unicode);
biWriter.Write(filters.Count);
foreach (AddressFilter filter in filters)
{
filter.SerializeToBinaryWriter(biWriter);
}
repMeta.CustomReplicaMetadata = memStream.GetBuffer();
}
public static List<AddressFilter> ReadFiltersFromReplicaMetadata(ReplicaMetadata repMeta)
{
MemoryStream memStream = new MemoryStream(repMeta.CustomReplicaMetadata, 0, repMeta.CustomReplicaMetadata.Length, false, true);
BinaryReader biReader = new BinaryReader(memStream, Encoding.Unicode);
int cFilters = biReader.ReadInt32();
List<AddressFilter> filters = new List<AddressFilter>(cFilters);
AddressFilter newFilter;
for (int iFilter = 0; iFilter < cFilters; iFilter++)
{
newFilter = new AddressFilter();
newFilter.DeserializeFromBinaryReader(biReader, memStream);
filters.Add(newFilter);
}
return filters;
}
public override string ToString()
{
return _filter;
}
}
각 항목은 필터에 있는지 여부와 필터에서 항목을 제외시킨 변경 내용의 버전을 추적합니다. 이 예제에서는 항목의 필터 추적 메타데이터를 메타데이터 저장소의 사용자 지정 항목 필드로 저장합니다.
// Allocate space for the filter tracking metadata for each tracked filter.
private void InitializeFilterTrackingFields(ItemMetadata itemMeta)
{
if (0 < _trackedFilters.Count)
{
byte[] newIsInFilterBytes = new byte[_trackedFilters.Count];
byte[] newMoveVersionBytes = new byte[_trackedFilters.Count * (sizeof(uint) + sizeof(ulong))];
itemMeta.SetCustomField(IsInFiltersField, newIsInFilterBytes);
itemMeta.SetCustomField(MoveVersionsField, newMoveVersionBytes);
}
}
// Gets a value that indicates whether the specified item is in the specified filter,
// according to the filter tracking metadata.
private bool GetIsInFilter(ItemMetadata itemMeta, int iFilter)
{
byte[] isInFilterList = itemMeta.GetBytesField(IsInFiltersField);
return (1 == isInFilterList[iFilter]);
}
// Sets a value that indicates whether the specified item is in the specified filter.
private void SetIsInFilter(ItemMetadata itemMeta, int iFilter, bool isInFilter)
{
byte[] isInFilterList = itemMeta.GetBytesField(IsInFiltersField);
isInFilterList[iFilter] = (byte)(isInFilter ? 1 : 0);
itemMeta.SetCustomField(IsInFiltersField, isInFilterList);
}
// Gets the version of the change that caused the specified item to move in relation
// to the specified filter.
private SyncVersion GetMoveVersion(ItemMetadata itemMeta, int iFilter)
{
// Get the raw bytes for the move version list.
byte[] moveVersionBytes = itemMeta.GetBytesField(MoveVersionsField);
// Read the SyncVersion elements from the specified location in the byte array.
MemoryStream memStream = new MemoryStream(moveVersionBytes);
memStream.Seek(iFilter * (sizeof(uint) + sizeof(ulong)), SeekOrigin.Begin);
BinaryReader biReader = new BinaryReader(memStream, Encoding.Unicode);
uint replicaKey = biReader.ReadUInt32();
ulong tickCount = biReader.ReadUInt64();
SyncVersion moveVersion = new SyncVersion(replicaKey, tickCount);
return moveVersion;
}
// Sets the version of the change that caused the specified item to move in relation
// to the specified filter.
private void SetMoveVersion(ItemMetadata itemMeta, int iFilter, SyncVersion moveVersion)
{
// Get the raw bytes for the move version list.
byte[] moveVersionBytes = itemMeta.GetBytesField(MoveVersionsField);
// Write the SyncVersion elements to the specified location in the byte array.
MemoryStream memStream = new MemoryStream(moveVersionBytes);
memStream.Seek(iFilter * (sizeof(uint) + sizeof(ulong)), SeekOrigin.Begin);
BinaryWriter biWriter = new BinaryWriter(memStream, Encoding.Unicode);
biWriter.Write(moveVersion.ReplicaKey);
biWriter.Write(moveVersion.TickCount);
itemMeta.SetCustomField(MoveVersionsField, moveVersionBytes);
}
// Set up fields used to track a new filter.
public bool StartTrackingFilter(AddressFilter filter)
{
bool filterIsNew = true;
foreach (AddressFilter addressFilter in _trackedFilters)
{
if (addressFilter.IsIdentical(filter))
{
filterIsNew = false;
break;
}
}
if (filterIsNew)
{
// Initialize the filter forgotten knowledge to the current knowledge of the replica.
filter.FilterForgottenKnowledge = new ForgottenKnowledge(ContactStore.ContactIdFormatGroup,
ContactReplicaMetadata.GetKnowledge());
_trackedFilters.Add(filter);
// Allocate new space for and initialize filter tracking metadata for all active items.
byte[] newIsInFilterBytes = new byte[_trackedFilters.Count];
byte[] newMoveVersionBytes = new byte[_trackedFilters.Count * (sizeof(uint) + sizeof(ulong))];
int iFilter = _trackedFilters.Count - 1;
foreach (ItemMetadata itemMeta in _ContactItemMetaList.Values)
{
// Get current filter tracking metadata, copy it to the new byte arrays, and store it.
byte[] isInFilterBytes = itemMeta.GetBytesField(IsInFiltersField);
byte[] moveVersionBytes = itemMeta.GetBytesField(MoveVersionsField);
if (null != isInFilterBytes)
{
isInFilterBytes.CopyTo(newIsInFilterBytes, 0);
}
if (null != moveVersionBytes)
{
moveVersionBytes.CopyTo(newMoveVersionBytes, 0);
}
itemMeta.SetCustomField(IsInFiltersField, newIsInFilterBytes);
itemMeta.SetCustomField(MoveVersionsField, newMoveVersionBytes);
// Initialize filter tracking metadata.
bool isInFilter = filter.IsInFilter(_ContactList[itemMeta.GlobalId]);
SetIsInFilter(itemMeta, iFilter, isInFilter);
if (isInFilter)
{
// If the item is in the filter, set the move version to the change version for the item.
// Otherwise, leave the move version as (0,0).
SetMoveVersion(itemMeta, iFilter, itemMeta.ChangeVersion);
}
}
// Update the list of filters that are stored in the custom replica metadata.
AddressFilter.StoreFiltersInReplicaMetadata(ContactReplicaMetadata, TrackedFilters);
}
return filterIsNew;
}
// Gets the list of address filters that are tracked by this replica.
public List<AddressFilter> TrackedFilters
{
get
{
return _trackedFilters;
}
}
private List<AddressFilter> _trackedFilters;
다음 단계
다음으로, 공급자가 대상 공급자와 통신하여 변경 내용 열거에 사용할 필터를 설정할 수 있도록 필터 협상 기능을 추가할 수 있습니다. 필터를 협상하는 방법에 대한 자세한 내용은 방법: 필터 협상을 참조하십시오.
사용하는 공급자가 대상 공급자일 때 필터링된 복제본을 나타내도록 활성화할 수도 있습니다. 필터링된 공급자를 구현하는 방법에 대한 자세한 내용은 방법: 복제본 필터링을 참조하십시오.