unchase / Unchase.Odata.Connectedservice

:scroll: A Visual Studio extension for connecting to OData services with generating client-side C# proxy-classes
Apache License 2.0
44 stars 13 forks source link

Feature request to allow an option to use existing models in particular namespaces. #67

Open jimnkey opened 2 years ago

jimnkey commented 2 years ago

Great work with this library! Thank you!!! Having the need to share the models from one project with the OData infrastructure and the rest of the solution.

The models are used in other projects that should not (must not) bring in OData. Right now we manually gut out the generated portions of Reference.cs that are already defined but leaving the skeleton partials and creates along with the Container. That seems to work well - but creating or updating entities becomes a bit tedious. Although manageable, it would be very nice...

Is this feature already available (somewhat) by not creating the models and somehow registering the models with the Container externally?

Any chance this was a planned feature? :) along with the 2022 support? Thanks again!

unchase commented 2 years ago

Hi, @jimnkey

Could you please demonstrate with a specific example what exactly you are cleaning up?

jimnkey commented 2 years ago

Thanks for responding so quickly.... Here's an example with an abstract class and a derived: Let's say I have these models define like so and it's going through and generating an edm from my odata endpoint:

namespace Core.Models
{
    public abstract partial class Intercept
    {
        public Guid ID {get;set;}
        public virtual InterceptType InterceptType { get; set; }
        public virtual long? StartTimeStamp { get; set; }
        public virtual long? EndTimeStamp { get; set; }
        public virtual Guid? SessionID { get; set; }
        public virtual Guid? AssetID { get; set; }
    }

    public partial class ArtifactIntercept : Intercept
    {
        public ArtifactIntercept()
        {
            InterceptType = InterceptType.ArtifactInt;
        }
        public string ArtifactName { get; set; }
        public string Filename { get; set; }
    }
}

Then consuming the metadata endpoint and keeping the same namespace Core.Models --- gives the models defined and referenced appropriately in Default.Container. From there I take everything except namespace and contents of Default and place in a file within Core.Models -- that's when the clearing out of the properties from the generated models takes place -- leaving something like this:

namespace Core.Models
{
    /// <summary>
    /// There are no comments for InterceptSingle in the schema.
    /// </summary>
    public partial class InterceptSingle : global::Microsoft.OData.Client.DataServiceQuerySingle<Intercept>
    {
        /// <summary>
        /// Initialize a new InterceptSingle object.
        /// </summary>
        public InterceptSingle(global::Microsoft.OData.Client.DataServiceContext context, string path)
            : base(context, path) {}

        /// <summary>
        /// Initialize a new InterceptSingle object.
        /// </summary>
        public InterceptSingle(global::Microsoft.OData.Client.DataServiceContext context, string path, bool isComposable)
            : base(context, path, isComposable) {}

        /// <summary>
        /// Initialize a new InterceptSingle object.
        /// </summary>
        public InterceptSingle(global::Microsoft.OData.Client.DataServiceQuerySingle<Intercept> query)
            : base(query) {}

    }
    /// <summary>
    /// There are no comments for Intercept in the schema.
    /// </summary>
    /// <KeyProperties>
    /// ID
    /// </KeyProperties>
    [global::Microsoft.OData.Client.Key("ID")]
    public abstract partial class Intercept
    {
    }

    /// <summary>
    /// There are no comments for ArtifactInterceptSingle in the schema.
    /// </summary>
    public partial class ArtifactInterceptSingle : global::Microsoft.OData.Client.DataServiceQuerySingle<ArtifactIntercept>
    {
        /// <summary>
        /// Initialize a new ArtifactInterceptSingle object.
        /// </summary>
        public ArtifactInterceptSingle(global::Microsoft.OData.Client.DataServiceContext context, string path)
            : base(context, path) {}

        /// <summary>
        /// Initialize a new ArtifactInterceptSingle object.
        /// </summary>
        public ArtifactInterceptSingle(global::Microsoft.OData.Client.DataServiceContext context, string path, bool isComposable)
            : base(context, path, isComposable) {}

