Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / IIS

Cloud over IFileSystemInfo extension (Cofe3) - Part II

0.00/5 (No votes)
17 Oct 2013LGPL320 min read 18.2K  
Framework library for designing RESTful home cloud services using FSI like interface.

Introduction

Cofe3 is a set of libraries that enables developers to build their home cloud services for their specific kind of entries, which enables the user to host them on their own computer, and using it to serve their own thin client computer like smart phone and tablet via the web browser or apps. This library is released under the LGPL license.

Index

This article is mainly written for describing the internal workings for Cofe3. To learn about how to use Cofe3, please read Part I instead.

Part II 

Features

Because I've been coding this for a while, I have no idea what others offer, this is a list of properties that I found interesting:

Not tied to file system

After so much work to make the properties cacheable in the database, accessible using web services, why limit it to only file system? That was a question I asked when the previous version of Cofe was mostly completed. So version 2 was scrapped and version 3 was rewritten. The result, Cofe.IO is separate implementation. Cofe3 can have multiple implementations at a time, all implementations can participate in path parsing, properties caching, and web exposing features like Cofe.IO.

Multiple source of properties

Just like FileSystemInfo in the .NET Framework, Cofe.IO provides properties related to I/O, for example, file size, stream, etc. But that is not sufficient because this is a general purpose library. So, we can define a secondary property provider to supply additional properties, like md5 of a file, image size, etc.

Entry implements multiple interfaces

One entry (ICofeSystemInfo) can support different interfaces, for examples, a zip archive is both IFileInfo and IDirectoryInfo, while a text file is just IFileInfo. This is definable in external projects, e.g. a jpg image is IFileInfo in Cofe.IO and IImageInfo in Cofe.Media.

Thanks to EFCodeFirst, properties in implementer of these interfaces can be written to database directly (e.g. dbo.FileInfo table),

Most properties are cachable and searchable

The reason for caching entries in database, beside improving listing speed, it allows one to search for entry using filter string. Cofe.Data added the tags and description property which makes personalization possible, and they are searchable too: e.g.
root:{pictures} filetype:jpg OR filetype:png subdir:true tags:cofe

Two interfaces - ICofeSystemInfo and RESTApi

Cofe's property based system enable the properties accessible locally (through ICofeSystemInfo) or via the web. The webapi returns Json which is suitable for consumption by the included javascript library (written with CoffeeScript, JQuery and Spine). Although I used the work "properties", it actually means both static values and runnable actions (in Actions, Funcs or Tasks), so one can CRUD entry and its resources, Executing an action using WebAPI, provided they are marked executable in web and sufficient permission.

Cofe3.Core 

Property

Image 1

