NHibernate Map an Object without a table - c#

I'm trying to map the following object:
public class DatabaseInfo
{
public virtual DateTime CurrentDateTime { get; set; }
}
with the following map:
public class DatabaseInfoMap : ClassMapping<DatabaseInfo>
{
public DatabaseInfoMap()
{
Property(x => x.CurrentDateTime, map => { map.Formula("(SELECT CURRENT UTC TIMESTAMP AS CurrentDateTime)"); });
}
}
I am getting a validation error, as the Map does not contain a 'Table' definition. Is it not possible to map obejcts without having to make a Table or View?
I'm not sure if this is the right approach for me to get the database time via NHibernate. Any help is appreciated.

Since there is no table mapping as an entity makes no sense (no identity). I suggest:
public class DatabaseInfo
{
public static DatabaseInfo Get(ISession session)
{
return session.CreateSQLQuery("SELECT CURRENT UTC TIMESTAMP AS CurrentDateTime").SetResultTransformer(Transformers.AliasToBean<DatabaseInfo>()).UniqueResult<DatabaseInfo>();
}
public DateTime CurrentDateTime { get; protected set; }
}

Can't you just use:
public class DatabaseInfo
{
public DateTime CurrentDateTime { get; set; } = DateTime.UtcNow;
}
When NH hydrates your object, or a graph containing this object, this property will be set with that default value.

Related

ASP.NET MVC: How can I put the StartDate of a current record on the EndDate of an earlier record