        /// <summary>
        /// Initialize a new ArtifactInterceptSingle object.
        /// </summary>
        public ArtifactInterceptSingle(global::Microsoft.OData.Client.DataServiceQuerySingle<ArtifactIntercept> query)
            : base(query) {}

    }
    /// <summary>
    /// There are no comments for ArtifactIntercept in the schema.
    /// </summary>
    /// <KeyProperties>
    /// ID
    /// </KeyProperties>
    [global::Microsoft.OData.Client.Key("ID")]
    public partial class ArtifactIntercept : Intercept
    {
        /// <summary>
        /// Create a new ArtifactIntercept object.
        /// </summary>
        /// <param name="interceptType">Initial value of InterceptType.</param>
        /// <param name="ID">Initial value of ID.</param>
        [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "2.7.0")]
        public static ArtifactIntercept CreateArtifactIntercept(global::Core.Models.InterceptType interceptType, global::System.Guid ID)
        {
            ArtifactIntercept artifactIntercept = new ArtifactIntercept();
            artifactIntercept.InterceptType = interceptType;
            artifactIntercept.ID = ID;
            return artifactIntercept;
        }
    }
}

Leaving Default.Container as is in the project to be used by clients that can bring in that client project. (did not include edm xml here)

namespace Default
{
    /// <summary>
    /// There are no comments for Container in the schema.
    /// </summary>
    public partial class Container : global::Microsoft.OData.Client.DataServiceContext
    {
        /// <summary>
        /// Initialize a new Container object.
        /// </summary>
        [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "2.7.0")]
        public Container(global::System.Uri serviceRoot) : 
                base(serviceRoot, global::Microsoft.OData.Client.ODataProtocolVersion.V4)
        {
            this.ResolveName = new global::System.Func<global::System.Type, string>(this.ResolveNameFromType);
            this.OnContextCreated();
            this.Format.LoadServiceModel = GeneratedEdmModel.GetInstance;
            this.Format.UseJson();
        }
        partial void OnContextCreated();
        /// <summary>
        /// Since the namespace configured for this service reference
        /// in Visual Studio is different from the one indicated in the
        /// server schema, use type-mappers to map between the two.
        /// </summary>
        [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "2.7.0")]
        protected string ResolveNameFromType(global::System.Type clientType)
        {
            return clientType.FullName;
        }
        /// <summary>
        /// There are no comments for Intercepts in the schema.
        /// </summary>
        [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "2.7.0")]
        public global::Microsoft.OData.Client.DataServiceQuery<global::Core.Models.Intercept> Intercepts
        {
            get
            {
                if ((this._Intercepts == null))
                {
                    this._Intercepts = base.CreateQuery<global::Core.Models.Intercept>("Intercepts");
                }
                return this._Intercepts;
            }
        }
        [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "2.7.0")]
        private global::Microsoft.OData.Client.DataServiceQuery<global::Core.Models.Intercept> _Intercepts;
        /// <summary>
        /// There are no comments for Intercepts in the schema.
        /// </summary>
        [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "2.7.0")]
        public void AddToIntercepts(global::Core.Models.Intercept intercept)
        {
            base.AddObject("Intercepts", intercept);
        }
        [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "2.7.0")]
        private abstract class GeneratedEdmModel
        {
            [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "2.7.0")]
            private static global::Microsoft.OData.Edm.IEdmModel ParsedModel = LoadModelFromString();
            [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "2.7.0")]
            private const string Edmx = @"...";
            [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "2.7.0")]
            public static global::Microsoft.OData.Edm.IEdmModel GetInstance()
            {
                return ParsedModel;
            }
            [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "2.7.0")]
            private static global::Microsoft.OData.Edm.IEdmModel LoadModelFromString()
            {
                global::System.Xml.XmlReader reader = CreateXmlReader(Edmx);
                try
                {
                    global::System.Collections.Generic.IEnumerable<global::Microsoft.OData.Edm.Validation.EdmError> errors;
                    global::Microsoft.OData.Edm.IEdmModel edmModel;

                    if (!global::Microsoft.OData.Edm.Csdl.CsdlReader.TryParse(reader, false, out edmModel, out errors))
                    {
                        global::System.Text.StringBuilder errorMessages = new global::System.Text.StringBuilder();
                        foreach (var error in errors)
                        {
                            errorMessages.Append(error.ErrorMessage);
                            errorMessages.Append("; ");
                        }
                        throw new global::System.InvalidOperationException(errorMessages.ToString());
                    }

                    return edmModel;
                }
                finally
                {
                    ((global::System.IDisposable)(reader)).Dispose();
                }
            }
            [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "2.7.0")]
            private static global::System.Xml.XmlReader CreateXmlReader(string edmxToParse)
            {
                return global::System.Xml.XmlReader.Create(new global::System.IO.StringReader(edmxToParse));
            }
        }
    }
}
jimnkey commented 2 years ago

As a side note ... I did try to generate the template files but I must have been doing something wrong -- I could not get them to properly generate. But now we're working with 2022 with the projects migrated already. And find we need to add an entity :)