I'm using nhibernate 5,1,1. Mapping by code
when you add an entry, you send 2 requests
select max (id) From Bred
insert Into Bred (Id, Name, PetType) valuses ({value of max (id)}, text, 1)
I need the field id not to be sent in the insert request and there was no first request. Id auto increment
How can I do that?
public abstract class BaseEntity
{
/// <summary>
/// Ин.
/// </summary>
public virtual int Id { get; set; }
/// <summary>
/// Дата добавления
/// </summary>
public virtual DateTime DateInsert { get; set; }
}
public abstract class BaseMapping<T> : ClassMapping<T> where T : BaseEntity
{
protected BaseMapping(string nameTabel)
{
this.Table(nameTabel);
this.Id(x => x.Id, map =>
{
map.Generator(Generators.Increment);
map.Column("\"Id\"");
});
this.Property(x => x.DateInsert, x =>
{
x.Column("\"DateInsert\"");
x.Insert(false);
x.Update(false);
});
}
}
/// <summary>
/// Справочник пород
/// </summary>
public class Breed : BaseEntity
{
/// <summary>
/// Название
/// </summary>
public virtual string Name { get; set; }
/// <summary>
/// Тип животных
/// </summary>
public virtual PetType PetType { get; set; }
}
public class BreedMap : BaseMapping<Breed>
{
public BreedMap() : base("\"Breed\"")
{
this.Property(x => x.Name, x => x.Column("\"Name\""));
this.Property(x => x.PetType, x => x.Column("\"PetType\""));
}
}
I need the field id not to be sent in the insert request and there was no first request...
In case that our DB is supporting IDENTITY (auto increment on DB side), we should not use Increment, but Native setting (or Identity)
//map.Generator(Generators.Increment);
map.Generator(Generators.Native);
Check the doc for detailed explanation
5.1.5.1. generator
small extract
increment
generates identifiers of any integral type that are unique only when
no other process is inserting data into the same table. Do not use in
a cluster.
...
native/identity
supports identity columns in DB2, MySQL, MS SQL Server and Sybase.
The identifier returned by the database is converted to the property
type using Convert.ChangeType. Any integral property type is thus
supported.
...
Related
I'm using c# .NET Framework 4.7.2, MySqlData and MySqlDataEntityFramework 8.0.22.0. This is my DbContext derived class:
/// <summary>
/// This is the class that maps the database table to the project's classes.
/// </summary>
[DbConfigurationType(typeof(MySqlEFConfiguration))]
class DatabaseContext : DbContext
{
/// <summary>
/// Session table.
/// </summary>
public DbSet<Session_table> Sessions { get; set; }
/// <summary>
/// Measure table.
/// </summary>
public DbSet<Measure_table> Measures { get; set; }
public DatabaseContext() : base()
{
//this.Configuration.LazyLoadingEnabled = false;
}
public DatabaseContext(System.Data.Common.DbConnection existingConnection, bool contextOwnConnection)
: base(existingConnection, contextOwnConnection)
{
}
///// <summary>
///// Measure Table.
///// </summary>
//public DbSet<Measure_table> Measures { get; set; }
/// <summary>
/// Override used to map class to database table.
/// </summary>
/// <param name="modelBuilder"></param>
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
#region Session Table
// session_id is the primary key
modelBuilder.Entity<Session_table>().HasKey(e => e.session_id);
// Specify the name of the table
modelBuilder.Entity<Session_table>().ToTable("session");
// Prevent MySql Server to auto-generate the value for primary key
modelBuilder.Entity<Session_table>().Property(a => a.session_id).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None);
// session_id: Specify name and mark as required
modelBuilder.Entity<Session_table>().Property(e => e.session_id).HasColumnName("session_id");
modelBuilder.Entity<Session_table>().Property(e => e.session_id).IsRequired();
// time_start: Specify name and mark as required
modelBuilder.Entity<Session_table>().Property(e => e.sessionTimeStart).HasColumnName("time_start");
modelBuilder.Entity<Session_table>().Property(e => e.sessionTimeStart).IsRequired();
// Time end: Specify name and mark as required
modelBuilder.Entity<Session_table>().Property(e => e.sessionTimeEnd).HasColumnName("time_end");
modelBuilder.Entity<Session_table>().Property(e => e.sessionTimeEnd).IsRequired();
// Description: Specify name and max length
modelBuilder.Entity<Session_table>().Property(e => e.description).HasColumnName("description");
modelBuilder.Entity<Session_table>().Property(e => e.description).HasMaxLength(255);
#endregion Session Table
#region Measure Table
var measureTable = modelBuilder.Entity<Measure_table>();
measureTable.HasKey(e => e.measure_id);
measureTable.HasIndex(a => a.session_id);
measureTable.HasRequired<Session_table>(e => e.session).WithMany().HasForeignKey(e => e.session_id);
measureTable.ToTable("measure");
// Measure Id
measureTable.Property(e => e.measure_id).HasColumnName("measure_id");
measureTable.Property(e => e.measure_id).IsRequired();
// Session Time
measureTable.Property(e => e.session_time).HasColumnName("session_time");
measureTable.Property(e => e.session_time).IsRequired();
// System Time
measureTable.Property(e => e.system_time).HasColumnName("system_time");
measureTable.Property(e => e.system_time).IsRequired();
// SubSystem
measureTable.Property(e => e.sub_system).HasColumnName("sub_system");
measureTable.Property(e => e.sub_system).IsRequired();
// Data Source
measureTable.Property(e => e.dataSource).HasColumnName("data_source");
measureTable.Property(e => e.dataSource).IsRequired();
// Raw
measureTable.Property(e => e.raw).HasColumnName("raw");
measureTable.Property(e => e.raw).IsRequired();
// Calib
measureTable.Property(e => e.calib).HasColumnName("calib");
measureTable.Property(e => e.calib).IsRequired();
// session id
measureTable.Property(e => e.session_id).HasColumnName("session_id");
measureTable.Property(e => e.session_id).IsRequired();
#endregion Measure Table
}
And here i have the two classes representing the database tables:
SESSION TABLE
/// <summary>
/// Session table
/// </summary>
public class Session_table
{
/// <summary>
/// Id of the session, primary key of the table.
/// </summary>
public int session_id { get; set; }
/// <summary>
/// Session datetime Start.
/// </summary>
public DateTime sessionTimeStart { get; set; } = DateTime.UtcNow;
/// <summary>
/// Session DateTime end.
/// </summary>
public DateTime sessionTimeEnd { get; set; } = DateTime.UtcNow;
/// <summary>
/// Session description.
/// Default: "No description"
/// </summary>
public string description { get; set; } = "No Description";
MEASURE TABLE (Has a foreign key for a one to many relationship: One session -> Many measures)
#region Enum
/// <summary>
/// Subsystem's type.
/// </summary>
public enum SubSystem
{
/// <summary>
/// Zerotype.
/// </summary>
ZERO,
/// <summary>
/// 1 type.
/// </summary>
FIRST,
/// <summary>
/// 2 type.
/// </summary>
SECOND
}
/// <summary>
/// Source of the measure.
/// </summary>
public enum DataSource
{
source1,
source_tbd
}
#endregion Enum
#region Properties
/// <summary>
/// Primary key (unique id) for the measure.
/// Default: 0
/// </summary>
public int measure_id { get; set; } = 0;
/// <summary>
/// DateTime of the session.
/// Default: DateTime min value.
/// </summary>
public DateTime session_time { get; set; } = DateTime.MinValue.ToUniversalTime();
/// <summary>
/// DateTime of the System.
/// Default: DateTime min value.
/// </summary>
public DateTime system_time { get; set; } = DateTime.MinValue.ToUniversalTime();
/// <summary>
/// SubSystem:
/// </summary>
public SubSystem sub_system { get; set; } = SubSystem.FIRST;
/// <summary>
/// Source of the measure.
/// </summary>
public DataSource dataSource { get; set; } = DataSource.source1;
/// <summary>
/// Raw value of the measure.
/// Default: 0
/// </summary>
public int raw { get; set; } = 0;
/// <summary>
/// Calibrated value of the measure.
/// Default: 0
/// </summary>
public double calib { get; set; } = 0;
/// <summary>
/// Foreign Key for One to Many relation.
/// </summary>
public virtual Session_table session { get; set; }
/// <summary>
/// Foreign key for the session table.
/// </summary>
public int session_id { get; set; } = 0;
Now i can connect to the database and insert an object (or list) to both table, i can also select some rows by using
var rows = context.Measures.Select(c => new { c.measure_id, c.raw, c.session_id, c.session.description }).Where(c => c.session_id == 666).ToList();
But when i try to get the all the column (the entire row) it fails in an exception "input string was not in the correct format", i've tried:
var rows = from t in context.Measures
where t.session_id == 666
select t;
var rows = (from t in context.Measures
where t.session_id == 666
select t).AsEnumerable<Measure_table>();
var rows = context.Measures.Where(a => a.session_id == 666).AsEnumerable<Measure_table>();
var rows = from a in context.Measures select a;
var rows = context.Measures;
But none of this works...for sure i'm missing something, but i cannot figure out what!
Thanks for any suggestion
I've also tried:
private void selectTest_2(string connet)
{
using (MySqlConnection connection = new MySqlConnection(connet))
{
try
{
connection.Open();
using (DatabaseContext context = new DatabaseContext(connection, false))
{
foreach (var row in context.Measures)
{
Console.WriteLine($"VALUE: {row.calib}" + Environment.NewLine);
System.Threading.Thread.Sleep(2000);
}
// Save to the database
context.SaveChanges();
}
}
catch (Exception exc)
{
Console.WriteLine($"Exception: {exc}");
}
}
}
But result in the same error. Now i'm sure that i'm missing something important...
I've found a workaround...it seems that the enum properties are the problem, by using a string field everything works, for the moment i'm using this "solution" but how can i safely and properly use the enums?
/// <summary>
/// SubSystem: ZERO, FIRST, SECOND
/// </summary>
public SubSystem sub_system_enum { get; set; } = SubSystem.FIRST;
/// <summary>
/// String properties for database compatibility
/// </summary>
public string sub_system
{
get
{
return this.sub_system_enum.ToString();
}
set
{
this.sub_system_enum = Enum.TryParse<SubSystem>(value, true, out SubSystem val) ? val : default(SubSystem);
}
}
And onModelCreating override method:
// IGnore enums
measureTable.Ignore(e => e.sub_system_enum);
measureTable.Ignore(e => e.dataSource_enum);
Database table:
enter image description here
Here i have a Class Like
Public Class Employee
{
[Required]
public string FName { get; set; }
[Required]
public bool Married { get; set; }
public string WifeName { get; set; }
public string Wife_dOB { get; set; }
}
Here WifeName And Wife_Dob is Required only When Married Is True
Please Help me How can i Resolve this Problem
Im using Here MVC
You can use a custom validation attribute.
You can see bellow a solution that I used a few time ago:
/// <summary>
/// Provides conditional validation based on related property value.
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class RequiredIfAttribute : ValidationAttribute
{
#region Properties
/// <summary>
/// Gets or sets the other property name that will be used during validation.
/// </summary>
/// <value>
/// The other property name.
/// </value>
public string OtherProperty { get; private set; }
/// <summary>
/// Gets or sets the display name of the other property.
/// </summary>
/// <value>
/// The display name of the other property.
/// </value>
public string OtherPropertyDisplayName { get; set; }
/// <summary>
/// Gets or sets the other property value that will be relevant for validation.
/// </summary>
/// <value>
/// The other property value.
/// </value>
public object OtherPropertyValue { get; private set; }
/// <summary>
/// Gets or sets a value indicating whether other property's value should match or differ from provided other property's value (default is <c>false</c>).
/// </summary>
/// <value>
/// <c>true</c> if other property's value validation should be inverted; otherwise, <c>false</c>.
/// </value>
/// <remarks>
/// How this works
/// - true: validated property is required when other property doesn't equal provided value
/// - false: validated property is required when other property matches provided value
/// </remarks>
public bool IsInverted { get; set; }
/// <summary>
/// Gets a value that indicates whether the attribute requires validation context.
/// </summary>
/// <returns><c>true</c> if the attribute requires validation context; otherwise, <c>false</c>.</returns>
public override bool RequiresValidationContext
{
get { return true; }
}
#endregion
#region Constructor
/// <summary>
/// Initializes a new instance of the <see cref="RequiredIfAttribute"/> class.
/// </summary>
/// <param name="otherProperty">The other property.</param>
/// <param name="otherPropertyValue">The other property value.</param>
public RequiredIfAttribute(string otherProperty, object otherPropertyValue)
: base("'{0}' is required because '{1}' has a value {3}'{2}'.")
{
this.OtherProperty = otherProperty;
this.OtherPropertyValue = otherPropertyValue;
this.IsInverted = false;
}
#endregion
/// <summary>
/// Applies formatting to an error message, based on the data field where the error occurred.
/// </summary>
/// <param name="name">The name to include in the formatted message.</param>
/// <returns>
/// An instance of the formatted error message.
/// </returns>
public override string FormatErrorMessage(string name)
{
return string.Format(
CultureInfo.CurrentCulture,
base.ErrorMessageString,
name,
this.OtherPropertyDisplayName ?? this.OtherProperty,
this.OtherPropertyValue,
this.IsInverted ? "other than " : "of ");
}
/// <summary>
/// Validates the specified value with respect to the current validation attribute.
/// </summary>
/// <param name="value">The value to validate.</param>
/// <param name="validationContext">The context information about the validation operation.</param>
/// <returns>
/// An instance of the <see cref="T:System.ComponentModel.DataAnnotations.ValidationResult" /> class.
/// </returns>
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (validationContext == null)
{
throw new ArgumentNullException("validationContext");
}
PropertyInfo otherProperty = validationContext.ObjectType.GetProperty(this.OtherProperty);
if (otherProperty == null)
{
return new ValidationResult(
string.Format(CultureInfo.CurrentCulture, "Could not find a property named '{0}'.", this.OtherProperty));
}
object otherValue = otherProperty.GetValue(validationContext.ObjectInstance);
// check if this value is actually required and validate it
if (!this.IsInverted && object.Equals(otherValue, this.OtherPropertyValue) ||
this.IsInverted && !object.Equals(otherValue, this.OtherPropertyValue))
{
if (value == null)
{
return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
}
// additional check for strings so they're not empty
string val = value as string;
if (val != null && val.Trim().Length == 0)
{
return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
}
}
return ValidationResult.Success;
}
FluentValidation might be a good choice for your problem. With FluentValidation it would be easy to create an EmployeeValidator class to perform as much or as little validation as needed.
Note: The examples below are written using the standard FluentValidation package since I don't use MVC. There appears to be nuget packages for several versions of MVC in addition to the standard nuget package and your mileage may vary using those implementations.
Here is a very simple example of how to use FluentValidation:
void Main()
{
var emp = new Employee { FName = "John Smith", Married = true };
var val = new EmployeeValidator();
val.ValidateAndThrow(emp);
}
public class Employee
{
public string FName { get; set; }
public bool Married { get; set; }
public string WifeName { get; set; }
public string Wife_dOB { get; set; }
}
public class EmployeeValidator : AbstractValidator<Employee>
{
public EmployeeValidator()
{
RuleFor(e => e.WifeName).NotEmpty().When(e => e.Married);
RuleFor(e => e.Wife_dOB).NotEmpty().When(e => e.Married);
}
}
EDIT
I forgot to include an example of the output from the validation code above.
Example output:
ValidationException: Validation failed:
-- WifeName: 'Wife Name' must not be empty.
-- Wife_dOB: 'Wife_d OB' must not be empty.
An alternative approach, and in my opinion a better one, would be something like this that requires no validation, at least for the Married property:
public class Person
{
public Person(string firstName, string surname, DateTime dateOfBirth)
{
FirstName = firstName;
Surname = surname;
DateOfBirth = dateOfBirth;
}
public string FirstName { get; set; }
public string Surname { get; set; }
public string FullName => $"{FirstName} {Surname}";
public DateTime DateOfBirth { get; }
}
public class Employee : Person
{
public Employee(string firstName, string lastName, DateTime dateOfBirth, DateTime dateOfHire)
: base(firstName, lastName, dateOfBirth)
=> DateOfHire = dateOfHire;
public DateTime DateOfHire { get; }
public Person Spouse { get; set; }
public bool IsMarried => Spouse != null;
}
With this implementation the IsMarried property simply projects whether or not the Spouse property is set. This is purely for convenience but can often be helpful.
Some validation that might make sense for these objects could be the following:
public class PersonValidator : AbstractValidator<IPerson>
{
public PersonValidator()
{
RuleFor(p => p.FirstName).NotEmpty();
RuleFor(p => p.Surname).NotEmpty();
RuleFor(p => p.DateOfBirth).SetValidator(new DateOfBirthValidator());
}
}
public class EmployeeValidator : AbstractValidator<IEmployee>
{
private static readonly DateTime CompanyInceptionDate
= DateTime.Today.Subtract(TimeSpan.FromDays(365.25d * 10d));
public EmployeeValidator()
{
// Person rules
RuleFor(p => p.FirstName).NotEmpty();
RuleFor(p => p.Surname).NotEmpty();
RuleFor(p => p.DateOfBirth).SetValidator(new DateOfBirthValidator());
// Employee rules
RuleFor(e => e.DateOfHire).SetValidator(
// Can't be in the future nor older than the company
new DateRangeValidator(CompanyInceptionDate, DateTime.Today));
}
}
// This class really isn't necessary
// The built-in InclusiveBetween/ExclusiveBetween validators would work just as well
public class DateRangeValidator : PropertyValidator
{
protected const double DaysInAYear = 365.25d;
public DateRangeValidator(DateTime from, DateTime to)
: base($"{{PropertyName}} out of range. Expected between {from:yyyy-MM-dd} and {to:yyyy-MM-dd}.")
{
From = from;
To = to;
}
public DateTime From { get; }
public DateTime To { get; }
protected override bool IsValid(PropertyValidatorContext context)
=> context.PropertyValue is DateTime date
? date >= From && date <= To
: false;
}
public class DateOfBirthValidator : DateRangeValidator
{
private static readonly TimeSpan OneHundredAndFiftyYears
= TimeSpan.FromDays(DaysInAYear * 150d);
public DateOfBirthValidator()
// Can't be in the future nor older than 150 years
: base(DateTime.Today.Subtract(OneHundredAndFiftyYears), DateTime.Today) { }
}
I want to create an application that makes use of a NoSQL database in such a way to it plays nicely with my domain entities.
Right now when I create a "domain entity" for MongoDB then I have to define it something like this:
public class User
{
[BsonId]
public int Id {get;set;}
[BsonElement("Username")]
public string Username {get;set;}
[BsonElement("Email")]
public string Email {get;set;}
}
But this means my entity isn't persistence ignorant. So how can I achieve that when using a NoSQL database like MongoDB?
One way is to define a POCO class in your domain like this
namespace MyApp.Domain
{
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Email { get; set; }
}
}
and define the same class for each persistence type, for example MongoDB
namespace MyApp.MongoDb
{
public class User
{
[BsonId]
public int Id { get; set; }
[BsonElement("Username")]
public string Username { get; set; }
[BsonElement("Email")]
public string Email { get; set; }
}
}
You business logic will interact with your domain classes through repository interfaces, and when you persist data, you just need copy the instance over.
You don't have to write copying data code manually because automapper can help you with that. Alternatively, you can use my simplified code to copy data between domain classes below:
/// <summary>
/// Copy public fields from an instance of the source type TV to an instance of the destination type T.
/// A source property will be copied if there is a property in the destination type with the same name.
/// For instance, TV.Name will be copied to T.Name
/// </summary>
/// <typeparam name="T">The destination type</typeparam>
/// <typeparam name="TV">The source type</typeparam>
/// <param name="input">The source data</param>
/// <param name="existingInstance">The instance that we want to copy values to, if it is null a new instance will be created</param>
/// <returns>An instance of type T</returns>
public static T CopyFields< TV,T>(TV input, T existingInstance=null)where T:class where TV:class
{
var sourcePublicFields = typeof (TV).GetProperties();
var instance =existingInstance ?? Activator.CreateInstance<T>();
var destinationPublicFields = typeof(T).GetProperties();
Debug.WriteLine("Copying data from source type {0} to destination type {1}", typeof(TV), typeof(T));
foreach (var field in sourcePublicFields)
{
var destinationField = destinationPublicFields.FirstOrDefault(it => it.Name == field.Name);
if (destinationField == null || destinationField.PropertyType != field.PropertyType)
{
Debug.WriteLine("No Destination Field matched with the source field. Source Field name {0}, source field type {1} ", field.Name, field.PropertyType);
continue;
}
var sourceValue = field.GetValue(input);
//Set the value
destinationField.SetValue(instance,sourceValue);
}
return instance;
}
Your entities can be POCO objects.
Almost all your rules you can resolve by using ConvetionRegistry.
Fox example this code set Id as string and ignore extra fields.
var convention = new ConventionPack {
new IgnoreExtraElementsConvention(true),
new IdGeneratorConvention() };
ConventionRegistry.Register("CubeConventions", convention, x => true);
public class IdGeneratorConvention : ConventionBase, IPostProcessingConvention
{
public void PostProcess(BsonClassMap classMap)
{
var idMemberMap = classMap.IdMemberMap;
if (idMemberMap == null || idMemberMap.IdGenerator != null)
{
return;
}
idMemberMap.SetIdGenerator(StringObjectIdGenerator.Instance);
}
}
In Datatables 1.10 the ajax server side parameters changed from
public class DataTableParamModel
{
public string sEcho{ get; set; }
public string sSearch{ get; set; }
public int iDisplayLength{ get; set; }
public int iDisplayStart{ get; set; }
public int iColumns{ get; set; }
public int iSortingCols{ get; set; }
public string sColumns{ get; set; }
}
to (API Here http://datatables.net/manual/server-side)
columns[i][data]
columns[i][name]
columns[i][orderable]
columns[i][search][regex]
columns[i][search][value]
columns[i][searchable]
...
draw
length
order[i][column]
order[i][dir]
...
search[regex]
search[value]
start
Some are easy to bind
public class DataTableParamModel
{
public string draw { get; set; }
public int length{ get; set; }
public int start { get; set; }
}
But the new array format looks tricky.
What is the new appropriate model to map the new parameter format?
Here is a model binder and class that will bind these new parameters...
Nuget Package: https://www.nuget.org/packages/Wetware.DataTables
Parameter Model:
[ModelBinder(typeof(DTModelBinder))]
public class DTParameterModel
{
/// <summary>
/// Draw counter. This is used by DataTables to ensure that the Ajax returns from
/// server-side processing requests are drawn in sequence by DataTables
/// </summary>
public int Draw { get; set; }
/// <summary>
/// Paging first record indicator. This is the start point in the current data set
/// (0 index based - i.e. 0 is the first record)
/// </summary>
public int Start { get; set; }
/// <summary>
/// Number of records that the table can display in the current draw. It is expected
/// that the number of records returned will be equal to this number, unless the
/// server has fewer records to return. Note that this can be -1 to indicate that
/// all records should be returned (although that negates any benefits of
/// server-side processing!)
/// </summary>
public int Length { get; set; }
/// <summary>
/// Global Search for the table
/// </summary>
public DTSearch Search { get; set; }
/// <summary>
/// Collection of all column indexes and their sort directions
/// </summary>
public IEnumerable<DTOrder> Order { get; set; }
/// <summary>
/// Collection of all columns in the table
/// </summary>
public IEnumerable<DTColumn> Columns { get; set; }
}
/// <summary>
/// Represents search values entered into the table
/// </summary>
public sealed class DTSearch
{
/// <summary>
/// Global search value. To be applied to all columns which have searchable as true
/// </summary>
public string Value { get; set; }
/// <summary>
/// true if the global filter should be treated as a regular expression for advanced
/// searching, false otherwise. Note that normally server-side processing scripts
/// will not perform regular expression searching for performance reasons on large
/// data sets, but it is technically possible and at the discretion of your script
/// </summary>
public bool Regex { get; set; }
}
/// <summary>
/// Represents a column and it's order direction
/// </summary>
public sealed class DTOrder
{
/// <summary>
/// Column to which ordering should be applied. This is an index reference to the
/// columns array of information that is also submitted to the server
/// </summary>
public int Column { get; set; }
/// <summary>
/// Ordering direction for this column. It will be asc or desc to indicate ascending
/// ordering or descending ordering, respectively
/// </summary>
public string Dir { get; set; }
}
/// <summary>
/// Represents an individual column in the table
/// </summary>
public sealed class DTColumn
{
/// <summary>
/// Column's data source
/// </summary>
public string Data { get; set; }
/// <summary>
/// Column's name
/// </summary>
public string Name { get; set; }
/// <summary>
/// Flag to indicate if this column is orderable (true) or not (false)
/// </summary>
public bool Orderable { get; set; }
/// <summary>
/// Flag to indicate if this column is searchable (true) or not (false)
/// </summary>
public bool Searchable { get; set; }
/// <summary>
/// Search to apply to this specific column.
/// </summary>
public DTSearch Search { get; set; }
}
Model Binder:
/// <summary>
/// Model Binder for DTParameterModel (DataTables)
/// </summary>
public class DTModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
base.BindModel(controllerContext, bindingContext);
var request = controllerContext.HttpContext.Request;
// Retrieve request data
var draw = Convert.ToInt32(request["draw"]);
var start = Convert.ToInt32(request["start"]);
var length = Convert.ToInt32(request["length"]);
// Search
var search = new DTSearch
{
Value = request["search[value]"],
Regex = Convert.ToBoolean(request["search[regex]"])
};
// Order
var o = 0;
var order = new List<DTOrder>();
while (request["order[" + o + "][column]"] != null)
{
order.Add(new DTOrder
{
Column = Convert.ToInt32(request["order[" + o + "][column]"]),
Dir = request["order[" + o + "][dir]"]
});
o++;
}
// Columns
var c = 0;
var columns = new List<DTColumn>();
while (request["columns[" + c + "][name]"] != null)
{
columns.Add(new DTColumn
{
Data = request["columns[" + c + "][data]"],
Name = request["columns[" + c + "][name]"],
Orderable = Convert.ToBoolean(request["columns[" + c + "][orderable]"]),
Searchable = Convert.ToBoolean(request["columns[" + c + "][searchable]"]),
Search = new DTSearch
{
Value = request["columns[" + c + "][search][value]"],
Regex = Convert.ToBoolean(request["columns[" + c + "][search][regex]"])
}
});
c++;
}
return new DTParameterModel
{
Draw = draw,
Start = start,
Length = length,
Search = search,
Order = order,
Columns = columns
};
}
}
Usage:
MyController.cs
public JsonResult DataTablesList(DTParameterModel model)
{
...
}
MVC6
If you're going to MVC6 you no longer need the model binder as MVC6 includes a JQueryFormValueProvider into the default model binder that can bind these values.
The model classes themselves may still be useful, however.
There is a bug to be fixed in 2.1.0 that doesn't allow binding for HttpGet but still works for HttpPost
Give this a try #shoe: datatables-mvc project: https://github.com/ALMMa/datatables-mvc
I changed my javascript to use the legacy ajax params option which uses the old parameters to send to the server. This is done through $.fn.dataTable.ext.legacy.ajax = true; so now my code becomes something like...
$.fn.dataTable.ext.legacy.ajax = true;
var datatable = $('#data-table').DataTable({
"processing": true,
"serverSide": true,
"ajax": "MyController/AjaxHandlerPaging",
"pageLength": 25,
"order": [[2, 'desc']],
"columns": []
});
Know this post is 2 years old but to those who want to use this with ASP.Net Core MVC 6. This is the converted/ upgraded answer provided by #Shoe
Model Binder:
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace TrackingAndTraining.Models
{
/// <summary>
/// Model Binder for DTParameterModel (DataTables)
/// </summary>
public class DTModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
var request = bindingContext.ActionContext.HttpContext.Request.Form;
// Retrieve request data
var draw = Convert.ToInt32(request["draw"]);
var start = Convert.ToInt32(request["start"]);
var length = Convert.ToInt32(request["length"]);
// Search
var search = new DTSearch
{
Value = request["search[value]"],
Regex = Convert.ToBoolean(request["search[regex]"])
};
// Order
var o = 0;
var order = new List<DTOrder>();
while (!string.IsNullOrEmpty(request["order[" + o + "][column]"]))
{
order.Add(new DTOrder
{
Column = Convert.ToInt32(request["order[" + o + "][column]"]),
Dir = request["order[" + o + "][dir]"]
});
o++;
}
// Columns
var c = 0;
var columns = new List<DTColumn>();
while (!string.IsNullOrEmpty(request["columns[" + c + "][name]"]))
{
columns.Add(new DTColumn
{
Data = request["columns[" + c + "][data]"],
Name = request["columns[" + c + "][name]"],
Orderable = Convert.ToBoolean(request["columns[" + c + "][orderable]"]),
Searchable = Convert.ToBoolean(request["columns[" + c + "][searchable]"]),
Search = new DTSearch
{
Value = request["columns[" + c + "][search][value]"],
Regex = Convert.ToBoolean(request["columns[" + c + "][search][regex]"])
}
});
c++;
}
var result = new DTParameterModel
{
Draw = draw,
Start = start,
Length = length,
Search = search,
Order = order,
Columns = columns
};
bindingContext.Result = ModelBindingResult.Success(result);
return TaskCache.CompletedTask;
}
}
}
Parameter Model:
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace TrackingAndTraining.Models
{
[ModelBinder(BinderType = typeof(DTModelBinder))]
public class DTParameterModel
{
/// <summary>
/// Draw counter. This is used by DataTables to ensure that the Ajax returns from
/// server-side processing requests are drawn in sequence by DataTables
/// </summary>
public int Draw { get; set; }
/// <summary>
/// Paging first record indicator. This is the start point in the current data set
/// (0 index based - i.e. 0 is the first record)
/// </summary>
public int Start { get; set; }
/// <summary>
/// Number of records that the table can display in the current draw. It is expected
/// that the number of records returned will be equal to this number, unless the
/// server has fewer records to return. Note that this can be -1 to indicate that
/// all records should be returned (although that negates any benefits of
/// server-side processing!)
/// </summary>
public int Length { get; set; }
/// <summary>
/// Global Search for the table
/// </summary>
public DTSearch Search { get; set; }
/// <summary>
/// Collection of all column indexes and their sort directions
/// </summary>
public List<DTOrder> Order { get; set; }
/// <summary>
/// Collection of all columns in the table
/// </summary>
public List<DTColumn> Columns { get; set; }
}
/// <summary>
/// Represents search values entered into the table
/// </summary>
public sealed class DTSearch
{
/// <summary>
/// Global search value. To be applied to all columns which have searchable as true
/// </summary>
public string Value { get; set; }
/// <summary>
/// true if the global filter should be treated as a regular expression for advanced
/// searching, false otherwise. Note that normally server-side processing scripts
/// will not perform regular expression searching for performance reasons on large
/// data sets, but it is technically possible and at the discretion of your script
/// </summary>
public bool Regex { get; set; }
}
/// <summary>
/// Represents a column and it's order direction
/// </summary>
public sealed class DTOrder
{
/// <summary>
/// Column to which ordering should be applied. This is an index reference to the
/// columns array of information that is also submitted to the server
/// </summary>
public int Column { get; set; }
/// <summary>
/// Ordering direction for this column. It will be asc or desc to indicate ascending
/// ordering or descending ordering, respectively
/// </summary>
public string Dir { get; set; }
}
/// <summary>
/// Represents an individual column in the table
/// </summary>
public sealed class DTColumn
{
/// <summary>
/// Column's data source
/// </summary>
public string Data { get; set; }
/// <summary>
/// Column's name
/// </summary>
public string Name { get; set; }
/// <summary>
/// Flag to indicate if this column is orderable (true) or not (false)
/// </summary>
public bool Orderable { get; set; }
/// <summary>
/// Flag to indicate if this column is searchable (true) or not (false)
/// </summary>
public bool Searchable { get; set; }
/// <summary>
/// Search to apply to this specific column.
/// </summary>
public DTSearch Search { get; set; }
}
}
Again all credit goes to #Shoe for original post.
I ran into the same issue when moving to 1.10. Basically, I changed my parameter class like this (getting rid of the unsupported parameters):
public class jQueryDataTableParamModel
{
/// <summary>
/// Request sequence number sent by DataTable,
/// same value must be returned in response
/// </summary>
public string draw { get; set; }
/// <summary>
/// Number of records that should be shown in table
/// </summary>
public int length { get; set; }
/// <summary>
/// First record that should be shown(used for paging)
/// </summary>
public int start { get; set; }
}
In my controller, I get the search value, sort order, and sort column like this:
var searchString = Request["search[value]"];
var sortColumnIndex = Convert.ToInt32(Request["order[0][column]"]);
var sortDirection = Request["order[0][dir]"]; // asc or desc
The full server side binding implementation can be found here. I ran into a similar issue and this was how I went about solving it.
I have the following mapping using code-first:
{
/// <summary>
/// Entity Framework Dc.Dc database table object representation
/// </summary>
[Table("DCDC", Schema = "MZMESDB")]
public class EfDcDc
{
/// <summary>
/// Element ID
/// </summary>
[Column("ID")]
public int Id { get; set; }
/// <summary>
/// Name of DC
/// </summary>
[Column("NAME")]
public string Name { get; set; }
/// <summary>
/// DC Description
/// </summary>
[Column("DESCRIPTION")]
public string Description { get; set; }
/// <summary>
/// Foreign Key
/// </summary>
public virtual EfSystemDataModule Module { get; set; }
/// <summary>
/// Name of module
/// </summary>
[Column("ENABLED")]
public string Enabled { get; set; }
}
}
and
{
/// <summary>
/// Entity Framework SystemData.Module database table object representation
/// </summary>
[Table("SYSTEMDATAMODULE", Schema = "MZMESDB")]
public class EfSystemDataModule
{
/// <summary>
/// Element ID
/// </summary>
[Column("ID")]
public int Id { get; set; }
/// <summary>
/// Name of module
/// </summary>
[Column("NAME")]
public string Name { get; set; }
/// <summary>
/// If the module is installed. Char because Oracle does not support boolean
/// </summary>
[Column("INSTALLED")]
public char Installed { get; set; }
/// <summary>
/// If the module is enabled. Char because Oracle does not support boolean
/// </summary>
[Column("ENABLED")]
public char Enabled { get; set; }
}
}
Oracle tables has a foreign key:
CREATE TABLE "MZMESDB"."DCDC" (
"ID" INTEGER NOT NULL ,
"NAME" VARCHAR2(64) NOT NULL ,
"DESCRIPTION" VARCHAR(256),
"MODULE_ID" INTEGER NOT NULL,
"MODULE_NAME" VARCHAR(64) NOT NULL,
"ENABLED" CHAR NOT NULL,
PRIMARY KEY ("ID") VALIDATE,
FOREIGN KEY (MODULE_ID, MODULE_NAME) REFERENCES SYSTEMDATAMODULE(ID, NAME)
No problems at compilation, but at rutime when I issue:
MzDbContext dbContext = new MzDbContext();
EfDcDc configuredDC = new EfDcDc();
try
{
configuredDC = dbContext.efDcDc.FirstOrDefault(item => item.Name == "COMMON_NAME");
}
catch (Exception e)
{
Debug.WriteLine("Error reading from database. Message: " + e.Message.ToString());
return false;
}
I get the following error from Oracle EF driver:
ORA-00904: \"Extent1\"."\"Module_Id\":Invalid identifier.
I just wanna check if the DcDc element exists in the database to later take its data for processing. what am I doing wrong ?
Rds
This is usually from an invalid column name. In Oracle, everything is in upper case for the column names. Where is the mixed case Module_Id coming from? I only see the ID used in your code.
The problem is related to EF internal mechanism that is not uppercasing some internal names...
So, Module_Id does not exist in Oracle, as the table fields are MODEL_ID. I´m still working around on this...