Mapping a DateTime property to a timestamp mysq+fluentnhibernate - mysql

I have a little problem with fluentNhibernate and MySQL.
I would like to map my entity:
public class Topic
{
public Topic()
{
ParentTopic = null;
}
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual DateTime CreatedAt { get; private set; }
public virtual Guid CreatedBy { get; set; }
public virtual IList<Post> Posts { get; set; }
public virtual Topic ParentTopic { get; set; }
}
To a table with the same name. My bigest problem is how to I do the mapping of the CreatedAt so that in the database I get a timestamp that is only changed on insert and ignored when updating.
thx ;)

Found the answer to my little problem :)
public class TopicMap : ClassMap<Topic>
{
public TopicMap()
{
Table("Topics");
Id(t => t.Id).GeneratedBy.Guid();
Map(t => t.CreatedAt).Not.Nullable().Generated.Insert().CustomSqlType("timestamp");
Map(t => t.CreatedBy).Not.Nullable();
Map(t => t.Name).Not.Nullable().Length(500);
HasMany(t => t.Posts);
References(t => t.ParentTopic).Nullable().Cascade.All().ForeignKey("FK_Topic_ParentTopic");
}
}
This seams to work in my unit tests. Hope that it will not produce any greater problems in the future.
If anybody seas a problem with this then please let me know.

Related

EF Core 2 Stopping Circular Dependency on Many to Many Relationship

