I'm following the core ef-mvc tutorial (https://learn.microsoft.com/en-us/aspnet/core/data/ef-mvc/complex-data-model) and I have a two models that share a many-to-many relationship with each other, which means that I have to have a one-to-many relationship defined between both these classes and a class that's used exclusively for the join table, right? ie.
public class JoinTable
{
public int model1ID;
public model1 model1;
public int model2ID;
public model2 model2;
}
In the example DbInitializer seed method, this code:
foreach(Enrollment e in enrollments)
{
var enrollmentInDataBase = context.Enrollments.Where(
s =>
s.Student.ID == e.StudentID &&
s.Course.CourseID == e.CourseID).SingleOrDefault();
if (enrollmentInDataBase == null)
{
context.Enrollments.Add(e);
}
}
Essentially saves all entities defined in the seed method that don't already exist in the table. How could I write a method that will save the entity to the database up to x amount of times (I need this capability for my own app)? Also based on my knowledge, I'm certain that there is an implicit restriction in the data model (or table) definition that would have to be altered?
Answered in comments, but moving here for visibility:
You want to have a third property in DeckCard called Count with only 1 entry for each card and the count will be how many of that card is in the Deck.
Related
I've joined a team that uses non standard names for tables and columns, and have trouble building database-first projects with Entity Framework.
Here's my problem:
tFWAClientProcessing (Table)
FWAClientHandling (Primary Key, INT)
iClientID (Foreign Key, INT)
.
tClients (Table)
AClientID (Primary Key, INT)
sClientName (VARCHAR(255))
I need Entity Framework to detect the relationship between these two tables without making changes to those tables in production.
I'd long given up on EDMX and convention-based mapping for relationships and just set up EF via EntityConfiguration classes. Attributes in the entity definitions are another option which should work for simple cases like identifying column names. You can also wire up mapping in the OnModelCreating override directly.
For instance: To have entities called Client and FWAClientProcessing for that table structure:
public class Client
{
public int ClientId { get; set; }
public string ClientName { get; set; }
}
public class FWAClientProcessing
{
public int FWAClientProcessingId { get; set; }
public virtual Client Client { get; set; }
}
public class ClientConfiguration : EntityTypeConfiguration<Client>
{
public ClientConfiguration()
{
ToTable("tClients"); // assumes default schema, i.e. "dbo" in SQL Server. Can add schema name as 2nd parameter otherwise.
HasKey(x => x.ClientId)
.Property(x => x.ClientId)
.HasColumnName("iClientID");
Property(x => x.ClientName)
.HasColumnName("sClientName");
}
}
public class FWAClientProcessingConfiguration : EntityTypeConfiguration<FWAClientPrcessing>
{
public FWAClientProcessingConfiguration()
{
ToTable("tFWAClientProcessing");
HasKey(x => x.FWAClientProcessingId)
.Property(x => x.FWAClientProcessingId)
.HasColumnName("FWAClientHandling");
HasRequired(x => x.Client)
.WithMany()
.Map(x => x.MapKey("iClientID"));
}
}
Assuming that the EntityTypeConfiguration classes are in the same assembly as the entities, and the DBContext, registering them in the context becomes:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.AddFromAssembly(TypeOf(YourDbContex).Assembly);
base.OnModelCreating(modelBuilder);
}
These examples are for EF6, EF Core uses the concept of Shadow Properties for mapping FK relationships without exposing FK properties, and can accommodate the different column naming. EntityTypeConfiguration is available as an Interface with a Configure method accepting the builder.
I favor using the explicit entity type configuration by default as it keeps the configuration nicely isolated and out of the way and can handle all mapping scenarios that might come up that annotations cannot do. It's a bit of a one-off cost to set up, but at least then you have full visibility and control over how the schema is mapped and not simply hoping EF works things out. :)
Use the modern replacement for EDMX-based Database-First and reverse-engineer a code-first model from the existing database. Customizing an EDMX-based model with its mappings is a rabbit-hole of obsolete technology.
This is available for EF Core and EF6.
The reverse-engineered model is then a starting point for you to make model customizations, like mapping the tables and columns to sensible names, and configuring any Navigation Properties that for whatever reason didn't get picked up by the tooling.
You are right, it is easier if people follow the entity framework conventions. However, if you have to deviate from them, OnModelCreating is your friend.
In OnModelCreating, from every Table, column, relation between tables, that are not standard, you can inform entity framework about these deviations.
You can give different table names
You can use other column names
You can say that certain properties should be saved in certain database formats, for instance ProductPrice is a decimal with 2 digits after the decimal point, instead of the default number of digits.
etc.
There seems to be a one-to-many relation between Clients and ClientsProcessing: every Client with primary key Id, has zero or more ClientsProcessings, every ClientProcessing belongs to exactly one Client, namely the Client that the foreign key ClientId refers to.
You want to use unconventional table names, unconventional names for you primary and foreign keys, and you need to inform about what keys are used to define the one-to-many relation.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Configure DbSet<Client>:
ver clients = modelBuilder.Entity<Client>();
clients.ToTable("tClients")
.HasKey(client => client.Id)
// property Id should be in "AClientID"
clients.Property(client => client.Id).HasColumnName("AClientID");
clients.Property(client => client.Name).HasColumnName("sClientName");
Apart from different names of the columns, you can also declare whether the properties are required or optional, what format they should have (is a decimal with two digits after the decimal point, or does it have four digits?), etc.
Do something similar for modelBuilder.Entity<ClientProcessing>();
For the one-to-many relation: every Client has zero or more ClientProcessings; every ClientProcessing belongs to exactly one (required!) Client, namely the foreign key that ClientId refers to:
clients.HasMany(client => client.ClientProcessings)
.WithRequired(clientProcessing => clientProcessing.Client)
.HasForeignKey(clientProcessing => clientProcessing.ClientId);
Or if you want, you can start at ClienProcessing: every ClientProcessing has exactly one Client (required!), using foreign key ClientId. Every Client has many ClientProcessings.
modelBuilder.Entity<ClientProcessing>()
.HasRequired(clientProcessing => clientProcessing.Client)
.WithMany()
.HasForeignKey(clientProcessing => clientProcessing.ClientId);
Note: by default this will cascade on delete: whenever you delete a client, you will also delete all its processings: you did define there are no processings without a client.
In some relations, you don't want this, especially many-to-many relations or one-to-zero-or-one relation: a Student may have zero or one School-supplied-Laptop. If you delete the Laptop, you don't want to delete the Student as well. In that case you'll have to add .WillCascadeOnDelete(false)
I have two tables:
dbo.Dashboards
Id (int PK) Title(nvarchar) WidgetIds(nvarchar)
1 Test [1,2]
dbo.Widgets
Id (int PK) Details(nvarchar)
1 {'text': 'some data'}
2 {'text': 'test'}
Expected output:
Dashboard.Id Dashboard.Title Widget.Id Widget.Details
1 Test 1 {'text': 'some data'}
1 Test 2 {'text': 'test'}
I would like to get dashboards with assigned widgets by using Entity Framework.
My first solution is to get dbo.Dashboards and then dbo.Widgets. After that I can merge it in a backend, but it is not the best practice.
Is there any option to get Dashboards with assigned Widget list?
Function Include() is not working because there isn't FK relationship between tables.
It seems to me that you have a many-to-many relationship between Dashboards and Widgets: Every Dashboard has zero or more Widgets and every Widget is used by zero or more Dashboards.
In a proper database you would have a separate junction table. Apparently you chose not to use this pattern, but create a string that contains a textual representation of the widgets that a 'Dashboard` has.
If you plan to create a serious application I strongly advise you to
use the standard pattern in many-to-many relationships
If you don't, all your queries will be more difficult. Imagine the problems you'll experience if you want to delete a Widget. You'd have to check the textual representation of every Dashboard to check if the widget that you want to remove is used somewhere and change it.
If you want to configure your many-to-many relations ship according to the Entity Framework Code-First Conventions, you will have something like this:
class Dashboard
{
public int Id {get; set;}
public string Title {get; set;}
// every Dashboard has zero or more Widgets
public virtual ICollection<Widget> Widgets {get; set;}
... // other properties
}
class Widget
{
public int Id {get; set;}
// every Widget is used in zero or more Dashboards
public virtual ICollection<Dashboard> Dashboards{get; set;}
... // other widget properties
}
class MyDbContext : DbContext
{
public DbSet<Dashboard> Dashboards {get; set;}
public DbSet<Widget> Widgets {get; set;}
}
Because you stuck to the conventions, this is all that entity framework needs to know to understand that you want to configure a many-to-many relationship between Dashboards and Widgets. Entity Framework will create the junction table for you. It will automatically update this table whenever you add a Widget to a Dashboard. It will also create the proper joins whenever you want to fetch Dashboards with their Widgets, or Widgets with the Dasheboards that use them.
Your query will be fairly simple:
var DashBoardsWithTheirWidgets = myDbcontext.Dashboards
// I only want to see the super dashboards
.Where(dashboard => dashboard.Type = DashboardType.Super)
.Select(dashboard => new
{
// Select only the properties you plan to use:
Id = dashboard.Id,
Title = dashboard.Title,
// select only the Widgets you plan to use:
Widgets = dashboard.Widgets
.Where(widget => widget.Price > 100.00)
.Select(widget => new
{
// again select only the properties you plan to use
Name = widget.Name,
Price = widget.Price,
})
.ToList();
});
See how easy it is if you stick to the conventions?
If you really want your obscure method of using foreign keys, you need a function to remove the square brackets and the commas from the widgetIds, split the string into sub-strings, Parse them to numbers, and do a join.
But before you plan to continue on this path, experiment on how to add a Widget and a Dashboard. How to add a Widget to a Dashboard, how to remove a Widget. I think the time needed to reform your database into proper format is much less than the time you'll need to implement those functions
Solution 1:
You need to restructure the dbo.dashboards table. Change the column layout of dbo.dashboards to
Auto_Generated_ID, Unique_Identifier(PK), Title, WidgetIds
I know the above column restructuring is done in a bad way. But still this will work in your case.
After redesigning it you can use join between dbo.dashboards and dbo.widgets to retrieve it in an efficient way.
Solution 2:
The below-normalized tables will work in your case
dbo.dashboard
id, title (columns)
dbo.dashboard_widget
id, dashboard_id, widget_id (columns)
dbo.widgets
id, details (columns)
Query:
select d.id, d.title, dw.widget_ids, w. details from dbo.dashboard d INNER JOIN dbo.dashboard_widget dw ON d.id = dw.dashboard_id INNER JOIN dbo.widgets w ON dw.widget_id = w.id where d.id = << id number >>
I'm working with Fluent nHibernate on a legacy database and have a main Person table and several extension tables containing additional information about the person. These extension tables are one-to-one, meaning that a person will only have one row on the extension table and the extension table should always map back to one person.
Table: Person
Columns: PersonID, FirstName, LastName, etc.
Table: PersonLogin
Columns: PersonID (FK, unique), UserName, Password, etc.
I have my mappings defined as this (with the irrelevant properties omitted):
public PersonMap()
{
Table("Person");
Id(x => x.Id, "PersonID").Not.Nullable();
References(x => x.Login, "PersonID").LazyLoad();
}
public LoginMap()
{
Table("PersonLogin");
Id(x => x.Id, "PersonID").GeneratedBy.Foreign("Person");
References(x => x.Person, "PersonID").LazyLoad();
}
This works when I have data on both tables, but I recently learned that some of the extension tables don't have data for all Person rows. This caused me to get errors during the query. So, I added .NotFound.Ignore() to my PersonMap making it look like this:
References(x => x.Login, "PersonID").LazyLoad().NotFound.Ignore();
That caused me to get unnecessary selects from the Login table due to https://nhibernate.jira.com/browse/NH-1001 when my business layer doesn't need to project any of the extension table values. It is causing the performance to be terrible in some of my search queries.
I've scoured a lot of posts, but haven't found a rock solid answer about how to address this scenario. Below are the options I've tried:
Option One:
Create rows on the extension table to ensure there is no Person without a row on the extension table and then remove the .NotFound.Ignore().
The issue with this option is that it's a legacy database and I'm not sure where I'd need to update to ensure that a PersonLogin is inserted when a Person is inserted.
Option Two:
Remove the PersonLogin reference from my PersonMap and custom load it inside my Person class. Like this:
public class Person
{
/// <summary> Gets or sets the PersonID </summary>
public virtual int Id { get; set; }
private bool loadedLogin;
private PersonLogin login;
public virtual PersonLogin Login
{
get
{
if (!loadedLogin)
{
login = SessionManager.Session().Get<PersonLogin>(Id);
loadedLogin = true;
}
return login;
}
set
{
login = value;
loadedLogin = true;
}
}
}
The issue I'm having with it is that I can't eagerly fetch the data when performing a query to pull back a large number of Person objects and their Logins.
Option Three:
I just started playing to see if I could write a custom IEntityNotFoundDelegate to not throw the exception for these objects.
private class CustomEntityNotFoundDelegate : IEntityNotFoundDelegate
{
public void HandleEntityNotFound(string entityName, object id)
{
if (entityName == "my.namespace.PersonLogin")
{
return;
}
else
{
throw new ObjectNotFoundException(id, entityName);
}
}
}
And I added this to the config
cfg.EntityNotFoundDelegate = new CustomEntityNotFoundDelegate();
It catches my scenario and returns back now instead of throwing the error, but now when I try to project those PersonLogin properties onto my business objects, it's attempting to use the Proxy object and throws this error that I'm trying to figure out if I can handle cleanly (possibly in a IPostLoadEventListener).
System.Reflection.TargetException occurred
Message = Non-static method requires a target
I think I've got this working now by keeping the .NotFound.Ignore().
I originally stated:
That caused me to get unnecessary selects from the Login table due to https://nhibernate.jira.com/browse/NH-1001 when my business layer doesn't need to project any of the extension table values. It is causing the performance to be terrible in some of my search queries.
I was able to tweak my LINQ queries to use the IQueryOver in some instances and to improve my use of LINQ in other scenarios to project only the necessary values. This appears to have resolved the queries from pulling back the extension tables since their values were not needed in the projections.
I thought that my queries weren't projecting these extension tables, but figured out that I had a method ToKeyValuePair that I was using in the projection to concatenate the ID and a Name field together of some related properties. That method was causing the objects to load completely since LINQ wasn't able to determine that the needed fields were present without joining to the extension table.
I started using NHibernate today, but I cannot figure out how I setup a simple relation between two tables. I don't really know what it's called, it could be one-to-many or foreign key relation (I'm not that into database design and the terms used), but here's a very simple example.
I have a table Product with attributes Id (PK), ProductName and CategoryId. Then I have a table Categories with attributes Id (PK) and CategoryName.
I created these classes:
public class Product
{
public virtual int Id { get; set; }
public virtual string ProductName { get; set; }
public virtual int CategoryId { get; set; }
public virtual Category Category { get; set; }
public virtual string CategoryName
{
get { return this.Category == null ? String.Empty : this.Category.CategoryName; }
}
}
public class Category
{
public virtual int Id { get; set; }
public virtual string CategoryName { get; set; }
}
In other words, I simply want the Product to store to which category it belongs (via the CategoryId attribute which points to an Id in the Categories table). I don't need the Category class to hold a list of related Products, if that makes it any simpler.
To make it even more clear what I'm after, this is the SQL that I'm expecting:
SELECT Products.*, Categories.*
FROM Products INNER JOIN Categories ON Products.CategoryId = Categories.Id
at least that's what I think it should be (again, I'm not that good at database design or queries).
I can't figure out which kind of mapping I need for this. I suppose I need to map it in the Product.hbm.xml file. But do I map the CategoryId as well? And how do I map the Category property?
It seems like I would need a 'one-to-many' relation since I have ONE category per product (or is this reasoning backward?) but it seems like there is no one-to-many mapping...
Thanks for any help!
Addition:
I tried to add the many-to-one relation in the Person mapping, but I keep getting an exception saying "Creating proxy failed", and in the inner exception "Ambiguous match found".
I should maybe mention I am using an old version of NHibernate (1.2 I think) because that is the only one I got running with MS Access due to it not finding the JetDriver in newer versions.
I've put the mapping files, classes, and code where the error occurs in screenshots because I can't figure out how to post XML code here... It keeps reading it as html tags and skipping half of it. Anyway.
The mappings:
http://www.nickthissen.nl/Images/tmp7B5A.png
The classes:
http://www.nickthissen.nl/Images/tmpF809.png
The loading code where the error occurs:
http://www.nickthissen.nl/Images/tmp46B6.png
(As I said, the inner exception says "Ambiguous match found".
(Product in my example has been replaced by Person)
The Person and Category classes inherit Entity which is an abstract base class and defines the Id, Deleted, CreatedTime and UpdatedTime properties.
The code where the error occurs is in a generic 'manager' class (type parameter TEntity which must inherit Entity). It is simply supposed to load all entities with the Deleted attribute false. In this case, TEntity is 'Person'.
It works fine if I leave out the many-to-one Category mapping in the Person mapping, but then obviously the Category property is always null.
Oh yeah, sorry about the mix between C# and VB, the C# code is in a generic framework I use for multiple projects while the VB part is the actual implementation of that framework on my website and I just happened to use VB for that.
Help? Thanks!
In your Product class only needs to contain a Category object, you don't need a CategoryId property. Then in your Product mapping you need to have this entry
<many-to-one name="Category" column="CategoryId" />
UPDATE:
Your mappings appear to be missing the fully qualified name of the mapped class in the tag. See http://nhibernate.info/doc/nh/en/index.html#mapping-declaration-class
UPDATE 2:
See if this helps you NHibernate 1.2 in a .NET 4.0 solution
The 'Ambiguous match found' exception was caused by the project targeting .NET Framework 4, which does not seem to be compatible with NHibernate 1.2.1. I switched to 3.5 and that seems to solve that particular issue.
Now on to the next. As you can see, the Person class has a CategoryName property that should return the name of the current Category object, or an empty string if the category happens to be null. This is so I can databind a collection of Person objects to a grid, specifying 'CategoryName' as a property to bind a column to.
Apparently this does not work with NHibernate. Whenever I try to databind my collection of persons, I get this exception:
"Property accessor 'CategoryName' on object 'NHibernateWebTest.Database.Person' threw the following exception:'Could not initialize proxy - the owning Session was closed.'"
This occurs on the 'DataBind' method call in this code:
public virtual void LoadGrid()
{
if (this.Grid == null) return;
this.Grid.DataSource = this.Manager.Load();
this.Grid.DataBind();
}
(This is an ASP.NET project and 'Grid' is a GridView)
'this.Manager' returns an existing instance of NHibernateEntityManager, and I've already shown its Load method before, it contains this:
public virtual EntityCollection Load()
{
using (ISession session = this.GetSession())
{
var entities = session
.CreateCriteria(typeof (TEntity))
.Add(Expression.Eq("Deleted", false))
.List();
return new EntityCollection(entities);
}
}
(THere's some generic type parameters in there but this website seems to hide them (due to the html like tags I guess)... Sorry about that).
This may have something to do with NHibernate itself, as I said I'm completely new to this. When I call my Load method I would expect it to return an EntityCollection(Of Person) with all its properties already set. It seems I have to keep the ISession open while I am databinding for some reason..? That seems a little strange...
Can I get around this? Can I make my Load method simply return a collection of persons already fully loaded, so that I can access CategoryName whenever I want?
Wait... Is this lazy loading perhaps?
I would like to know if there is a way to disable automatic loading of child records in nHibernate ( for one:many relationships ).
We can easily switch off lazy loading on properties but what I want is to disable any kind of automatic loading ( lazy and non lazy both ). I only want to load data via query ( i.e. HQL or Criteria )
I would still like to define the relationship between parent child records in the mapping file to facilitate HQL and be able to join parent child entities, but I do not want the child records to be loaded as part of the parent record unless a query on the parent record
explicitly states that ( via eager fetch, etc ).
Example:
Fetching Department record from the database should not fetch all employee records from the database because it may never be needed.
One option here is to set the Employees collection on Department as lazy load. The problem with this approach is that once the object is given to the calling API it can 'touch' the lazy load property and that will fetch the entire list from the db.
I tried to use 'evict' - to disconnect the object but it does not seem to be working at all times and does not do a deep evict on the object.
Plus it abstracts the lazy loaded property type with a proxy class that plays havoc later in the code where we are trying to operate on the object via reflection and it encounters unexpended type on the object.
I am a beginner to nHibernate, any pointers or help would be of great help.
Given your request, you could simply not map from Department to Employees, nor have an Employees property on your department. This would mean you always have to make a database hit to find the employees of a database.
Aplogies if these code examples don't work out of the box, I'm not near a compiler at the moment
So, your department class might look like:
public class Department
{
public int Id { get; protected set; }
public string Name { get; set; }
/* Equality and GetHashCode here */
}
and your Employee would look like:
public class Employee
{
public int Id { get; protected set; }
public Name Name { get; set; }
public Department Department { get; set; }
/* Equality and GetHashCode here */
}
Any time you wanted to find Employees for a department, you've have to call:
/*...*/
session.CreateCriteria(typeof(Employee))
.Add(Restrictions.Eq("Department", department)
.List<Employee>();
Simply because your spec says "Departments have many Employees", doesn't mean you have to map it as a bi-directional association. If you can keep your associated uni-directional, you can really get your data-access to fly too.
Google "Domain Driven Design" Aggregate, or see Page 125 of Eric Evan's book on Domain Driven Design for more information
You can have the lazy attribute on the collection. In your example, Department has n employees, if lazy is enabled, the employees will not be loaded by default when you load a department : http://www.nhforge.org/doc/nh/en/#collections-lazy
You can have queries that explicitly load department AND employees together. It's the "fetch" option : http://www.nhforge.org/doc/nh/en/#performance-fetching-lazy