NHibernate Speaker Number

In my C # project, I need to create one table with a dynamic column number in the code database. Like this

------------------------------------------------------------------------------ | Time ¦ Element #1 | Element#2 ¦ ... ¦ Element#N ¦ ------------------------------------------------------------------------------ ¦ TimeValue#1 ¦ Element#1Value#1 ¦ Element#2Value#1 ¦ ... ¦ Element#NValue#1 ¦ ¦ TimeValue#2 ¦ Element#1Value#2 ¦ Element#2Value#2 ¦ ... ¦ Element#NValue#2 ¦ ... ¦ TimeValue#M ¦ Element#1Value#M ¦ Element#2Value#M ¦ ... ¦ Element#NValue#M ¦ ------------------------------------------------------------------------------ 

I am using a simple SQL query "SQL CREATE TABLE"

 public void AddTable(string TableName, Dictionary<string,Type> columns) { ... string query = @"CREATE TABLE " + TableName + "("; foreach (var c in columns) { SqlParameter temp = new SqlParameter(); string t = SetSQLType(ref temp, c.Value).ToString(); query += c.Key + " " + (t == SqlDbType.NVarChar.ToString() ? (t + "(200)") : (t)) + ","; } query = query.Substring(0, query.Length - 1); query += " )"; ... } 

But I think, maybe I can use a more convenient ORM for this, for example, NHibernate. May I? I read about mapping by code and dynamic component , but I'm not sure what exactly I need to do in my case.

I think I need something like this:

 public class Elements { public virtual DateTime Timestamp { get; set; } public virtual IEnumerable<double> Values { get; set; } } 

Can this class be mapped?

+4
source share
1 answer

This topic is interesting. And maybe not so clear at first glance. Accept my answer as a review, a summary from all of the sources listed below. And perhaps this will give you an answer.

From a C # perspective, you can think of these dynamic properties this way

 public virtual IDictionary<keyType, valueType> Additional { get; set; } // (eg <string, object> public virtual IDictionary Additional { get; set; } 

Both are dynamic. There is no compile time check for handling IDictionary . The best situation is to check the general arguments for IDictinary<,> . But since we are talking about dynamic matching, compile-time checking is something we can sacrifice ...

To load data into one of these dictionaries, we must (in most cases) perform different comparisons and have different table structures. Generic would be useful for wrapping data contained in strings . Non-Generic can be used to display a column (as an example in the question). Let's discuss how

1) Generic IDictionary<,> - Dynamic Strings

Start with a more secure scenario. Then tap the solution next to the question

1a) Triple Associations

For example, let's match several individuals with some objects (for example, Contract) based on their roles / types. 1) Manager 2) Head 3) Tester. If we know that there can only be one person per type / role (only one tester or none), we can explain this as:

 public virtual IDictionary<PersonType, Person> Persons { get; set; } 

We are dynamic now. There may be 0, 1 or 1+ persons associated with the Agreement. Each of them must be unique with PersonType . And we can also introduce a new PersonType at runtime and extend any contract associated with a person ...

The display should be like this:

 <map name="Persons" table="ContractPerson" > <key column="ContractId"/> <index-many-to-many column="PersonTypeId" class="PersonType"/> <one-to-many class="Person"/> </map> 

This is an example of 6.9. Triple associations . There are three subjects, and we still have flexibility at runtime. As already mentioned, we can insert new PersonTypes and modify these Contract-Person relationships.

This script (compared to IDictiniary<string, object> ) still provides many compile-time checks.

1b) Closer to the question

In the scenario described above, if we want to use <map> and be dynamic in terms of strings , we need a table like this:

 ElemntId| TheKey | TheValue (eg nvarchar) 1 | "name" | "Element A" 1 | "time" | "20:02" 1 | "date" | "2013-05-22" 1 | "value" | "11.22" 

C # will look like this:

 public class Elements { ... public virtual IDictionary<string, string> Values { get; set; } } 