I have a class that makes appointments, the person making a appointments only inserts the start date and time, but the end date must be equal to the start date of the next appointments. My difficulty is in ensuring that the previous appointments always receives the EndDate as the StartDate of the current appointments
public class InfoAppointments : Entity
{
public bool Active { get; set; }
public bool Excluded { get; set; }
public string Status { get; set; }
public string Observation{ get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
EDIT
My Repository:
public class InfoAppointmentsRepository : Repository<InfoAppointments>, IAppointmentsRepository
{
public InfoAppointmentsRepository(RveContext rveContext) : base(rveContext)
{
}
public InfoAppointments FindByName(string name)
{
return Search(c => c.Name== name).FirstOrDefault();
}
public InfoAppointments FindByStatus()
{
throw new NotImplementedException();
}
public override void Remove(Guid id)
{
throw new NotImplementedException();
}
}
}
There are several possible solutions to this, and it may depend on your preference for adding this sort of business logic in your application code or in SQL (e.g. as a trigger). I would personally recommend the former, as this requirement may evolve over time, and may affect other components of your business logic.
I've made a few assumptions: 1) That you are using Entity Framework, and 2) Appointments don't overlap, and EndDate is unique. If that is the case, you could implement this functionality using similar logic to the following:
public class AppointmentService
{
private readonly MyContext _db;
public AppointmentService(MyContext db) => _db = db;
public void AddAppointment(InfoAppointments appointment)
{
// Update the previous appointment's end date
var previousAppointment = _db.Appointments
.OrderByDescending(e => e.EndDate)
.FirstOrDefault();
if (previousAppointment != null)
{
previousAppointment.EndDate = appointment.StartDate;
}
// Add the new appointment
_db.Appointments.Add(appointment);
_db.SaveChanges();
}
}
One other comment: based on your explanation, it appears that EndDate should default to null, but you've used a non-nullable type. I would change it to the following:
public DateTime? EndDate { get; set; }

EF returns "Cannot Insert NULL" ... But column defines a default value [duplicate]

is there "elegant" way to give specific property a default value ?
Maybe by DataAnnotations, something like :
[DefaultValue("true")]
public bool Active { get; set; }
Thank you.
You can do it by manually edit code first migration:
public override void Up()
{
AddColumn("dbo.Events", "Active", c => c.Boolean(nullable: false, defaultValue: true));
}
It's been a while, but leaving a note for others.
I achieved what is needed with an attribute and I decorated my model class fields with that attribute as I want.
[SqlDefaultValue(DefaultValue = "getutcdate()")]
public DateTime CreatedDateUtc { get; set; }
Got the help of these 2 articles:
EF on CodePlex
Andy Mehalick blog
What I did:
Define Attribute
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class SqlDefaultValueAttribute : Attribute
{
public string DefaultValue { get; set; }
}
In the "OnModelCreating" of the context
modelBuilder.Conventions.Add( new AttributeToColumnAnnotationConvention<SqlDefaultValueAttribute, string>("SqlDefaultValue", (p, attributes) => attributes.Single().DefaultValue));
In the custom SqlGenerator
private void SetAnnotatedColumn(ColumnModel col)
{
AnnotationValues values;
if (col.Annotations.TryGetValue("SqlDefaultValue", out values))
{
col.DefaultValueSql = (string)values.NewValue;
}
}
Then in the Migration Configuration constructor, register the custom SQL generator.
SetSqlGenerator("System.Data.SqlClient", new CustomMigrationSqlGenerator());
The above answers really helped, but only delivered part of the solution.
The major issue is that as soon as you remove the Default value attribute, the constraint on the column in database won't be removed. So previous default value will still stay in the database.
Here is a full solution to the problem, including removal of SQL constraints on attribute removal.
I am also re-using .NET Framework's native DefaultValue attribute.
Usage
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[DefaultValue("getutcdate()")]
public DateTime CreatedOn { get; set; }
For this to work you need to update IdentityModels.cs and Configuration.cs files
IdentityModels.cs file
Add/update this method in your ApplicationDbContext class
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
var convention = new AttributeToColumnAnnotationConvention<DefaultValueAttribute, string>("SqlDefaultValue", (p, attributes) => attributes.SingleOrDefault().Value.ToString());
modelBuilder.Conventions.Add(convention);
}
Configuration.cs file
Update your Configuration class constructor by registering custom Sql generator, like this:
internal sealed class Configuration : DbMigrationsConfiguration<ApplicationDbContext>
{
public Configuration()
{
// DefaultValue Sql Generator
SetSqlGenerator("System.Data.SqlClient", new DefaultValueSqlServerMigrationSqlGenerator());
}
}
Next, add custom Sql generator class (you can add it to the Configuration.cs file or a separate file)
internal class DefaultValueSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
private int dropConstraintCount;
protected override void Generate(AddColumnOperation addColumnOperation)
{
SetAnnotatedColumn(addColumnOperation.Column, addColumnOperation.Table);
base.Generate(addColumnOperation);
}
protected override void Generate(AlterColumnOperation alterColumnOperation)
{
SetAnnotatedColumn(alterColumnOperation.Column, alterColumnOperation.Table);
base.Generate(alterColumnOperation);
}
protected override void Generate(CreateTableOperation createTableOperation)
{
SetAnnotatedColumns(createTableOperation.Columns, createTableOperation.Name);
base.Generate(createTableOperation);
}
protected override void Generate(AlterTableOperation alterTableOperation)
{
SetAnnotatedColumns(alterTableOperation.Columns, alterTableOperation.Name);
base.Generate(alterTableOperation);
}
private void SetAnnotatedColumn(ColumnModel column, string tableName)
{
if (column.Annotations.TryGetValue("SqlDefaultValue", out var values))
{
if (values.NewValue == null)
{
column.DefaultValueSql = null;
using var writer = Writer();
// Drop Constraint
writer.WriteLine(GetSqlDropConstraintQuery(tableName, column.Name));
Statement(writer);
}
else
{
column.DefaultValueSql = (string)values.NewValue;
}
}
}
private void SetAnnotatedColumns(IEnumerable<ColumnModel> columns, string tableName)
{
foreach (var column in columns)
{
SetAnnotatedColumn(column, tableName);
}
}
private string GetSqlDropConstraintQuery(string tableName, string columnName)
{
var tableNameSplitByDot = tableName.Split('.');
var tableSchema = tableNameSplitByDot[0];
var tablePureName = tableNameSplitByDot[1];
var str = $#"DECLARE #var{dropConstraintCount} nvarchar(128)
SELECT #var{dropConstraintCount} = name
FROM sys.default_constraints
WHERE parent_object_id = object_id(N'{tableSchema}.[{tablePureName}]')
AND col_name(parent_object_id, parent_column_id) = '{columnName}';
IF #var{dropConstraintCount} IS NOT NULL
EXECUTE('ALTER TABLE {tableSchema}.[{tablePureName}] DROP CONSTRAINT [' + #var{dropConstraintCount} + ']')";
dropConstraintCount++;
return str;
}
}
Your model properties don't have to be 'auto properties' Even though that is easier. And the DefaultValue attribute is really only informative metadata
The answer accepted here is one alternative to the constructor approach.
public class Track
{
private const int DEFAULT_LENGTH = 400;
private int _length = DEFAULT_LENGTH;
[DefaultValue(DEFAULT_LENGTH)]
public int LengthInMeters {
get { return _length; }
set { _length = value; }
}
}
vs.
public class Track
{
public Track()
{
LengthInMeters = 400;
}
public int LengthInMeters { get; set; }
}
This will only work for applications creating and consuming data using this specific class. Usually this isn't a problem if data access code is centralized. To update the value across all applications you need to configure the datasource to set a default value. Devi's answer shows how it can be done using migrations, sql, or whatever language your data source speaks.
What I did, I initialized values in the constructor of the entity
Note: DefaultValue attributes won't set the values of your properties automatically, you have to do it yourself
I admit that my approach escapes the whole "Code First" concept. But if you have the ability to just change the default value in the table itself... it's much simpler than the lengths that you have to go through above... I'm just too lazy to do all that work!
It almost seems as if the posters original idea would work:
[DefaultValue(true)]
public bool IsAdmin { get; set; }
I thought they just made the mistake of adding quotes... but alas no such intuitiveness. The other suggestions were just too much for me (granted I have the privileges needed to go into the table and make the changes... where not every developer will in every situation). In the end I just did it the old fashioned way. I set the default value in the SQL Server table... I mean really, enough already! NOTE: I further tested doing an add-migration and update-database and the changes stuck.
After #SedatKapanoglu comment, I am adding all my approach that works, because he was right, just using the fluent API does not work.
1- Create custom code generator and override Generate for a ColumnModel.
public class ExtendedMigrationCodeGenerator : CSharpMigrationCodeGenerator
{
protected override void Generate(ColumnModel column, IndentedTextWriter writer, bool emitName = false)
{
if (column.Annotations.Keys.Contains("Default"))
{
var value = Convert.ChangeType(column.Annotations["Default"].NewValue, column.ClrDefaultValue.GetType());
column.DefaultValue = value;
}
base.Generate(column, writer, emitName);
}
}
2- Assign the new code generator:
public sealed class Configuration : DbMigrationsConfiguration<Data.Context.EfSqlDbContext>
{
public Configuration()
{
CodeGenerator = new ExtendedMigrationCodeGenerator();
AutomaticMigrationsEnabled = false;
}
}
3- Use fluent api to created the Annotation:
public static void Configure(DbModelBuilder builder){
builder.Entity<Company>().Property(c => c.Status).HasColumnAnnotation("Default", 0);
}
It's simple! Just annotate with required.
[Required]
public bool MyField { get; set; }
the resultant migration will be:
migrationBuilder.AddColumn<bool>(
name: "MyField",
table: "MyTable",
nullable: false,
defaultValue: false);
If you want true, change the defaultValue to true in the migration before updating the database
In .NET Core 3.1 you can do the following in the model class:
public bool? Active { get; set; }
In the DbContext OnModelCreating you add the default value.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Foundation>()
.Property(b => b.Active)
.HasDefaultValueSql("1");
base.OnModelCreating(modelBuilder);
}
Resulting in the following in the database
Note:
If you don't have nullable (bool?) for you property you will get the following warning
The 'bool' property 'Active' on entity type 'Foundation' is configured with a database-generated default. This default will always be used for inserts when the property has the value 'false', since this is the CLR default for the 'bool' type. Consider using the nullable 'bool?' type instead so that the default will only be used for inserts when the property value is 'null'.
I found that just using Auto-Property Initializer on entity property is enough to get the job done.
For example:
public class Thing {
public bool IsBigThing{ get; set; } = false;
}
using System.ComponentModel;
[DefaultValue(true)]
public bool Active { get; set; }
In EF core released 27th June 2016 you can use fluent API for setting default value. Go to ApplicationDbContext class, find/create the method name OnModelCreating and add the following fluent API.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<YourTableName>()
.Property(b => b.Active)
.HasDefaultValue(true);
}
Just Overload the default constructor of Model class and pass any relevant parameter which you may or may not use. By this you can easily supply default values for attributes. Below is an example.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Aim.Data.Domain
{
[MetadataType(typeof(LoginModel))]
public partial class Login
{
public Login(bool status)
{
this.CreatedDate = DateTime.Now;
this.ModifiedDate = DateTime.Now;
this.Culture = "EN-US";
this.IsDefaultPassword = status;
this.IsActive = status;
this.LoginLogs = new HashSet<LoginLog>();
this.LoginLogHistories = new HashSet<LoginLogHistory>();
}
}
public class LoginModel
{
[Key]
[ScaffoldColumn(false)]
public int Id { get; set; }
[Required]
public string LoginCode { get; set; }
[Required]
public string Password { get; set; }
public string LastPassword { get; set; }
public int UserGroupId { get; set; }
public int FalseAttempt { get; set; }
public bool IsLocked { get; set; }
public int CreatedBy { get; set; }
public System.DateTime CreatedDate { get; set; }
public Nullable<int> ModifiedBy { get; set; }
public Nullable<System.DateTime> ModifiedDate { get; set; }
public string Culture { get; set; }
public virtual ICollection<LoginLog> LoginLogs { get; set; }
public virtual ICollection<LoginLogHistory> LoginLogHistories { get; set; }
}
}
Even from .NET Core 1.0, It is possible to set default values when you are using the code first approach. See the following code snippet.
using System.ComponentModel;
[DefaultValue(true)]
public bool Active { get; set; }
Read for more: Microsoft official docs
Lets consider you have a class name named Products and you have a IsActive field. just you need a create constructor :
Public class Products
{
public Products()
{
IsActive = true;
}
public string Field1 { get; set; }
public string Field2 { get; set; }
public bool IsActive { get; set; }
}
Then your IsActive default value is True!
Edite :
if you want to do this with SQL use this command :
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.IsActive)
.HasDefaultValueSql("true");
}
The Entity Framework Core Fluent API HasDefaultValue method is used to specify the default value for a database column mapped to a property. The value must be a constant.
public class Contact
{
public int ContactId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public bool IsActive { get; set; }
public DateTime DateCreated { get; set; }
}
public clas SampleContext : DbContext
{
public DbSet<Contact> Contacts { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Context>()
.Propery(p => p.IsActive)
.HasDefaultValue(true);
}
}
Or
like it!
You can also specify a SQL fragment that is used to calculate the default value:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Created)
.HasDefaultValueSql("getdate()");
}
Hmm... I do DB first, and in that case, this is actually a lot easier. EF6 right? Just open your model, right click on the column you want to set a default for, choose properties, and you will see a "DefaultValue" field. Just fill that out and save. It will set up the code for you.
Your mileage may vary on code first though, I haven't worked with that.
The problem with a lot of other solutions, is that while they may work initially, as soon as you rebuild the model, it will throw out any custom code you inserted into the machine-generated file.
This method works by adding an extra property to the edmx file:
<EntityType Name="Thingy">
<Property Name="Iteration" Type="Int32" Nullable="false" **DefaultValue="1"** />
And by adding the necessary code to the constructor:
public Thingy()
{
this.Iteration = 1;
Set the default value for the column in table in MSSQL Server, and in class code add attribute, like this:
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
for the same property.

Incorrect Syntax near keyword "From"

I have this custom repo and when I try execute a query, it returns an exception
If I execute the query using sql string, it does not return an error, but when I use some extention then I have this exception.
Like this:
public override IEnumerable<TableContrato> All()
{
//var query = "select * from Contrato";
//var data = Conn.Query<TableContrato>(query);
var data = Conn.GetList<TableContrato>();
return data;
}
All my entities are created in c# using "Table" Prefix, like TableContrato, and my table is called Contrato
This way, I've build a custom mapper, like this.
public class CustomMapper<TTableEntity> : PluralizedAutoClassMapper<TTableEntity> where TTableEntity : class
{
public override void Table(string tableName)
{
tableName = tableName.Replace("Table", string.Empty).Trim();
base.Table(tableName);
}
}
and this is my repo base
public abstract class ReadOnlyRepositoryBase<TEntity, TTable, TKey> : IReadOnlyRepository<TEntity, TKey>
where TEntity : class where TTable : class
{
protected IDbConnection Conn { get; set; }
protected DapperContext Context { get; private set; }
protected ReadOnlyRepositoryBase()
{
Context = new DapperContext();
Conn = Context.Connection;
InicializaMappings();
}
public void InicializaMappings()
{
global::DapperExtensions.DapperExtensions.DefaultMapper = typeof(CustomMapper<>);
}
}
and here is my exception.
I know I could do all with literal queries, but this way I cannot use Expression trees for filtering, neither generics.
What I'm doing wrong?
EDIT: 26/05/2015 - TableContrato
public class TableContrato
{
public Guid ContratoId { get; set; }
public Guid EmpresaId { get; set; }
public string ContratoNome { get; set; }
public string ContratoCodigo { get; set; }
public DateTime? DataDeCriacao { get; set; }
public Guid? UsuarioQueCriou { get; set; }
public TableEmpresaGrupo Empresa { get; set; }
public virtual ICollection<TableLocal> Locais { get; set; }
}
UPDATE - 31/05/2016 - Sql profiler
Here is an image of Sql Server Profile, of executed Sql.
Aparently, the '*' character is missed.
I think is a configuration error, so here is the map class
public class TableContratoMap : ClassMapper<TableContrato>
{
public TableContratoMap()
{
// ReSharper disable once RedundantBaseQualifier
base.Table("Contrato");
}
}
One more doubt... I'm familiar with EF mapping, where I don't need to map every single column.
Is it really neccessary in Dapper?
You need to call AutoMap within your class mapper. Once you call that, AutoMap will build the collection of fields internally and apply that to the field list within the SQL statement.
public class TableContratoMap : ClassMapper<TableContrato>
{
public TableContratoMap()
{
// ReSharper disable once RedundantBaseQualifier
base.Table("Contrato");
AutoMap();
}
}
One more doubt... I'm familiar with EF mapping, where I don't need to map every single column. Is it really neccessary in Dapper?
No just use dapper contrib and same names in db as code.
Table Persons
Id
Name
Birth
Class Persons
Id
Name
Birth
var person = dapper.Get<Persons>(22);

Mapping a private member and populate it back with a method in MongoDB

With mongodb c# drivers we can map a private member like this
cm.MapField("_sessionTimes").SetElementName("SessionTimes");
And this populates SessionTimes in MongoDb.
This is a private member, it isn't a backing field of a public property.
Is there a way to populate this field back while getting the entity form MongoDb?
My class has AddSessionTime method, ideally I would like to use this method to populate that private field while mongodb drivers deserializing my entity.
By the way the type of _sessionTimes is
IDictionary<DayOfWeek, SessionTime>
SessionTime is a simple class:
public class SessionTime
{
public TimeSpan Start { get; set; }
public TimeSpan End { get; set; }
}
And AddSessionTime is a method in my class:
public void AddSessionTime (DayOfWeek dayOfWeek, SessionTime sessionTime)
{
if (_sessionTimes.ContainsKey(dayOfWeek))
{
_sessionTimes[dayOfWeek] = sessionTime;
}
else
{
_sessionTimes.Add(dayOfWeek, sessionTime);
}
}
Thanks.

How to map JSON field to POCO using Automapper

Is it possible to create mapping between POCO and JSON field using Automapper?
public class SomeObjectEntity
{
//JSON
public string TaskData { get; set; }
public GUID Id { get; set; }
public DateTime CreateTime { get; set; }
}
public class SomeObjectModel
{
public string Name { get; set; }
public string[] Emails { get; set; }
public GUID Id { get; set; }
public DateTime CreateTime { get; set; }
}
In TaskData i have this JSON string:
#"
{
""Name"": ""Denis"",
""EMails"": [
""someemail1#email.com"",
""someemail2#email.com""
]
}"
Is there any way to create map?
protected override void Configure()
{
Mapper.CreateMap<SomeObjectEntity, SomeObjectModel>() ...
Mapper.CreateMap<SomeObjectModel, SomeObjectEntity>() ...
}
Thanks.
From the code above, I see you want to turn the Name + Emails properties from the SomeObjectModel class and turn these into a JSON string and map this to the SomeObjectEntity.TaskData string property. This can be accomplished using a custom AutoMapper ValueResolver.
public class NameAndEmailResolver : ValueResolver<SomeObjectModel, string>
{
protected override string ResolveCore(SomeObjectModel source)
{
// need a quick and dirty list so we can perform linq query to isolate properties and return an anonymous type
var list = new List<SomeObjectModel>(){source};
return JsonConvert.SerializeObject(list.Select(x => new{x.Name, x.Emails});
}
}
And then call your mapper code:
Mapper.CreateMap<SomeObjectEntity, SomeObjectModel>()
.ForMember(x => x.TaskData, map => map.ResolveUsing<NameAndEmailResolver>());
This is only for one way binding. You'll have to write the code to go the opposite direction: SomeObjectModel --> SomeObjectEntity.
As mentioned by Jeroen, you need to first deserialize your json string to its corresponding type. And to do that, you can use the following. Assuming your type corresponding to the json is T, the following will deserialize it and gives you an object you can use it to map.
private async Task<T> ParseJsonToObjectAsync(string jsonValue)
{
var obj = await Task.Factory.StartNew(()=>JsonConvert.DeserializeObject<T>(jsonValue);
return obj;
}
You can also use http://json2csharp.com/ to help you generate the type corresponding your json string. It will save you time.
If SomeObjectEntity or TaskData in your description is the object representation of your json string, then that is what T is.
Once you have the json deserialized, you can either manually map or use https://github.com/AutoMapper/AutoMapper

Categories

Resources