Override LinqToSql class property - linq-to-sql

One of the fields in a table in my database stores string values delimited by '|'. Of course, when I generated a Linq data model from the table using the VS designer, a string property has been created for the mentioned field. I want to override that property so as to expose the stored values as a more convenient string array (splitting them by '|').

What I would do is create a partial class (all LINQ-to-SQL classes are partial by default) of your class and exposing a property with the desired behaviour. Something like this:
public partial YourClass {
public string[] SpecialProp { get { return OtherProp.Split('|'); } }
}

Related

EF Core 7 can't deserialize dynamic-members in JSON column

I am trying to map my Name column to a dynamic object. This is how the raw JSON data looks (note that this is SQL-morphed from our old relational data and I am not able to generate or interact with this column via EF Core):
{ "en": "Water", "fa": "آب", "ja": "水", ... }
Just to note, available languages are stored in a separate table and thus are dynamically defined.
Through T-SQL I can perfectly interact with these objects eg
SELECT *
FROM [MyObjects]
WHERE JSON_VALUE(Name, '$.' + #languageCode) = #searchQuery
But it seems EF Core doesn't want to even deserialize these objects as whole, let alone query them.
What I get in a simple GetAll query is an empty Name. Other columns are not affected though.
I have tried so far
Using an empty class with a [JsonExtensionData] dictionary inside
Using a : DynamicObject inheritance and implementing GetDynamicMembers, TryGetMember, TrySetMember, TryCreateInstance
Directly mapping to a string dictionary.
Combining 1 & 2 and adding an indexer operator on top.
All yield the same results: an empty Name.
I have other options like going back to a junction table relational which I have many issues with, hardcoding languages which is not really intuitive and might cause problems in the future, using HasJsonConversion which basically destroys the performance on any search action... so I'm basically stuck here with this.
I think currently it's not fully supported:
You can not use dynamic operations on an expression tree like a Select statement because it needs to be translated.
JsonValue and JsonQuery requires a path to be resolved.
If you specify OwnsOne(entity = >entity.owned, owned => owned.ToJson()) and the Json could not be parsed you will get an error.
I suggest this workaround while the EF team improves the functionality.
Create a static class with static methods to be used as decoys in the expression tree. This will be mapped to the server built-in functions.
public static class DBF
{
public static string JsonValue(this string column, [NotParameterized] string path)
=> throw new NotSupportedException();
public static string JsonQuery(this string column, [NotParameterized] string path) => throw new NotSupportedException();
}
Include the database functions on your OnModelCreating method.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasDbFunction(
typeof(DBF).GetMethod(nameof(DBF.JsonValue))!
).HasName("JSON_VALUE").IsBuiltIn();
modelBuilder.HasDbFunction(
typeof(DBF).GetMethod(nameof(DBF.JsonQuery))!
).HasName("JSON_QUERY").IsBuiltIn();
/// ...
modelBuilder.Entity(entity => {
//treat entity as text
entity.Property(x => x.Metadata)
.HasColumnType("varchar")
.HasMaxLength(8000);
});
}
Call them dynamically with LINQ.
var a = await _context.FileInformation
.AsNoTracking()
.Where(x => x.Metadata!.JsonValue("$.Property1") == "some value")
.Select(x => x.Metadata!.JsonValue("$.Property2"))
.ToListAsync();
You can add casts or even build anonymous types with this method.
My solution was I added a new class which has KEY and VALUE , which will represent the dictionary i needed :
public class DictionaryObject
{
public string Key { set; get; }
public string Value { set; get; }
}
and instead of having this line in the JSON class :
public Dictionary<string, string> Name { get; set; }
I changed to :
public List<DictionaryObject> Name { get; set; }
Hope it helps.

Lower casing column values dynamically

