コード例 #1
0
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="entityType">The entity type.</param>
        /// <param name="typeIdentifier">The entity's application domain unique identifier string (or <c>null</c>).</param>
        /// <param name="creator">The entity creation delegate.</param>
        public EntityRegistration(Type entityType, string typeIdentifier, EntityCreateDelegate creator)
        {
            Covenant.Requires <ArgumentNullException>(entityType != null);
            Covenant.Requires <ArgumentNullException>(creator != null);

            this.EntityType     = entityType;
            this.TypeIdentifier = typeIdentifier;
            this.Creator        = creator;
        }
コード例 #2
0
        /// <summary>
        /// Constructs a <typeparamref name="TEntity"/> from a <see cref="JObject"/>.
        /// </summary>
        /// <typeparam name="TEntity">The entity type.</typeparam>
        /// <param name="jObject">The backing <see cref="JObject"/>.</param>
        /// <param name="context">The <see cref="IDynamicEntityContext"/> or <c>null</c>.</param>
        /// <returns>The new <typeparamref name="TEntity"/>.</returns>
        /// <remarks>
        /// <para>
        /// This method is capable of instantiating derived entities in a future-proof
        /// manner.  This is possible for entities whose defining <c>interface</c>s were
        /// tagged with a <see cref="DynamicEntityAttribute.Type"/> value.  This is
        /// set to a unique identifier for the type within the application domain.
        /// </para>
        /// <para>
        /// Entities serialize their own type identifier as well as the
        /// identifiers for any inherited types to the internal <see cref="DynamicEntity.EntityTypePathName"/>
        /// JSON property.  The type identifiers are formatted into a list of
        /// colon (<b>:</b>) separated values with the current type's identifier
        /// listed first, followed by its parent's identifier and so on, all the
        /// way to the root entity's identifier.
        /// </para>
        /// <para>
        /// This identifier list provides the information necessary to dynamically
        /// instantiate the derived entity that best fits the data.  For example,
        /// say you release a product that defines a base <c>IProduct</c> interface
        /// with <c>ICandy</c> derived from it.  IProduct defines a <b>Name</b> as
        /// property and ICandy adds <b>Calories</b>.
        /// </para>
        /// <para>
        /// An application can persist a base IProduct instance to the database
        /// as well as a derived ICandy.  Calling this method to load the base
        /// IProduct or ICandy document will simply load it.  Calling this method
        /// to load IProduct but passing the ICandy document, will actually create
        /// an ICandy instance but return it cast to the base type (IProduct).
        /// </para>
        /// <para>
        /// Applications can test entity types using the usual C# <c>is</c>/<c>as</c>
        /// operators or switch on the entity's type property, if one was tagged
        /// using <see cref="DynamicEntityPropertyAttribute.IsTypeProperty"/>.
        /// </para>
        /// <para>
        /// This also enables future-proofing.  Say version 1.0 of an application only
        /// knew about IProduct and ICandy.  Then, version 2.0 is released that adds
        /// <c>IBeer</c> which also derives from IProduct and persists some beer to
        /// a database.  Loading an IBeer document as an IProduct from version 1.0
        /// will still work, even though v1 is not aware of this type.  An IProduct
        /// entity will be returned (what else can be done), but the IBeer properties
        /// will still be loaded into the underlying <c>JObject</c>.
        /// </para>
        /// </remarks>
        public static TEntity Create <TEntity>(JObject jObject, IDynamicEntityContext context)
            where TEntity : class, IDynamicEntity, new()
        {
            Covenant.Requires <ArgumentNullException>(jObject != null);

            EntityCreateDelegate creator = null;

            string[]   typePath = null;
            JToken     typePathToken;
            CreateInfo createInfo;

            if (jObject.TryGetValue(EntityTypePathName, out typePathToken) && typePathToken.Type == JTokenType.String)
            {
                typePath = ((string)typePathToken).Split(colonSplitter);
            }

            var hasTypePath = typePath != null && typePath.Length > 0;

            if (!hasTypePath)
            {
                // The document doesn't have a type path property so we're going to
                // use the creator registered for the type.

                typeToEntityCreator.TryGetValue(typeof(TEntity), out creator);
            }
            else
            {
                // Try each type identifier from the beginning of the path and use the
                // first registered creator we can find.  This will return the derived
                // class that best fits the data.

                foreach (var typeIdentifier in typePath)
                {
                    if (typeIdentifierToEntityCreator.TryGetValue(typeIdentifier, out createInfo))
                    {
                        creator = createInfo.Creator;
                        break;
                    }
                }
            }

            if (creator == null)
            {
                if (hasTypePath)
                {
                    throw new InvalidOperationException($"Entity type [{typeof(TEntity).FullName }] has not registered an entity creation function.  Make sure you have registered the generated entities and binder document types.");
                }
                else
                {
                    throw new InvalidOperationException($"Could not map any type from the [{typePath}] type path to an entity type.  Make sure you have registered the generated entities and binder document types.");
                }
            }

            var untypedEntity = creator(jObject, context);

            if (untypedEntity == null)
            {
                return(null);
            }

            var entity = untypedEntity as TEntity;

            if (entity == null)
            {
                throw new InvalidCastException($"Unable to cast [{untypedEntity.GetType().FullName}] to [{typeof(TEntity).FullName}].  Possible data corruption or invalid type path [{typePath}].");
            }

            return(entity);
        }