まあ、ペッターの解決策は素晴らしいです。
私の実装を共有するだけで、私の質問に再び答えてくれるはずです。評価し、コメントしてください。
public interface IRepository<T>
{
IEnumerable<T> GetAll();
IEnumerable<T> GetAll(object parentId);
T GetByKey(object keyValue);
void Insert(T entidade, bool autoPersist = true);
void Update(T entidade, bool autoPersist = true);
void Delete(T entidade, bool autoPersist = true);
void Save();
}
そしてXMLリポジトリ
public abstract class XmlRepositoryBase<T> : IRepository<T>
{
public virtual XElement ParentElement { get; protected set; }
protected XName ElementName { get; private set; }
protected abstract Func<XElement, T> Selector { get; }
#endregion
protected XmlRepositoryBase(XName elementName)
{
ElementName = elementName;
// clears the "cached" ParentElement to allow hot file changes
XDocumentProvider.Default.CurrentDocumentChanged += (sender, eventArgs) => ParentElement = null;
}
#region
protected abstract void SetXElementValue(T model, XElement element);
protected abstract XElement CreateXElement(T model);
protected abstract object GetEntityId(T entidade);
#region IRepository<T>
public T GetByKey(object keyValue)
{
// I intend to remove this magic string "Id"
return XDocumentProvider.Default.GetDocument().Descendants(ElementName)
.Where(e => e.Attribute("Id").Value == keyValue.ToString())
.Select(Selector)
.FirstOrDefault();
}
public IEnumerable<T> GetAll()
{
return ParentElement.Elements(ElementName).Select(Selector);
}
public virtual IEnumerable<T> GetAll(object parentId)
{
throw new InvalidOperationException("This entity doesn't contains a parent.");
}
public virtual void Insert(T entity, bool autoPersist = true)
{
ParentElement.Add(CreateXElement(entity));
if (autoPersist)
Save();
}
public virtual void Update(T entity, bool autoPersist= true)
{
// I intend to remove this magic string "Id"
SetXElementValue(
entity,
ParentElement.Elements().FirstOrDefault(a => a.Attribute("Id").Value == GetEntityId(entity).ToString()
));
if (persistir)
Save();
}
public virtual void Delete(T entity, bool autoPersist = true)
{
ParentElement.Elements().FirstOrDefault(a => a.Attribute("Id").Value == GetEntityId(entity).ToString()).Remove();
if (autoPersist)
Save();
}
public virtual void Save()
{
XDocumentProvider.Default.Save();
}
#endregion
#endregion
}
}子エンティティへ
そして2つのより抽象クラス、一つの独立した事業体へと他の基底クラス。 XMLファイルを毎回読ん避けるために、私は今、キャッシュ制御の種類
public abstract class EntityXmlRepository<T> : XmlRepositoryBase<T>
{
#region cache control
private XElement _parentElement;
private XName xName;
protected EntityXmlRepository(XName entityName)
: base(entityName)
{
}
public override XElement ParentElement
{
get
{
// returns in memory element or get it from file
return _parentElement ?? (_parentElement = GetParentElement());
}
protected set
{
_parentElement = value;
}
}
/// <summary>
/// Gets the parent element for this node type
/// </summary>
protected abstract XElement GetParentElement();
#endregion
}
子タイプ
public abstract class ChildEntityXmlRepository<T> : XmlRepositoryBase<T>
{
private object _currentParentId;
private object _lastParentId;
private XElement _parentElement;
public override XElement ParentElement
{
get
{
if (_parentElement == null)
{
_parentElement = GetParentElement(_currentParentId);
_lastParentId = _currentParentId;
}
return _parentElement;
}
protected set
{
_parentElement = value;
}
}
/// <summary>
/// Defines wich parent entity is active
/// when this property changes, the parent element field is nuled, forcing the parent element to be updated
/// </summary>
protected object CurrentParentId
{
get
{
return _currentParentId;
}
set
{
_currentParentId = value;
if (value != _lastParentId)
{
_parentElement = null;
}
}
}
protected ChildEntityXmlRepository(XName entityName) : base(entityName){}
protected abstract XElement GetParentElement(object parentId);
protected abstract object GetParentId(T entity);
public override IEnumerable<T> GetAll(object parentId)
{
CurrentParentId = parentId;
return ParentElement.Elements(ElementName).Select(Selector);
}
public override void Insert(T entity, bool persistir = true)
{
CurrentParentId = GetParentId(entity);
base.Insert(entity, persistir);
}
public override void Update(T entity, bool persistir = true)
{
CurrentParentId = GetParentId(entity);
base.Update(entity, persistir);
}
public override void Delete(T entity, bool persistir = true)
{
CurrentParentId = GetParentId(entity);
base.Delete(entity, persistir);
}
}
今、現実世界の実装
public class RepositorioAgendamento : EntityXmlRepository<Agendamento>, IRepositorioAgendamento
{
protected override Func<XElement, Agendamento> Selector
{
get
{
return x => new Agendamento() {
Id = x.Attribute("Id").GetGuid(),
Descricao = x.Attribute("Descricao").Value,
TipoAgendamento = x.Attribute("TipoAgendamento").GetByte(),
Dias = x.Attribute("Dias").GetByte(),
Data = x.Attribute("Data").GetDateTime(),
Ativo = x.Attribute("Ativo").GetBoolean(),
};
}
}
protected override XElement CreateXElement(Agendamento agendamento)
{
agendamento.Id = Guid.NewGuid();
return new XElement(ElementName,
new XAttribute("Id", agendamento.Id),
new XAttribute("Descricao", agendamento.Descricao),
new XAttribute("TipoAgendamento", agendamento.TipoAgendamento),
new XAttribute("Dias", agendamento.Dias),
new XAttribute("Data", agendamento.Data),
new XAttribute("Ativo", agendamento.Ativo),
new XElement(XmlNames.GruposBackup),
new XElement(XmlNames.Credenciais)
);
}
protected override void SetXElementValue(Agendamento modelo, XElement elemento)
{
elemento.Attribute("Descricao").SetValue(modelo.Descricao);
elemento.Attribute("TipoAgendamento").SetValue(modelo.TipoAgendamento);
elemento.Attribute("Dias").SetValue(modelo.Dias);
elemento.Attribute("Data").SetValue(modelo.Data);
elemento.Attribute("Ativo").SetValue(modelo.Ativo);
}
public RepositorioAgendamento() : base(XmlNames.Agendamento)
{
}
protected override XElement GetParentElement()
{
return XDocumentProvider.Default.GetDocument().Elements(XmlNames.Agendamentos).First();
}
protected override object GetEntityId(Agendamento entidade)
{
return entidade.Id;
}
public IEnumerable<Agendamento> ObterAtivos()
{
return ParentElement.Elements()
.Where(e => e.Attribute("Ativo").GetBoolean())
.Select(Selector);
}
}
の実装を作りました
そして、XDocumentProvider。その機能は、XMLファイルへのアクセスを抽象化し、すべてのリポジトリにXDocumentがデータコンテキストであることを統一することです。 これをUnitOfWorkという名前にすることができますか?
public abstract class XDocumentProvider
{
// not thread safe yet
private static bool pendingChanges;
private bool _documentLoadedFromFile;
FileSystemWatcher fileWatcher;
public static XDocumentProvider Default;
public event EventHandler CurrentDocumentChanged;
private XDocument _loadedDocument;
public string FileName { get; set; }
protected XDocumentProvider()
{
fileWatcher = new FileSystemWatcher();
fileWatcher.NotifyFilter = NotifyFilters.LastWrite;
fileWatcher.Changed += fileWatcher_Changed;
}
void fileWatcher_Changed(object sender, FileSystemEventArgs e)
{
if (_documentLoadedFromFile && !pendingChanges)
{
GetDocument(true);
}
}
/// <summary>
/// Returns an open XDocument or create a new document
/// </summary>
/// <returns></returns>
public XDocument GetDocument(bool refresh = false)
{
if (refresh || _loadedDocument == null)
{
// we need to refactor it, but just to demonstrate how should work I will send this way ;P
if (File.Exists(FileName))
{
_loadedDocument = XDocument.Load(FileName);
_documentLoadedFromFile = true;
if (fileWatcher.Path != Environment.CurrentDirectory)
{
fileWatcher.Path = Environment.CurrentDirectory;
fileWatcher.Filter = FileName;
fileWatcher.EnableRaisingEvents = true;
}
}
else
{
_loadedDocument = CreateNewDocument();
fileWatcher.EnableRaisingEvents = false;
_documentLoadedFromFile = false;
}
if(CurrentDocumentChanged != null) CurrentDocumentChanged(this, EventArgs.Empty);
}
return _loadedDocument;
}
/// <summary>
/// Creates a new XDocument with a determined schemma.
/// </summary>
public abstract XDocument CreateNewDocument();
public void Save()
{
if (_loadedDocument == null)
throw new InvalidOperationException();
try
{
// tells the file watcher that he cannot raise the changed event, because his function is to capture external changes.
pendingChanges = true;
_loadedDocument.Save(FileName);
}
finally
{
pendingChanges = false;
}
}
}
}
それから私は、単一のデータコンテキストのペンダント永続アクションを追加diferentエンティティのための多くのリポジトリを持つことができます。
私は、このレポジトリをモックを使用してうまく機能するアプリケーションのテストを行いました。
私のIoC設定では、XDocumentProviderのデフォルトを設定する必要があります。必要に応じて、この静的な "Default"プロパティの代わりにXDocumentProvider throughtコンストラクタを渡すことができます
私の実装についてどう思いますか?
おかげ
私はちょうどそれを明確に、この実装は時間前に行われたと、今日XMLにLINQを使用してのように、それを行うには良い方法があることをできるようにしたい
。 今日、この回答を確認したところ、今すぐ改善できることがわかりました。いつか私がそれを必要とすれば、私はここで実装し、投稿します。ご支援ありがとうございます –