Every piece of data, every runnable method in an entry (ICofeSystemInfo and it's sub-interfaces) are property, for examples, we have CofeProperties.ParseName, we have CofeStreamProperties.OpenReadStreamAsync.

A group of related properties are reside in same enum, each property have an unique number, which can be lookup or reverse lookup using CofeServices.PropertyDictionary.

One major reason of declaring them as enum is that I can define declarative attributes on them, which allows specifing output types (PRopertyType, Action, ListAction), how to obtain the value (GetFirst, HandleOnce, AggregateResult), additional check to output (FileSise, BitmapSize), alternative name (Alias), how to cache (CacheOptions) and how to expose via WebAPI (WebMetadata, WebResource and WebAction). Because they are frequently accessed, AttributeHelper (CofeServices.PropertyDictionary.AttributeHelper) provide cache access of these attributes.

It's annoying to pass dozens of individual properties around, so there is PropertyDefinitions. PropertyDefinitions is a generic class that takes an enum (e.g. CofeDirectoryProperies). By default the PropertyDefinition include all properties in the specific category, using another constructor can filter some away. Cofe/CofeDB/CofeWS all have PropertyDefinitions static class, which contains all definitions defined in that library. PropertyDefinitionExpression is for lookup a property in database.

A PropertyPair is a property and its value, PropertyPair.Value is in object type so you have to typed it using ValueAs<T>() method. PropertyPair.IsValid is for validating if the value matchs the PropertyTypeAttributes of the property.

PropertyDictionary

PropertyDictionary (CofeServices.PropertyDictionary) is a dictionary for all PropertyDefinitions and Properties, you can lookup PropertyDefinitions from a property (GetPropertyDefinition), you can convert among property object, it's id and it's alias string (GetProperty/GetPropertyString), it also contains AttributeHelper and PropertyInvoker.

PropertyDictionary.AttributeHelper  

AttributeHelper responsible for caching attributes in particular property, you can GetDefaultValue(), GetAlias(), GetProperty/DatabaseCacheOption() and FindAttribute<t>() from a property. The most notable attribute for a property is PropertyTypeAttribute, it's used to indicate what value does it return, if an invalid value is returned, the PropertyPair's IsValid return false and this will fail when SetProperty.

The most common use of PropertyTypeAttribute is to specify using typeof(T) and a default value, ActionAttribute derived PropertyTypeAttribute is used to represent an action, while ListActionAttribute and StreamActionAttribute derived from ActionAttribute is an action that return a specialized type.

In Cofe both Bitmap and Stream are embedded in a container (BitmapContainer and StreamContainer), this is intended to allow one code to run both on Desktop and RT runtime, even one run using System.IO while another running WindowsStorage.

PropertyDictionary.PropertyInvoker 

Image 2

Given the output type, PropertyPair and ParameterDic, PropertyInvoker's Invoke<T>() and InvokeAsync<T>() (or without <T>) invoke an action/func or task in PropertyPair, and return the output value if there's one, in some case, the input ParameterDic also contain output value. PropertyInvoker can invoke an async property in sync mode or vice versa.

Take a look to the following, while the first and the second invokes an action from an entry, the third group actually get the action from the entry and use PropertyInvoker to execute it. The last one cast the action to appropriate output and run it as a task.

Image 3

EntryFilters

When listing in System.IO, you use a mask to indicate what you want to return, and SearchOption to indicate whether to search subdirectory. In Cofe, because of so many properties, you use EntryFilters. EntryFilters is used to filter a set of entries using a property. Consider a scenario like:

/api/parse?path={cofe}&rel=list&filterString=name:abc OR name:cde type:txt OR type:cs OR type:pas size:>100 subdir:true

in web url, after parsing using IFilterParser, it converts to:

Image 4

When database is not enabled, EntryFilter's Match() method is used to return whether CurrentEntry is matched, and in the case of directory it returns whether to search sub-directory as well. Most property uses PropertyEntryFilter, which obtain the specified value from the entry. PropertyEntryFilter support < and > operator for numbers and datatime.

While Matching sub-entries (whether to search subdirectory for entries) is OR based, matching entries in EntryFilters are matched in AND based, that means the entry is matched only if all EntryFilters return Yes. When or is needed, one have to embed it into OrEntryFilter, e.g. new OrEntryFilter(new NameEntryFilter("abc"), new NameEntryFilter("cde")).

When working with database, this becomes impossible, consider if I want to search the whole database for certain criteria, I'll have to materialize every entry in database and compare its values. This is the reason that in every PropertyDefinitions theres a PropertyExpressionDefinition, which provides expression (Func<CofeSystemInfo, T>) to lookup a variable in database. By default all properties is lookup from the PropertyPair_{type} table (where type depended on property type), if the property is cached in ORM, (e.g. ImageProperties.CameraMaker), it have to be specify explicitly, for examples:

Image 5

EntryOptionFilterParser

EntryFilterParser (CofeServices.EntryFilterParser) is responsible to convert a Filter string to Entry / OptionFilters and vice versa, it construct filter using it's FilterConstructor.TryConstruct<T>() method, which ask lookup the appropriate IFilterFactory and construct from there. EntryFilterParser also contains PropertySerializer which responsible to convert a Key/Value pair to/from string.

Entry (CofeSystemInfo) 

Image 6

Entry in Cofe usually indicate the interface ICofeSystemInfo and it's child interface like IFileSystemInfo, IMediaInfo and IEntryTypeInfo. The base implementation of them is CofeSystemInfo.

CofeSystemInfo and it's sub-implementations, like Cofe.IO.FileInfo, has two purpose. One is to implement the interfaces so user can access the properties and methods through the interfaces. The another is to participate in EntityFramework CodeFirst, so some of the properties can be written as named column in appropriate table, e.g. FileInfo(Id, Type, FileAttributes, Md5), instead of writing in a typed table, e.g. PropertyPair_Int16 (PropertyId, Id, ORMValue).

Unlike System.IO, Cofe is designed to support other third-party plugins so it's not possible for a implementer to implement all possible interfaces, for examples, Cofe.IO support compression archives (via SevenZipSharp), so a FileInfo may be expected to implement IDirectoryInfo. This is not possible so I added a mechanism called IDynamicInterface, which adds Is<T>() and As<T>() methods to CofeSystemInfo, so if you call zipFile.As<IDirectoryInfo>(), you gets a DirectoryInfo which implements IDirectoryInfo.

The construction of PropertyHost and specific type of ICofeSystemInfo is done in CofeServices.IEntryContructor. Converting a PropertyProvider to IPropertyHost involves creating of property cache and querying secondary property providers, while Converting IPropertyHost to ICofeSystemInfo involves querying the dynamic interface provider to create the appropriate container.

This may sound strange because the FileInfo shouldn't contain properties required in DirectoryInfo. The implementer of an entry is actually a skin. If you open FileInfo.cs you can find something like the following in the getter of a property:

return Properties.Behaviors.getProperty<string>(CofeStreamProperties.Md5).

The Properties shown above is a IPropertyHost, which contains the cached variables and the means of getting, setting and invoking a noncached variables, so the process casting from File to DirectoryInfo is just create a blank DirectoryInfo and assign the PropertyHost object.

PropertyHost

PropertHost is a host of all supported PropertyProvides, which includes PropertyCache, Primary and Secondary PropertyProviders. It also contains PropertyBehaviorManager (IPropertyHost.Behaviors), which responsible for getting, setting and invoking properties.

There is one PrimaryPropertyProvider per PropertyHost, and it must provides all basic required properties for the entry to be functional, including ParseName and Type. In CofeIO implementation it's the SystemIOPropertyProvider (or WindowStoragePropertyProvider) that talks with underlying SystemIO.

PropertyHost exists in a connected state or disconnected state. When connected, PrimaryPP is assigned and the general property access (get/set) strategy will be Cache -> Primary -> SecondaryPPs. When disconnected, PrimaryPP is null and the sequence becomes Cache -> SecondaryPPs -> Connect() -> PrimaryPP. This allows properties from database written to PropertyCache and access from there without connecting to the backstore.

SeconaryPropertyProviders (SeconaryPPs) are generated when PropertyHost is constructing, it ask every ISecondaryPropertyProviderFactory if they can create new PropertyProviders for this IPropertyHost. For examples, ExifPropertyProvider is attached when name is end with .jpg and CofeStreamProperties.OpenStreamAsync is supported.

PropertyBehaviorManager responsible for access (get/set) and invoke of property. Based on InvokeBehaviorAttribute of the property, which included HandleOnes, ReturnMin/MaxOnly and AggregateResult), it create different IGetBehavior or ISetBehavior to handle the request. For invocation, it first get the property, depended on how it's defined in the attribute, it can be Action<PD>, Func<PD, T>, Func<PD, Task> or Func<PD, Task<T>>, depended on sync/async and whether have return value T. PD is ParameterDic, it is a string object dictionary, used for both passing input parameters and sometimes some additional output like IsHandled, or in the case of listing with pagination, TotalPages and TotalItems. Although ParameterDic is a string object dictionary, in CofeWS it support string parameter in url only.