I am using the Sakila Sample Database from MySql on a MySql server. The Diagram looks as follows.
The important tables are the store, inventory and film tables. The is a many-to-many relationship between the tables and the linker table is the inventory table.
I scaffolded this Database in a new dotnetcore project using EFCore 2.
I am trying to get a list of stores and their list of films.
The Entities are defined as follows:
Store
public class Store
{
public Store()
{
Customer = new HashSet<Customer>();
Inventory = new HashSet<Inventory>();
Staff = new HashSet<Staff>();
}
public byte StoreId { get; set; }
public byte ManagerStaffId { get; set; }
public short AddressId { get; set; }
public DateTimeOffset LastUpdate { get; set; }
public Address Address { get; set; }
public Staff ManagerStaff { get; set; }
public ICollection<Customer> Customer { get; set; }
public ICollection<Inventory> Inventory { get; set; }
public ICollection<Staff> Staff { get; set; }
}
Inventory
public partial class Inventory
{
public Inventory()
{
Rental = new HashSet<Rental>();
}
public int InventoryId { get; set; }
public short FilmId { get; set; }
public byte StoreId { get; set; }
public DateTimeOffset LastUpdate { get; set; }
public Film Film { get; set; }
public Store Store { get; set; }
public ICollection<Rental> Rental { get; set; }
}
Film
public partial class Film
{
public Film()
{
FilmActor = new HashSet<FilmActor>();
FilmCategory = new HashSet<FilmCategory>();
Inventory = new HashSet<Inventory>();
}
public short FilmId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public short? ReleaseYear { get; set; }
public byte LanguageId { get; set; }
public byte? OriginalLanguageId { get; set; }
public byte RentalDuration { get; set; }
public decimal RentalRate { get; set; }
public short? Length { get; set; }
public decimal ReplacementCost { get; set; }
public string Rating { get; set; }
public string SpecialFeatures { get; set; }
public DateTimeOffset LastUpdate { get; set; }
public Language Language { get; set;
public Language OriginalLanguage { get; set; }
public ICollection<FilmActor> FilmActor { get; set; }
public ICollection<FilmCategory> FilmCategory { get; set; }
public ICollection<Inventory> Inventory { get; set; }
}
My context looks as follows:
modelBuilder.Entity<Inventory>(entity =>
{
entity.ToTable("inventory", "sakila");
entity.HasIndex(e => e.FilmId)
.HasName("idx_fk_film_id");
entity.HasIndex(e => new { e.StoreId, e.FilmId })
.HasName("idx_store_id_film_id");
And lastly the repo looks as follows:
public IEnumerable<Store> GetStores()
{
return _context.Store.
Include(a => a.Inventory).
ToList();
}
Problem:
When I call this method from a Controller to get the list of stores I don´t get any json response on Postman. Yet if I debug into the list that is returned from the Controller I find the list of stores.
The problem is that the list contains:
store->inventory->film->store->inventory->film->store... Etc. Creating a circular dependency that fills up the allowed Process memory of the request.
Possible Solutions:
I think it has to do with the fact that on the Context both the Foreign Keys are defined as HasIndex instead of HasKey
entity.HasIndex(e => new { e.StoreId, e.FilmId })
.HasName("idx_store_id_film_id");
When I define it as HasKey then I get an Error:
'The relationship from 'Rental.Inventory' to 'Inventory.Rental' with
foreign key properties {'InventoryId' : int} cannot target the primary
key {'StoreId' : byte, 'FilmId' : short} because it is not compatible.
Configure a principal key or a set of compatible foreign key
properties for this relationship.'
To answer #hamzas comment, I did find a solution to this problem. I used EFCore to build the entities and the DBContext through scaffolding (DB First). As a best practice you should be using Models (Dtos) to represent the Data for the client. EFCore is very helpful in giving us the flexibility to access this M to N relationship however we want. This gives us the flexibility to represent this Data to the client however we want.
Whatever your use case might be. You have to convert the M to N relationship into an 1 to N model.
Use Case #1: You want to show all the movies for a specific store.
Solution
Step #1: You create a StoreDto (Model)
public class StoreDto
{
int StoreId { get; set; }
ICollection<FilmDto> Films { get; set; }
= new List<FilmDto> ();
}
Step #2: Create a FilmDto
public class FilmDto
{
int FilmId { get; set; }
int StoreId { get; set; }
string FilmName { get; set; }
}
Step #3: You provide a Mapping with auto mapper
public class MappingProfiles : Profile
{
public MappingProfiles()
{
CreateMap<Store, StoreDto>();
CreateMap<Film, FilmDto>();
}
}
Step #4: Query the data correctly, Unfortunately I don´t have this example anymore to test this code, so here is where you´ll have to experiment a bit
public Store GetFilmsForStore(byte StoreId)
{
return _context.Store.
Include(a => a.Inventory).
ThenInclude(i => i.Film)
ToList();
}
On the "Include" part you want to only get the Inventory entries where StoreId == Inverntory.StoreId and then Include the Films Object from the resulting list.
I hope you get the jist of it. You want to break up your m to n relationships and make them seem like 1 to m for your clients.

REST API returns "bad array" instead of JSON object

I'm building REST API server in .NET core. I'm testing my code via Postman software. I have a problem with Include() method that enables me to attach navigation property data. I'm trying to get data in [HttpGet] action and objects that are being returned are wrong.
My code :
MODELS
Session model
public class Session
{
[Key]
public int IDSession { get; set; }
[Required]
public DateTime LogInTime { get; set; }
public DateTime LogOutTime { get; set; }
[Required]
public int IDUser { get; set; }
public User User { get; set; }
[Required]
public int IDMachine { get; set; }
public Machine Machine { get; set; }
}
User model
public class User
{
[Key]
public int IDUser { get; set; }
[Required]
public string Forename { get; set; }
[Required]
public string Name { get; set; }
public string AvatarPath { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public string Password { get; set; }
public User CreatedBy { get; set; }
public DateTime CreatedAt { get; set; }
public List<UserGroup> UsersGroups { get; set; }
public List<Alarm> ExecutedAlarms { get; set; }
public List<Alarm> ResetedAlarms { get; set; }
public List<AccessCard> Cards { get; set; }
public List<AccessCard> UserCardsAdded { get; set; }
public List<User> UsersAdded { get; set; }
public List<Session> Sessions { get; set; }
public List<EventsLog> Events { get; set; }
public List<Reference> References { get; set; }
public List<UserPermission> UsersPermissions { get; set; }
}
Session controller
[Produces("application/json")]
[Route("api/Sessions")]
public class SessionsController : Controller
{
private readonly DBContext _context;
#region CONSTRUCTOR
public SessionsController(DBContext context)
{
_context = context;
}
#endregion
#region HTTP GET
// GET: api/sessions
[HttpGet]
public async Task<IActionResult> GetSessions()
{
var sessions = await _context.Sessions.Include(s => s.User). ToListAsync();
if (sessions.Any())
{
return new ObjectResult(sessions);
}
else
{
return NotFound();
}
}
// GET: api/sessions/1
[HttpGet("{id}", Name = "GetSessionByID")]
public async Task<IActionResult> GetSessionByID(Int32 id)
{
var session = await _context.Sessions.Include(s => s.User).FirstOrDefaultAsync(s => s.IDSession == id);
if (session == null)
{
return NotFound();
}
else
{
return new ObjectResult(session);
}
}
#endregion
}
The idea is that User model contains List<Session> collection that he/she created. I want to be able to return users with its sessions
Of course Session model contains a single User because every session is related with a specific, single User.
Now, when I need to get all sessions objects in SessionController with GetSessions() or GetSessionsByID() I use POSTMAN [HttpGet] action like this : http://localhost:8080/api/sessions which returns me wrong data :
A session contains a user and in turn a single user is related with its sessions. It looks like it tries to return me Session object properly, includes User object but then it tries to include all sessions for that User. That's not what I want. It looks like some kind of a loop. Sessions shoud be returned with its User objects and that's it. How can I achieve that ? Am I doing some logical mistake in my models ?
Thanks !
I met also this issue recently. So, I've fixed it by adding this script in the Startup.cs file and ConfigureServices method :
services.AddMvc().AddJsonOptions(
options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
So, you suffix services.AddMvc() by this code who means that you have to make JSON.Net to ignore cycles finded to the nested object request. And of course having Newtonsoft.Json package installed to your project and referenced in each concerned file
For much clearer information, see this link at Related Data and Serialization section :
https://learn.microsoft.com/en-us/ef/core/querying/related-data
Hope this is helpfull for you

Issues creating inheritance with fluent NHibernate

I trying to create inheritance so I can have some super classes with general properties and then more specialized classes that are the ones getting used.
I want something like the Table per type or table per class they make here http://www.codeproject.com/Articles/232034/Inheritance-mapping-strategies-in-Fluent-Nhibernat
So I have my supertype Game here
public abstract class Game
{
public virtual int Id { get; set; }
public virtual List<UserGame> UserList { get; set; }
}
public class GameMap : ClassMap<Game>
{
public GameMap()
{
Id(x => x.Id, "GameId")
.GeneratedBy
.HiLo("100");
HasMany(x => x.UserList)
.Cascade.All();
}
}
And then my specialized class here QMGameActive
public class QMGameActive : Game
{
public virtual DateTime LastUpdate { get; set; }
public virtual string SelectedBrick { get; set; }
public virtual int Score { get; set; }
public virtual string History { get; set; }
public virtual int StartingPlayerId { get; set; }
public QMGameActive()
{
LastUpdate = DateTime.Now;
History = "";
SelectedBrick = "";
}
}
public class QMGameActiveMap : SubclassMap<QMGameActive>
{
public QMGameActiveMap()
{
KeyColumn("GameId");
Map(x => x.LastUpdate);
Map(x => x.SelectedBrick);
Map(x => x.Score);
Map(x => x.History);
Map(x => x.StartingPlayerId);
}
}
But when I get a diagram from the server I can see there is no connection between Game and QMGameActive there
So what am I missing to make it use inheritance?
I am fairly sure that if you KeyColumn("GameId"); from the QMGameActiveMap() then NHibernate will generate QMGameActive with an ID Column of GameID which will be a foreign key of Game.GameId. which would seem to give you what you want.
(sorry away from home and cannot try code out to make sure).

Best approach for my EF 'DataBase'

I've been a few weeks working on a web project, amd mostly thinking how would I implement data layer. I chosed Entity Framework 4.1, code first model.
So, among lot of other entities , think of PLAYER who has N CHARACTER, that can be in 0..1 GUILD
public class Player
{
public int Id { get; set; }
public string Login { get; set; }
public string Password { get; set; }
public bool IsLogged { get; set; }
public DateTime LastActivity { get; set; }
public DateTime LastLogin { get; set; }
public DateTime LastLogout { get; set; }
public string DisplayName { get; set; }
public string DefaultImage { get; set; }
public virtual Board Board { get; set; }
public virtual PlayerData PlayerData { get; set; }
public virtual ICollection<Character> Characters { get; set; }
}
public class Guild
{
public int Id { get; set; }
public string Name { get; set; }
public string DefaultImage { get; set; }
public virtual ICollection<Character> Characters { get; set; }
}
public class Character
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Player Player { get; set; }
public virtual Guild Guild { get; set; }
public virtual GuildRank GuildRank { get; set; }
public virtual Game Game { get; set; }
}
As you can see, there a lot more entities and relationships, but this will work.
Well it does not, this code:
Character character = mod.Characters.Where(c => c.Player == player).FirstOrDefault();
Throws an exception:
Unable to create a constant value of type 'DataObjects.Player'. Only
primitive types ('such as Int32, String, and Guid') are supported in
this context.
I don't understand why.
I also tried with using [Key] and [ForeingKey] attributes, but I can't find them! :S (though the where in System.Data.Entity.dll, but the don't).
So after so many errors, I started to think maybe I got the whole thing wrong...
Any ideas on how to fix the error, or to go in other direction?
Thanks in advance.
This is stupidity in EF. You cannot compare Player directly. You must compare Ids so your query can be rewritten to:
int playerId = player.Id;
Character character = mod.Characters.FirstOrDefault(c => c.Player.Id == playerId);

Code First Object not properly instantiating

I have a class department inheriting from activeentity
public class ActiveEntity : Entity, IActive
{
public ActiveEntity()
{
IsActive = true;
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public Guid Id { get; set; }
public bool IsActive { get; set; }
[Timestamp, ScaffoldColumn(false), DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Computed)]
public Byte[] Timestamp { get; set; }
[ScaffoldColumn(false)]
public string CreationUserId { get; set; }
[ScaffoldColumn(false)]
public string LastModifiedUserId { get; set; }
}
public class Department:ActiveEntity
{
public Department()
{
this.Address = new DepartmentAddress();
}
[StringLength(9),MinLength(9),MaxLength(9)]
public string Name { get; set; }
public Guid ManagerId { get; set; }
[UIHint("AjaxDropdown")]
public User Manager { get; set; }
public Guid? AddressId { get; set; }
public DepartmentAddress Address { get; set; }
public ICollection<OverheadRate> OverheadRates { get; set; }
}
I am just using annotations no Fluent API. The data saves to the data Sql Server 2008 just fine however the address object never gets instantiated, even though I have the context use the include
return c.Set<Department>().Include(d => d.Address).Include(d => d.Manager).Where(predicate);
The data is returned I run sql profiler and then run the query it returns the correct data.
Any thoughts or suggestions?
Remove instantiating the address (this.Address = new DepartmentAddress();) in the Department constructor. Instantiating navigation references in the default constructor is evil and has nasty side effects like these:
What would cause the Entity Framework to save an unloaded (but lazy loadable) reference over existing data?
EF 4.1 Code First: Why is EF not setting this navigation property?