Servicestack OrmLite Ignore insert update - POCO - c#

Is there any attribute set a POCO field just for SELECT.
Something like below;
public class Poco {
public string Id { get; set; }
public string Name { get; set; }
[IgnoreUpdate]
public Datetime CreatedOn{ get; set; }
[IgnoreInsert]
public Datetime UpdateOn{ get; set; }
}

OrmLite has [Ignore] to ignore the property completely, [IgnoreOnInsert] to ignore the property during INSERT's and [IgnoreOnUpdate] to ignore the property during updates.
An alternative solution is to use a different Model for SELECT's where you can use the [Alias] attribute to map it back to the original tablename, e.g.
[Alias("Poco")]
public class PocoDetails
{
public string Id { get; set; }
public string Name { get; set; }
public Datetime CreatedOn{ get; set; }
public Datetime UpdateOn{ get; set; }
}

Just small update - in current verion of ServiceStack v5 there is possibility to mark property with attributes [IgnoreOnInsert], [IgnoreOnUpdate] or [IgnoreOnSelect]

Related

Is there a way for me to use a List<string> property class with Entity Framework?

This is the class:
namespace backend
{
[Table("Products")]
public class Product
{
public long Id { get; set; }
[Required]
public string? Name { get; set; }
public string? Category { get; set; }
public string? Short { get; set; }
public string? Description { get; set; }
[Required]
public float Price { get; set; }
public string? MainImage { get; set; }
public float Disccount { get; set; }
public string[]? Images { get; set; } // List<string>
}
}
I've tried to run EF migrations but it appears that [] is not supported and, if I make it a List, it will ask for this List's key but it's just an array of strings
What am I doing wrong? Is not possible to add arrays as classes properties in EF?
It depends on the use case. Does the data be used in a relational child table way (1:many)? Or is it really just a list of some urls that don't have any further relations within the database?
For the first case, take a look at Caius answer. The second approach would be to write a type converter and register it within EF core. In that case your underlying type in the database would be some kind of string (e.g. nvarchar(max)) and EF core makes the conversion on the client side
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<Product>()
.Property(e => e.Images)
.HasConversion(
v => JsonSerializer.Serialize(v),
v => JsonSerializer.Deserialize<string[]>(v));
}
Instead of JSON you could also use some other approach like string.Join() and string.Split(), that's up to you. Further information can be found at Microsoft documentation.
What's wrong with doing what a relational database would expect:
namespace backend
{
[Table("Products")]
public class Product
{
public long Id { get; set; }
[Required]
public string? Name { get; set; }
public string? Category { get; set; }
public string? Short { get; set; }
public string? Description { get; set; }
[Required]
public float Price { get; set; }
public string? MainImage { get; set; }
public float Disccount { get; set; }
public ICollection<ProductImage>? Images { get; set; } //make it a new hashset in the constructor, btw
}
[Table("ProductImages")]
public class ProductImage
{
public long Id { get; set; }
[Required]
public string? Name { get; set; }
public string? Url { get; set; }
public long ProductId { get; set; }
public Product? Product { get; set; }
}
this way you can add more data to the image, like "front view", "side view", "version 2" etc, and EF can map it like it knows how to; as a separate table that is 1:M related
Is not possible to add arrays as classes properties in EF?
Technically, the ICollection<ProductImage> is a property that is an "array".. but out-of-the-box, not arrays of primitives, no
string is primitive type. you can't save list of primitive types in entity framework instead you can save your data in one property which is a single string then define another property to just get and set the main string property.
public class example {
public string AttachmentsString { get ; set; }
[NotMapped]
public List<string> Attachments {
get => !string.IsNullOrEmpty(AttachmentsString) ? AttachmentsString.Split(",").ToList() : new List<string>();
set => AttachmentsString = string.Join(",", value);
}
}
then in your controllers or service just manipulate with Attachments property.

Wrong Query Generated with EF Core 3.0

I am trying to use a simple where clause to retrieve data from a SQL Server. However the generated query is incorrect.This query worked perfectly with EF Core 2.2 but with EF Core 3 it throws an exception.
public async Task<List<CharacterReplacements>> GetReplacementsAsync(int? replacementSetId)
{
var replacementQuery = _context.CharacterReplacements.AsQueryable();
if (replacementSetId.HasValue)
{
replacementQuery = replacementQuery.Where(r => r.CharacterReplacementSetID == replacementSetId.Value); // .AsQueryable();
}
var replacementList = await replacementQuery.ToListAsync();
return replacementList;
}
[Serializable]
[Table("CharacterReplacementSets", Schema = "SYSTEM")]
public class CharacterReplacementSets
{
[NavigationPropertyKey]
[Key]
public int CharacterReplacementSetID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ICollection<CharacterReplacements> CharacterReplacements { get; set; }
public ICollection<FormatField> FormatFields { get; set; }
public string CreatedBy { get; set; }
public DateTime CreatedOn { get; set; }
public string UpdatedBy { get; set; }
public DateTime? UpdatedOn { get; set; }
public string DefaultEncoding { get; set; } // Default character set
public string DefaultCharacter { get; set; }
public CharacterReplacementSets()
{
CharacterReplacements = new List<CharacterReplacements>();
}
}
[Serializable]
[Table("CharacterReplacements", Schema = "SYSTEM")]
public class CharacterReplacements
{
[NavigationPropertyKey]
[Key]
public int CharacterReplacementID { get; set; }
public char OriginalCharacter { get; set; }
public string ReplacementCharacter { get; set; }
public string CreatedBy { get; set; }
public DateTime CreatedOn { get; set; }
public string UpdatedBy { get; set; }
public DateTime? UpdatedOn { get; set; }
[ForeignKey("CharacterReplacementSets")]
public int CharacterReplacementSetID { get; set; }
}
Expected result- Retrieve all CharacterReplacements where the replacementSetId equals the provided replacementSetId.
Actual result-
Microsoft.Data.SqlClient.SqlException: 'Invalid column name 'CharacterReplacementSetsCharacterReplacementSetID'.
Could someone kindly help me out with this?
The problem is not the specific query, but the model mapping.
First, the ForeignKey attribute here
[ForeignKey("CharacterReplacementSets")]
public int CharacterReplacementSetID { get; set; }
has no effect. When applied to navigation property, it's supposed to specify the FK property name. And when applied on FK property as here, it's supposed to specify the navigation property name. CharacterReplacements has no navigation property called CharacterReplacementSets, so the attribute is simply ignored. It would be good if EF Core generates runtime error to indicate a mapping problem, but it doesn't.
The attribute has been ignored in EF Core 1.x / 2.x as well. However it worked because the name of the property CharacterReplacementSetID matches the name of the PK of CharacterReplacementSets. This is no more true for EF Core 3.0 due to the following breaking change - The foreign key property convention no longer matches same name as the principal property.
So remove the incorrect and misleading ForeignKey attribute, and configure the FK property by either HasForeignKey fluent API (my preferred):
modelBuilder.Entity<CharacterReplacementSets>()
.HasMany(e => e.CharacterReplacements)
.WithOne()
.HasForeignKey(e => e.CharacterReplacementSetID);
or with ForegnKey attribute on navigation property (or inverse navigation property when there is no navigation property as here):
[ForeignKey("CharacterReplacementSetID")]
public ICollection<CharacterReplacements> CharacterReplacements { get; set; }
Note that you might have similar problem with FormatField and other entities using similar named FKs w/o navigation properties.
Another way to avoid this issue is to use singular entity class names like CharacterReplacementSet, CharacterReplacement etc. because the [entity name] + ID still matches EF Core conventions. And in general singular class names are better/preferable, even just for readability.

Dapper is converting my dates into 01-Jan-01 and I cannot see why?

I'm using SQL Server 2012 and Dapper v1.50.5.
I have a standard query:
SELECT *
FROM [TsData].[ImportingLogs]
WHERE ImportContextGuid = '{3c19d706-0895-49e4-b96c-38eb6a3cc579}'
which returns some data:
and the CreationTime column I am interested in has valid datetimes.
The table is simply defined as:
The POCO is:
public class OzCsImportingLogsTableModel
{
public DateTime CreationDateTime { get; set; }
public int? CreatorUserId { get; set; }
public int? DeleterUserId { get; set; }
public DateTime DeletionTime { get; set; }
public int DurationMs { get; set; }
public int Id { get; set; }
public Guid ImportContextGuid { get; set; }
public bool IsDeleted { get; set; }
public DateTime? LastModificationTime { get; set; }
public int? LastModifierUserId { get; set; }
public string Message { get; set; }
public OzCsImportManagerMessageKindEnum MessageKindId { get; set; }
public string Source { get; set; }
public string StructuredData { get; set; }
public string Tags { get; set; }
}
and the Dapper call is:
DbContext.Execute($"[{Schema}].[usp_ImportingQueue_FinaliseImport]", storedProcParams, aCommandType: CommandType.StoredProcedure)
However when I look at OzCsImportingLogsTableModel.CreationTime, the value is always 01-Jan-01 00:00:00 which indicates the value is NULL.
I don't understand this. Can someone point me in the right direction here please?
#John has it correct in his comment. The name of the property (here CreationDateTime) generally must match the name of the column (here CreationTime). Having that said, it is not really the name of the table column, but the name of the result column, so you could do something like this:
SELECT CreationTime as CreationDateTime, ...
if you can modify the actual query.
As commented by #fstam: the behavoir is that 01-jan-01 is the default value of a DateTime and since it's a non-nullable type and never set to anything by dapper, it is the value shown.
Note that the logic applied to find the member for a column name is available here:
// preference order is:
exact match over underscore match, exact
case over wrong case, backing fields over regular fields,
match-inc-underscores over match-exc-underscores
In your case, however none of the above applies; it is probably best to adjust the name of the property in code.
Update:
Another thing that I just saw: the DurationMs column in your schema is nullable, but the DurationMs property is not. You might want to define
this property as public int? DurationMs { get; set; } instead.
(I haven't checked all members).

Entity Framework Core default values for missing columns

I have a sqlite database which has some tables and columns like the following:
int Id
text Name
text Comment
...
And my object in my project looks like this:
Public Class Entry {
public int Id { get; set; }
public String Name { get; set; }
public String Comment { get; set; }
public String Additional { get; set; }
}
This can happen, because my programm need to handle different versions of the database.
EF Core now trys to access the Additional field of the database but returns an error that it cannot find the field. (Expected behaviour)
Now my question is, if there is a way to ignore this error and return a default value for the property?
I could bypass the error by making the properties nullable. But i don't want to check each property with .HasValue() before accessing it. Because the real database has 50+ columns in the table.
https://www.entityframeworktutorial.net/code-first/notmapped-dataannotations-attribute-in-code-first.aspx
Put NotMapped as an attribute on the Additional field:
using System.ComponentModel.DataAnnotations.Schema;
Public Class Entry {
public int Id { get; set; }
public String Name { get; set; }
public String Comment { get; set; }
[NotMapped]
public String Additional { get; set; }
}
This tells EF that the field is not a column in the database.
I would advise you to split your domain object from that persisted dto object. That way you can have different dtos with different mappings. Now you can instantiate your domain object with your dto and decide inside your domain object what values are the correct default values.
public class Entry
{
public int Id { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
public string Additional { get; set; }
}
public class EntryDtoV1
{
public int Id { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
}
public class EntryDtoV2
{
public int Id { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
public string Additional { get; set; }
}
Now you only need to create some kind of factory that creates the correct repository depending on what database version you query.

Is there a way to control the order of properties in JSON objects?

I'm using Entity Framework Core, and the generated class has its own properties, i.e.
DataModel.Agent.cs
public partial class Agent {
public virtual decimal Id
{
get;
set;
}
public virtual string Name
{
get;
set;
}
}
But I need other properties, so I declare them in another file:
Agent.cs
public partial class Agent
{
[NotMapped]
public dynamic Custom { get; set; }
}
The problem is that Agent.cs is compiled before DataModel.Agent.cs, so the compiler generates properties in this order: Custom, Id, Name, and the resulting JSON is weird.
I want it to be: Id, Name, Custom. In other words, I always want the DataModel class to come first.
EDIT: Just to clarify, the only objective is to make the JSON prettier by always putting the Id first, which is a very common pattern. This has absolutely no impact on how the application works.
Is there a way to force the compiler to always compile one of the files first?
Well you really shouldn't count on JSON property order BUT if using json.net
public class Account
{
public string EmailAddress { get; set; }
// appear last
[JsonProperty(Order = 1)]
public bool Deleted { get; set; }
[JsonProperty(Order = 2)]
public DateTime DeletedDate { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime UpdatedDate { get; set; }
// appear first
[JsonProperty(Order = -2)]
public string FullName { get; set; }
}
http://www.newtonsoft.com/json/help/html/JsonPropertyOrder.htm

Categories

Resources