Implementation: 

The implementation of entry properties are really verbose, if you want to implement your own entry types and properties, you have to:

  1. Define a property enum (e.g. CofeProperties), create property definition (CofePropertyDefinitions, which is PropertyDefinitionBase<CofeProperties>)
  2. Create PropertyProvider to return values (Primary for new entry types, Secondary for existing entry types) 
  3. Define new interface (e.g. IFileInfo) and the means to provide the interface (IDynamicInterfaceProvider)
  4. Define the implements (FileInfo), participate in the Database table construction (IDatabaseConfigurator) and EntityFramework Migration.
  5. Define the volume factories and diretory listers.

I hope this can be automated a bit in future.

EntryList 

Image 7

EntryList is for representing multiple entries, it's inherited from ICofeDirectoryInfo so you can use it's GetCofeSystemInfosAsync() or EnumerateCofeSystemInfos() methods to access it contents (actual content in auto, link in custom)

There are two kinds of entry lists:

Custom entry lists allow user to specify the content of an Entrylists by adding them manually. User calls ICustomEntryList.AddLink to link an entry to a CustomEntryList, which will create an IEntryLink object that have it's Entry property linked to the actual target entry. When database is enabled, this link is represented in DatabaseEntry, just like an entry. It's possible to create an entryLink to another CustomEntryList, this is what happened when you called IEntryList.CreateLinkFolder() method, it calls createAsync() and return an IEntryLink.

CustomEntryList can be created using EntryFE.NewEntryList() method, once it is make parsable using EntryListFE.MakeParsable() method, it is accessible as a volume.

