0

I'm trying to automate setting up datagrip for work. Each project has a few files that define what the datasource is, what the workspace looks like etc etc. The part that confuses me on how to go about implementing is the idea of "multifile-model". Here is an example: for ease of use I put each of the projects in a git repository and created a new project and committed that. I then added a new database connection to a ms.sql database that requires windows authentication. in dataSources.xml it looks similar to

<project version="4">
  <component name="DataSourceManagerImpl" format="xml" multifile-model="true">
    <data-source name="test" source="LOCAL" uuid="fcc0408c-408c-fcc0-e254-83c7094db613">
      <driver-ref>sqlserver.ms</driver-ref>
      <synchronize>true</synchronize>
      <jdbc-driver>com.microsoft.sqlserver.jdbc.SQLServerDriver</jdbc-driver>
      <jdbc-url>jdbc:sqlserver://myserver\someinstance;databaseName=somedatbase;</jdbc-url>
      <auto-commit>false</auto-commit>
    </data-source>
<!-- 95 more datasources -->
  </component>
</project>

making that part is rather easy

DataSourceElement.cs

[XmlRoot("data-source")]
public class DataSourceElement
{
    [XmlAttribute("name")]
    public string Name { get; set; }

    [XmlAttribute("source")]
    public string Source { get; set; }

    [XmlAttribute("uuid")]
    public Guid Uuid { get; set; }

    [XmlElement("driver-ref")]
    public string DriverRef { get; set; }

    [XmlElement("synchronize")]
    public bool Synchronized { get; set; }

    [XmlElement("jdbc-driver")]
    public string DriverName { get; set; }

    [XmlElement("jdbc-url")]
    public string DriverUrl { get; set; }

    [XmlElement("default-dialect")]
    public string DefaultDialect { get; set; }

    [XmlElement("auto-commit")]
    public bool IsAutoCommit { get; set; }

    [XmlArray("driver-properties")]
    [XmlArrayItem("property")]
    public List<DatagripPropertyElement> DriverProperties { get; set; } = new List<DatagripPropertyElement>();

    [XmlArray("jdbc-additional-properties")]
    [XmlArrayItem("property")]
    public List<DatagripPropertyElement> DriverAdditionalProperties { get; set; } = new List<DatagripPropertyElement>();
}

DataSourceManagerImpl.cs **

[XmlRoot("component")]
public class DataSourceManagerImpl : ComponentElement
{
    [XmlAttribute("format")]
    public string Format { get; set; }

    [XmlAttribute("multifile-model")]
    public bool MultiFileModel { get; set; }

    [XmlElement("data-source")]
    public List<DataSourceElement> DataSources { get; set; } = new List<DataSourceElement>();
}
[XmlRoot("component")]
public abstract class ComponentElement
{
    [XmlAttribute("name")]
    public string Name { get; set; }
}

DataSourceProject.cs

[XmlRoot("project")]
public class DataSourceProject : ProjectElement
{
    [XmlElement("component")]
    public DataSourceManagerImpl Component { get; set; }
}
[XmlRoot("project")]
public abstract class ProjectElement
{
    [XmlAttribute("version")]
    public int Version { get; set; }
}

linqpad to test

private const string DATAGRIP_PROJECT = @"C:\Users\snyder\.DataGrip2017.3\config\projects";
void Main()
{
    DataSourceManager = new DataSoureProject();
    //initialize omitted
    Save(DATAGRIP_PROJECT, "default");
}

    public void Save(string datagripProjectPath, string datagripProjectName)
    {
        var projectPath = Path.Combine(datagripProjectPath, datagripProjectName, ".idea");
        WriteToFile(DataSourceManager, Path.Combine(projectPath, "datasources.xml"));
    }

    private static void WriteToFile<T>(T obj, string fullPath, bool omitXmlDeclaration = true)
    {
        var settings = new XmlWriterSettings
        {
            OmitXmlDeclaration = omitXmlDeclaration,
            Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false),
            Indent = true,
        };
        using (var writer = XmlWriter.Create(fullPath, settings))
        {
            var ns = new XmlSerializerNamespaces();
            ns.Add("", "");
            var serializer = new XmlSerializer(typeof(T));
            serializer.Serialize(writer, obj, ns);
        }
    }

so then I look at datasources.Local.xml which contains the bit that i want

<data-source name="test" uuid="fcc0408c-408c-fcc0-e254-83c7094db613">
  <database-info product="" version="" jdbc-version="" driver-name="" driver-version="" />
  <domain-auth>true</domain-auth>
</data-source>

At first I was thinking of adding said property to my DataSourceElement class but couldn't figure out how I would split the properties. So then I was like..well I guess I can just make duplicates of classes and pretend they are different. Anywho, searching has turned up nothing. So now I ask.. is it possible to add a few properties to my DataSourceElement class that only gets added to the other file? if so how?

Robert Snyder
  • 2,399
  • 4
  • 33
  • 65
  • I don't understand what you need. Which other properties need to go in which different file? Do you mean your DataSourceElement has different properties? Like a base class with subclasses? Something else? – rene Dec 30 '17 at 22:52
  • 2
    Make your life easier by leveraging `AutoMapper` to map out the this class into smaller definitions. You can serialize the smaller classes into the desired payloads, at that point. – code4life Dec 30 '17 at 23:06
  • 1
    Don't use Serialize. You can generate custom xml output files using XmlDocument or XDocument or XmlWriter. – jdweng Dec 30 '17 at 23:12
  • 1
    You could use the [ShouldSerialize pattern](https://stackoverflow.com/q/37838640/3744182) to conditionally suppress serialization of certain properties based on, say, some threadstatic variable, then serialize multiple times using different values for the threadstatic. But the suggestion of @code4life to use [DTOs](https://en.wikipedia.org/wiki/Data_transfer_object) with [tag:automapper] sounds cleaner. – dbc Dec 30 '17 at 23:19
  • @code4life I like this idea. Would you mind doing a super simple example as an answer so I can accept it? – Robert Snyder Dec 30 '17 at 23:25
  • @jdweng This is a one of a kind thing that shouldn't be changing much. I am using XDocument to load and Parse and what not. I don't see how your idea helps. Could you elaborate as an answer so I can potentially use it? – Robert Snyder Dec 30 '17 at 23:27
  • So why can't you use XDocument to serialize? Just ignore "multifile-model". And just generate the xml. The file groups are just for loading the server from files and really doesn't make a difference if you are loading from c#. See : https://learn.microsoft.com/en-us/sql/relational-databases/databases/database-files-and-filegroups – jdweng Dec 31 '17 at 08:58
  • @jdweng the problem isn't so much reading as it is writing. That's the part I don't know how to do. – Robert Snyder Dec 31 '17 at 13:32
  • Then what is the issue? – jdweng Dec 31 '17 at 14:36
  • @jdweng join me in chat https://chat.stackoverflow.com/rooms/7/c – Robert Snyder Dec 31 '17 at 14:50

1 Answers1

0

I think I thought of something that might just make it easier and is based off of both @jdweng and @code4life comments

I was thinking that if I were to check if the jetbrains project doesn't exist I could create the folders and files by using serialization. This would be easy and very simple as it would just be the skeleton. I could then use XDocument to load the skeletons and update/create the connections as needed using AutoMapper to create the XElements as I need them. This should make life easier in all respects as well as making it easy to add to this if need be or change it if jetbrains decides to change the coding.

Robert Snyder
  • 2,399
  • 4
  • 33
  • 65