I have a mysql script and I want to convert a column value to be always in lower case. I don't want to use trigger. When I run my hibernate code and fill data in DB I want a column value to be always in lowercase.
Is there is any way I can use Lower() function of mysql during table creation so that every time data is inserted it is lower Case?
I saw many examples of lowercase but all are update operation.
You can use Hibernate interceptor (see an example here)
In the method
public boolean onSave(Object entity,Serializable id,
Object[] state,String[] propertyNames,Type[] types)
You can check whether the entity is instance of your class to be lowercased and update necessary fields with lower cased values.
Or just in the entity extend setters to convert to the lowercase on call.
Assume you have an entity like:
#Entity
#EntityListeners(MyEntityListener.class)
public class MyEntity {
private String name;
...
}
and EntityListener like:
import javax.persistence.PrePersist;
public class MyEntityListener {
#PrePersist
public void entityPrePersist(MyEntity obj) {
if (obj != null && obj.getName() != null) {
obj.setName(obj.getName().toLowerCase());
}
// ... same to other properties
}
}

Convert Java objects to JSON with specific fields

I want to create two different JSON documents and each contains 5 fields. I have a POJO class with 10 attributes. I want to form json1 with 5 attributes and json2 with 5 attributes using that POJO class. Is there any way to construct these objects?
Consider writing two separate wrapper classes which each expose the fields you want for the two cases, and pass the pojo as a constructor arg.
So, one of them exposes one set of properties and might look like this:
public class JsonObject1 {
private MyPojo myPojo;
public JsonObject1(MyPojo myPojo) {
this.myPojo = myPojo;
}
public void getProperty1() {
return myPojo.getProperty1();
}
......
}
and the other is similar, but exposes the other subset of properties.
Alternatively, you could add two methods (possibly to your POJO, or possibly to a service class that is exposing the POJO) that each returns a Map (eg a HashMap) where you've copied across the specific properties you want for each view, and then convert those Maps to JSON. This is less "model-driven", but might be less work overall. Thanks to #fvu for this observation!
public Map<String, Object> getPojoAsMap1() {
Map<String, Object> m = new HashMap<>();
m.put("property1", pojo.getProperty1());
....
return m;
}
It's also possible that the two different JSON representations are trying to tell you that your POJO should be split up into two POJOs - sometimes things like this are hints about how your code could be improved. But it depends on the circumstances, and it might not apply in this case.

Unexpected duplicate key error using #JsonTypeInfo property

I have a simple hierarchy of data objects, which have to be converted to JSON format. Like this:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "documentType")
#JsonSubTypes({#Type(TranscriptionDocument.class), #Type(ArchiveDocument.class)})
public class Document{
private String documentType;
//other fields, getters/setters
}
#JsonTypeName("ARCHIVE")
public class ArchiveDocument extends Document { ... }
#JsonTypeName("TRANSCRIPTIONS")
public class TranscriptionDocument extends Document { ... }
Upon JSON parsing I encounter errors like this one:
Unexpected duplicate key:documentType at position 339. , because in the generated JSON there are actually two documentType fields.
What should be changed to make JsonTypeName value appear in documentType field, without an error (eg replacing the other value)?
Jackson version is 2.2
Your code doesn't show it, but I bet you have a getter in your Document class for the documentType property. You should annotate this getter with #JsonIgnore like so:
#JsonIgnore
public String getDocumentType() {
return documentType;
}
There is an implicit documentType property associated with each subclass, so having the same property in the parent class causes it to be serialized twice.
Another option would be to remove the getter altogether, but I assume you might need it for some business logic, so the #JsonIgnore annotation might be the best option.

Is it possible to cast database columns to different types in the L2S entity?

Given a table or view with an Integer column is it possible to do a conversion or cast to a String value in the DBML or create a calculated property on the entity that can be used as a relationship to another entity?
I have tried making the generated type a string but it gives an error:
Error 1 DBML1005: Mapping between DbType 'Int' and Type 'System.String' in Column 'Foo' of Type 'FooRecord' is not supported. 0 0
I don't think you can do anything like this - not in the Linq-to-SQL data model, for sure. If it's an INT in the database, it's an INT in the model and cannot be "manipulated" into being a string all of a sudden...
But what you could do is extend that class that Linq-to-SQL generates for you - it's a partial class, e.g. you can extend it in a separate, second file:
YourEntityEx.cs
public partial class YourEntity
{
public string YourPropertyAsString
{
get { return YourProperty.ToString(); }
set { YourProperty = Convert.ToInt32(value); } // if you even this
}
}
This way, you now have a second property YourPropertyAsString on your YourEntity class that will always be exactly the same as YourProperty - only of type string.