AutoEntryList means the entries are automatically polled, using Entry and Option Filters. It's mainly for use in search.

AutoEntryList can be created using EntryFE.SearchAsync() method with a filter string, the filter string is passed to IEntryOptionFilterParser to convert into Entry and OptionFilters.

EntryTypeInfo

There is a SystemVolume named {EntryTypeInfo} when Cofe starts, it represent the parent directory of all EntryTypes. EntryType is a way to define properties for broad range of entries,one entry can have one entry type only. In database, it's stored as a field in DatabaseEntry.

When an EntryTypeInfo is requested via calling CofeEntryTypeProperties.EntryTypeKey or Info provided by EntryTypeSecondaryPP, it actually calls EntryTypeInfoManager.GetEntryTypeKey() method, which use it's IEntryTypeIdentifier to locate EntryTypeKey and IEntryTypeProvider to create PrimaryPP for the EntryType. It's then passed to EntryConstructor to construct as PropertyHost and entry.

Because EntryTypeInfo is an entry, other external libraries can attach secondary PropertyProviders to the resulting PropertyHost.

TransferManager

Image 8

Except CofeDirectoryProperties.CreateAsync and CofeEntryLinkProperties.AddLink, entry itself do not provide commands for transferring entries. Instead, CofeServices.TransferManager is used for transfering entries to a directory. TransferManager has Transfer/RenameAsync() methods, when called, look for appropriate ITransferHelper to do the transfer. The default transfer included CopyDirectory, CopyStream, RenameEntry and LinkEntry, which do the transfer by entry by entry based.

Volume

Image 9

Similar to System.IO, Volumes in Cofe are root root directories of every entry. But unlike SystemIO, whcih every volume represent a drive letter, Cofe does not have concept of drive, it's volumes are defined by it's implementation. For CofeIO, one use root directory as a volume. Because of this, when Cofe is started, there's a system volume (EntryTypeInfo), but there's no non-system volume in CofeServices.VolumeRegistrar, a place which keep track of registered volumes and volume factories.

VolumeFactories are defined by it's implementation, Cofe3 has EntryListVolumeFactory for registering entrylist as volume, while CofeIO has SystemIO/WindowsStorageVolumeFactory for registering volume as disk path. If a volume is registered (e.g. using VolumeFE.MapAsync("CofeIO", "doc", Tuple.Create("VolumePath", RootPath))), a Volume is created with VolumeInfo, DirectoryLister and EventSources. which after that {doc} directory can then be parsable as CofeDirectory.

DirectoryLister is a class that provide the root PropertyProviders of a volume, and parse a partial path (a path without volume id) and return appropriate PrimaryPropertyProvider if found. EventSources is for monitoring events happened in the particular file system, when the volume is registered, it's event source will be registered in EventHub so when the source raise a event, it passed to the hub and distribute to the listeners.

CofeSettings

The registed VolueInfos can be reused when the application is restarted if it's stored in CofeSettings. CofeServices.CofeSettings is a central place to store all settable settings (there's also ICofeWsSettings too, which inherits ICofeSettings), one can update the property and call CofeServices.SaveAsync(), which will notify all ISaveSettings implementers to save settings to CofeSettings, and all settings will be serialized in _cofe.xml in IsolatedStorage folder. And when CofeServices.RestoreAsync() is called, it will deserialized the settings and notify all IRestoreSettings implmenters to restore settings. Volume(IVolume) are stored as IVolumeInfo, which contains FactoryId, VolumeId and parameter only.

Security

Security can be setup on user role and volume only, not it's sub-directory, so if you want to assign different role requirement for different sub-directory, you will have to create multiple volumes. VolumeFE helper contains GrantPermission(role, volumeId, permission) and RevokePermission(), which calls CofeServices.SecurityManager.Add/RemovePermissionPolicy() with appropriate parameters.

When an entry is parsed, listed, a property or action is called or invoked, CofeServices.SecurityManager.DemandPermission<ivolumeinfo>() is called, and SecurityManager will evaluate it's PermissionPolicy. If there's any match, it will check PermissionPolicy's Required role, and check if the role is included in ISecurityManager.CurrentPrincipal, which is generated by ISecurityManager.UserManager.GetPrincipal(CurrentThread.Identity).

Reading, Writing and Invoking normally require respective permission (R/W/E), but it can be overrided by using RequirePermirrionAttribute. The check take place mostly in PropertyBehaviorManager. The security is designed to deny unauthorize access on web only; after all, if someone has access to the source code they don't need Cofe to access the data they wanted.

