1

I have a requirement to build a 'base' profile library (in C# - NET 4.5.2) for various different internal (technical) consumers. This profile consists of metadata fields that would be shared by a wide-variety of different profile implementations. The metadata for these different profiles is stored in a single location but each actual implementation for the base profile will have it's own data storage (which I'll know nothing about).

Rather than provide an actual service we will only be providing the base classes / interfaces necessary to allow developers to implement their own profile services.

Ideally, I'd like to make the hydration of the base profile's metadata fields completely automatic for developers - they focus on hydrating their own fields in their child class only.

My requirements are:

  • Base profile all other profile objects must inherit
  • Base profile self-hydrates from a static data source that is opaque to implementors.
  • The ability to query for many profiles based on a predicate, a profile ID list, or both.
  • CRUD operations for a given profile that permit said operations on the base profile metadata in addition to whatever the user needs to do in their own system(s).

On idea that has been floated already includes allowing developers to provide a generator function (Func<Guid, Profile<T>>) where Guid is the Id of the profile (for existing reads) and the function executes whatever logic the developer requires to initialize their profile while at the same time getting the base profile data.

I'd be curious to hear the communities thoughts on this though (and whether or not this is even a good course to chart).

1 Answer 1

1
+50

I recently made an implementation of the ASP.NET 5 Identity-framework which was a pain in the butt, it has its UserStore (and RoleStore) which is kind of annoying to implement.

My implementation can be found here, it might give you a few ideas!


This sounds sort of like the same thing. You need a base class where different type of profiles(?) can be stored, but only abstract methods for getting/setting these profiles. I kind of enjoy the pattern that the Identity base-classes provides, even though it's kind of annoying to implement, because it gives the implementer full freedom of what data-store (s)he wants to use.

From what i can tell from your question, each profile must have an Id which must be a System.Guid.

So why not start with an interface containing only this?

public interface IProfile
{
    Guid Id { get; }
}

Then why not implement a Store for the IProfile, such as in Identity

public abstract class ProfileStore<TProfile>
    where TProfile : class, IProfile
{
    public abstract Task<TProfile> FindByIdAsync(Guid profileId);
    public abstract Task<IEnumerable<TProfile>> FindManyAsync(Expression<Func<TProfile, bool>> query);
    public abstract Task SaveProfileAsync(TProfile profile);
}

You might (of course) want to add more methods of getting or saving your profiles.

All profile stores could then be added to some sort of ProfileStoreCollection where you can self-hydrate all stores by iterating over them, and invoking the InsertAsync-method.


This should be sufficient for what you are asking, please comment if i have missed some vital part!


Here is an example implementation (Using MongoDB and its latest driver):

A profile:

public class SomeProfile : IProfile
{
    [BsonId]
    public Guid Id { get; set; }

    public string Name { get; set; }

    public DateTime Expires { get; set; }
}

The store:

public class MongoDBProfileStore : ProfileStore<SomeProfile>
{
    private IMongoCollection<SomeProfile> _profileCollection;

    public MongoDBProfileStore(IMongoCollection<SomeProfile> profileCollection)
    {
        _profileCollection = profileCollection;
    }

    public override async Task<SomeProfile> FindByIdAsync(Guid profileId)
    {
        var filter = Builders<SomeProfile>.Filter
            .Eq(nameof(SomeProfile.Id), profileId);
        // the nameof-keyword is new in C#6 (returns "Id")

        var profiles = await _profileCollection.FindAsync(filter);
        await profiles.MoveNextAsync();

        if (profiles.Current != null)
        {
            return profiles.Current.FirstOrDefault();
        }
        return null;
    }

    public override async Task<IEnumerable<SomeProfile>> FindManyAsync(Expression<Func<SomeProfile, bool>> query)
    {
        var profiles = await _profileCollection.FindAsync(query);

        await profiles.MoveNextAsync();

        if (profiles.Current != null)
        {
            return profiles.Current.ToList();
        }
        return null;
    }

    public override async Task SaveProfileAsync(SomeProfile profile)
    {
        await _profileCollection.InsertOneAsync(profile);
    }
}
2
  • I had considered creating a store as well but struggled with how a single store could handle both the 'base' data source as well as the user-defined one in an elegant fashion. Having multiple stores seems like a good solution although it means other developers will have to be more aware of using the store collection to interact with their profiles instead of the profile object itself. Oh well :) Commented Aug 3, 2015 at 15:48
  • The way Identity solves it is by dependency-injection. It's a well established pattern; but most often only used in web-applications. You can make your ProfileStoreCollection have helper-classes, such as ProfileStoreCollection.Get<TProfileStoreType>(); and ProfileStore.Get(Type profileStoreType). If you store your profiles within a dictionary-like "inner"-collection <Type, ProfileStore>. That way your developers can easily "get back" their store after "adding" it.
    – mausworks
    Commented Aug 3, 2015 at 15:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.