display:

 <map name="Values" table="DynamicElementValues" > <key column="ElementId"/> <index column="TheKey" type="string"/> <element column="TheValue" type="string"/> </map> 

Since we used IDictionary<string, string> , all values ​​are strings. We need a little MetaData to correctly interpret its value

We got:

  • dynamic set of many values ​​of many types

We lost:

  • the ability to put any value in a SELECT or ORDER BY clause
  • need to convert data types (from string to any other)

1c) Key as string

In fact, a dictionary with the string key is dynamic, but too large. As shown in 1a), it is always a better idea to somehow control the set of values ​​that will be used as a key . That is why we discussed the Ternar Association. Since sooner or later we must interpret the data in this dictionary - with some MetaData, it would be convenient to use them also as a key ...

2) Non-Generic IDictionary - Dynamic Columns

This time we will really try to execute a dynamic solution by columns.

NHibernate features we can use here:

This type of comparison is very close to the Question, to our requirement. We will have this representation of C #

 public class Elements { ... public virtual IDictionary DynamicValues { get; set; } } 

And it could be a mapping:

 <join table="ElemntValues" > <key column="ElementId" /> <dynamic-component name="DynamicValues" > <property name="Time" type="TimeSpan" /> <property name="Date" type="DateTime" /> <property name="Salary" type="decimal" /> <property name="Color" type="string" /> <property name="WorkingDays" type="integer" /> <many-to one.... ... </dynamic-component> </join> 

In this case, we split the ElementValues table associated with our parent entity (as part of its <class> display).

This is not the only mapping. There may be other types of display, for example. from 4.4. Dynamic models

 <class entity-name="DynamicValues"... 

This may require some additional processing (insert, udpate, delete)

Join will simplify many things, but will always be used in SQL status (even if only the kernel properties of the parent are required)

Is it dynamic?

Well, we got:

  • In C #, we only have the IDictionary ElementValues property
  • NHibernate does runtime checks for us. Only the correct value types can be inserted into the keys (Salary must be decimal).
  • with some MetaData model we can really be dynamic for the user
  • Any of the Mapped properties can be used in SELECT (projections) and ORDER BY (the user will like it)

We lost:

  • some performance since all data will be loaded (e.g. session.Get <> (id))
  • We are not dynamically in the case of adding or removing columns. All mapping is part of the distribution of the application and cannot be changed at run time. Well, we can always redeploy only a new mapping ...

2b) IDictionary and MetaData h3>

While IDictinary from the point of view of C # is very dynamic (it can contain any key / pair value), due to NHibernate mapping, the contents succeeded. Only integer values ​​can be added to properties displayed as integer . But how would we know in time: what keys do we have? What values ​​can be placed there and retrieved from there? Again, we need some MetaData ... not acting as a key, but they will be crucial at runtime. NHibernate checks the latest line of defense.

3) How to change the display at runtime (added a new column)?

Well, what is indicated in the documentation? 7.5. Dynamic components :

The advantage of this type of mapping is the ability to determine the actual properties of the component during deployment, simply by editing the mapping document. (Processing the runtime of a mapping document is also possible using the DOM parser.)

... using the DOM parser. Honestly, I do not know what this means, how to implement it.

And also Adam Bar in Display by code - a dynamic component (see comments)

I think that a dynamic component cannot be truly dynamic on both objects and the database level. Please note that the components are saved as regular columns, so we need to know its list ...

But there is a good idea from Firo 's dynamic mapping - NHibernate (look, too complicated for some fragments). If it is really necessary, it can be a solution for the real dynamic world ... new columns at runtime ... matching new keys in an IDictionary

Summary

With the <map> mapping, we can be very dynamic. Different keys and values ​​at runtime. No need to relocate. But we cannot use these dynamic properties in select or order by. It is difficult to filter these values

With a <dynamic-component> we (out of the box) are display dependent. But we have our data in columns, and so we can use joins to retrieve them. Easy to filter, sort. And /, but there must be some MetaData to guide us to what we have and what we can do.

Other sources:

+8
source

Source: https://habr.com/ru/post/1482117/


All Articles