While SecurityManager holds IPermissionPolicy, UserManager holds IPrincipalPolicy, which defined what role is given to what identity, one can use AddPrincipalPolicy() and RemovePrincipalPolicy() to update the PrincipalPolicies.

The main use of security is in CofeWs. In WSUserManager, if the user identity is in Forms Authentication, UserManager calls ASP.Net's SimpleRoleProvider for roles, thus you just need to manager the roles in ASP.Net instead of setting PrincipalPolicies.

Both IPermissionPolicy and IPrincipalPolicy are saved in CofeSettings.

Events

Event handling for Cofe system changes is important because to enable CofeSystemWatcher to report when there's an event, to keep database updated, and to keep the feed system work, it has to be notified when there's a change.

In Cofe, event handling is done by EventHub, it's a hub that accepts an event and broadcasts multiple events in an interval. On one side, we can use its RegisterEventSource<T>() to register an IEventSource<T>, which raises a specific type of EventArgs. For example, each Volume may contain one or more EventSources to raise a CofeSystemEvent when an item in the volume is changed, (e.g., SystemIOEventSource). The default implementation of Volume is ManualVolumeSource, which allows the user to raise an event by calling the RaiseChange/Created/Renamed/Deleted() methods.

On the other side, we can use its RegisterEventListener<T>() to register an IEventListenener<T>. EventHub routinely broadcasts received events to the listeners every n seconds using IEventListener's DisplatchEvent().

Both IEventSource and IEventListener can unregister itself by using their CancellationToken.

A list of EventListener implementations:

  • CofeSystemWatcher - Just like FileSystemWatcher, there's a CofeSystemWater to raise an event when CofeSystem changes, CofeSystemWatcher only registers itself to the EventHub when EnableRaisingEvents is set to true.
  • CofeDBEventListener - CofeDBEventListener has an IEventListenerFactory which enables it to be registered when the first event is dispatched; it refreshes the LastUpdateTime/LastListTime of the parent of the affected entry, expires the affected entry, and updates the entry if CacheUpdateOption is Passive.
  • FeedServices - CofeWsServices.FeedServices writes a fee periodically to the \log directory using ATOM RSS format. 

CofeServices

Image 10

CofeServices is a static class that provide to all ICofeServices. IEntryConstructor, IPathParserManager, and ISecurityManaager are all ICofeServices. CofeServices contains ServiceLocater, which is responsible for holding and querying ICofeServices.

BootStrapper

The BootStrapper registers all related ICofeServices to the ServiceLocater. ManualBootStrapper and MEFBootStrapper are now replaced by ModuleBootStrapper; all libraries contain a class named RegisterModule, which contains the ICofeServices in that library, all you need is to include these RegisterModules in the constructor of the BootStrapper (sequence doesn't matter, as ICofeServices are sorted when imported) and invoke its Run() method.

new ManualBootStrapperIODB(false, false).Run();     //Obsoluted
new ModuleBootStrapper(
        true,
        new Cofe.Core.RegisterModule(),
        new Cofe.Data.RegisterModule(true, CacheUpdateOptions.Manual),
        new Cofe.IO.RegisterModule(),
        new Cofe.Media.RegisterModule()
    ).Run();     

Beside CofeServices, there's CofeDBServices and CofeWsServices as well, which provide services defined in Cofe.Data and Cofe.Web.

Cofe3.Data

Image 11

CofeDB3 is responsible for caching entries in the database, allow parsing, listing, and searching entries in the database.

IRepository (Pending to be refactored)

When you call the DatabaseProperties.WriteRepositoryAsync action to add or update an entry to the database, you are actually calling IRepository.AddOrUpdate(). IRepository talks with the database implementation and it provides update, lookup, and remove for DatabaseEntry.

CofeDBServices.RepositoryFactory is for constructing an IRepository, which is responsible for reading, writing, and removing entries (in the PropertyHost form) to and from the database. A factory is required because each copy of EFRepository, the implementation of IRepository that uses EntityFramework, contains a DbContext, which must be released when EFRepository is disposed.

using (var Repository = RepositoryFacoty.CreateRepository()) {... }

Image 12

DatabaseEntry

Because one entry may have more than one implementation, the most logical way is to have a common parent class to hold these implementations, and this is DatabaseEntry. This class is defined in Cofe.Core, and contains implementation which contains CofeSystemInfos, and ExtendedProperties, which contains non-ORM (Object Relational Mapping, e.g., FileInfo.Length) property values. To reduce redundancy, properties in CofeSystemInfo, like ParseName, Label, CofeAttributes, are moved to DatabaseEntry.DatabaseEntryFactory.

But how to construct these DatabaseEntrys? CofeDBServices.DatabaseEntryFactory provides a bi-directional way to construct them.

When constructing from IPropertyHost to DatabaseEntry (to EF), DatabaseEntryFactory asks PropertyInterfaceManager for each castable type, and creates an implementation (e.g., FileInfo) when applicable. This part is easy because it's the EF side that needs only the ORM property for each implementation.

When constructing from DatabaseEntry (from EF) to IPropertyHost, each implementation has a PropertyCache that only caches the ORM properties, while the non-ORM properties are in ExtendedProperties. DatabaseEntryFactory has to group the PropertyCache into one, write the properties in the root DatabaseEntry and ExtendedProperties to the PropertyCache.

DatabaseConfigurator

The DbContext of Cofe is CofeDbContext, to allow an external library to participate when constructing the model and database, CofeDbContext loops through each IDatabaseConfigurator when ModelCreating. The construction of the CofeDbContext initiator is best done through CofeDbContext.Initializer, which uses migration to create the database.

Image 13

I have manually modified the migration file after it's automatically generated, added cascade delete to the child classes of CofeSystemInfo and PropertyPair so a child row is deleted with the parent row, instead of complaining a child row is linking the parent row. I also added a SQL statement which makes DatabaseEntry.ParseName unique.

A list of commands in Migrations\CommandLine.txt is available for how to generate migrations and use it to update a database manually.

Cofe3.Web

Image 14

Cofe3.Web provides Feed (FeedServices) and Serialization/Deserialization services (HttpMessageFormatterManager) to Cofe.Web.Services, while the Cofe.Web.Services library is an ASP.NET application that includes the APIController for serving the web service.

FeedServices 

Use CofeWsServices.Feeds.GetFeedServices<T>() to access the IFeedServices<T> object, which contains anything related to a feed for a particular (T) typed EventArgs. FeedServices contains FeedFileSystemHelper, FeedUpdater, FeedBuilder, FeedLinkHelper, and FeedRepository.

FeedFileSystemHelper 

FeedServices uses FeedFileSystemHelper as a class for providing IO access, the actual path of feeds is also defined in this class. 

FeedUpdater 

When FeedServices.Updater (IFeedUpdater) is started, it listens to events from EventHub and writes to its EventBuffer, which stores EventLog<EventArgs>. Once every n seconds, it calls its Update thread, which Runs the script command QueryingEventsCommand<T> shown on the diagram. When this command completes it will emit IdentifyingRecentEventFeedsCommand<T>, and it will be run and so on, until NotifyingListeners<T>, which will emit ResultComamnds.OK, and thus stops the script command. This implementation is taken from the book, REST in Practice.

FeedBuilder 

FeedBuilder is responsible for loading and building Feed<T>. There is always a RecentEventFeed and ArchiveFeeds, where if RecentEventFeed is full it is copied as an ArchiveFeed, and a new RecentEventFeed is created. These feeds are stored in \log\current (as {FeedGuid}.atom) and \log\archive (as {Feed#}.atom) as an ATOM file by default. For each feed item added, the entry is written separately in \log\entry (as {EntryGuid}.atom).

The Feed object contains the FromFileName(), FromFeedId(), and Save() methods to load and save the SyndicationFeed object.

FeedLinkHelper 

Given LinkType and Link formation, FeedLinkHelper creates a SyndicationLink object for adding to the result SyndicationFeed, based on the template defined in FeedBuilder. Creatable links include Entry (CofeSystemInfo), Event (SyndicationItem), and Feed (SyndicationFeed).

FeedRepository 

FeedRepository is for external classes to access an Event or Feed stored in memory and file system.

FeedRepository returns a SyndicationItem or Feed, we can serialize the object itself, or use the included CofeServices.HttpMessageFormatter to create an HttpResponseMessage.

HttpMessageFormatterManager 

CofeServices.HttpMessageFormatter is responsible for both Serializing and Deserializing an entry to HttpResponseMessage or from HttpRequestMessage, which are used in WebAPI. The manager class is an interface class, the actual conversion is done by the implemented IHttpRequestMessageFormatter and IHttpResponseFormatter.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)