I am working on MIT open source license example ASP.NET MVC web applications, and adding them as github public repos, I am also planning to have private github repos for my applications I plan to make money with in the future. I have a developer account with github.com.
I created a BOOKS MVC 5 web application using a TSQL script I was provided during a previous job interview some years ago, and am using GUID for the primary key ID fields with a default value of NEWID(), instead of an INT with IDENTITY, the solution is an ASP.NET MVC 5 web application with database first Entity Framework. I am using LocalDB for my SQL Server with this project, the script to create and populate the database is in my SQL-Server repo and is called BOOKS_Create.sql
I published the solution to my GitHub at the following URL:
https://github.com/ABION-Technology/Books
The TSQL scripts are available in the following repo:
https://github.com/ABION-Technology/SQL-Server
I added links the the shared layout view to show the index view for all Authors in the database, and also links to Index views for the TITLE and SALE EF models.
THe AUTHORS link works just fine, and lists all the authors in the database. But when I click the TITLES link, I get a SQL Exception of 'Author_ID' invalid column name, I did a search through my entire solution and did not find any variable named 'Author_ID' and did not find a C# class with a property called 'Author_ID". So this issue has me very confused there does not appear to be a good way to debug this issue. Thanks.
EF will follow some default conventions to work out FK relationships. The error you are seeing is due to Author having a Titles collection and EF is attempting to automatically set up the 1-to-many between the two. It's expecting to find an "Author_ID" on the Title, which doesn't exist because your schema is set up with a joining table called TitleAuthor.
To resolve this, you will need to map the TitleAuthor entity, in which the Author will contain a collection of TitleAuthors which refer themselves to an Author and Title entity. EF can automatically map joining tables given those tables consist of just the two FKs. As soon as you want to introduce additional fields, then you need to define the joining entity.
public class TitleAuthor
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; internal set;}
public virtual Title Title { get; internal set;}
public virtual Author Author { get; internal set;}
// add other properties as needed..
}
So from your Author entity:
public virtual ICollection<TitleAuthor> Authors {get; internal set;} = new List<TitleAuthor>();
To access the titles for the author:
author.Titles.Select(x => x.Title);
I would recommend reading up on many-to-many mapping with EF. I invariably use deliberate mapping with EF rather than relying on it's conventions. It just helps make it more predictable.
If you are using defaults for PKs then you need to tell EF via the DatabaseGenerated attribute. This isn't needed for read operations, but it will be needed when you go to insert records.
Also, with SQL Server, consider using NewSequentialId() as the default for your UUID PKs. These are more index-friendly than NewId().
The above example using internal (private works too) setters to promote DDD style use of entities. Public setters can lead to misuse/abuse of entities in the sense that the context will diligently attempt to save whatever you set. It's generally a good idea to restrict functionality that would alter an entity's state to a method in the entity with required arguments to be validated, or a repository. I use internal scoping to allow unit tests to still initialize entities. (leveraging InternalsVisibleTo between domain and unit test assemblies)
Reason is you are getting that Author ID error is, you have list of Titles in Author Class. Then there should be relationship between Author and Title entities, which is not exists in your data context. Comment public virtual ICollection<Title> Titles { get; set; } . And it should work.
Reason for you cant search this attribute is, its automatically generated by entity framework. (TableName_PrimaryKey)
If you want to keep this, create relationship in database using foreign keys and add that to your data context also. You may refer this
Related
I am attempting to create a new Web API using ASP.Net Core 3, Entity Framework Core, and AutoMapper against a previously existing database. I will try to explain my problem briefly.
For the sake of example, assume the database has the following tables:
Person
--------------------
person_id int PK
first_name varchar
last_name varchar
...other fields common to all Persons...
Owner
--------------------
owner_id int PK, FK Person.person_id
...fields specific to Owners...
Renter
--------------------
renter_id int PK, FK Person.person_id
...fields specific to Renters...
Note: The original developers of the database did not make the person_id an identity column. They use a [UniqueIds] table and a stored procedure to fetch and increment Ids for tables in the database.
In the context of the data model objects, Person, Owner, and Renter are all distinct classes with their on DbSet<> properties in the DbContext. The owner_id or renter_id is a person_id, and ties the tables together through a foreign key constraint.
From a Domain object perspective, I've designed the Owner and Renter domain classes as sub-classes of a Person class.
class Person { ... }
class Owner : Person { ... }
class Renter : Person { ... }
I'm still learning the plumbing of how to put all this together, which is proving to be difficult given the plethora of information available, which is sometimes incomplete, lacking context, or outdated. So I really could use some up-to-date guidance. What isn't clear to me:
If I were using the data model directly, my application would be responsible for knowing that in order to create a new Owner record (i.e., add a new class instance to DbSet<Owner>), a new Person record with the same ID must be added to DbSet<Person>. So, I am assuming that a domain model in the layer that sits atop the data/persistence layer has to do something similar, and that AutoMapper will take care of ensuring the domain object's properties are properly mapped to the data objects if properly configured.
With that in mind, when defining the domain model, should I define a separate OwnerId field in the Owner class and somehow map it in AutoMapper? This seems rather sloppy to me, and relies on the consumer of my domain to ensure that OwnerId and PersonId from the base class hold the same value when creating a new Owner.
It would seem to me that AutoMapper should support the ability to represent the inheritance (which it does with IMappingExression.Include or IMappingExression.IncludeBase) and map Owner.PersonId (Domain) to both Owner.OwnerId and Person.PersonId in the data objects. But I cannot find any practical examples of this. Perhaps my Google Fu is failing me and similar question has been asked, but I could not find one. Any help or guidance would be much appreciated.
This question already has answers here:
Should Entities in Domain Driven Design and Entity Framework be the same?
(4 answers)
Closed 5 years ago.
I have a three tier app with a class library as the Infrastructure Layer, which contains an Entity Framework data model (database first).
Entity Framework creates entities under the Model.tt folder. These classes are populated with data from the database.
In the past I would map the classes created by Entity Framework (in the data project) to classes in the Domain project e.g. Infrastructure.dbApplication was mapped to Domain.Application.
My reading is telling me that I should be using the classes contained in .tt as the domain classes i.e. add domain methods to the classes generated by Entity Framework. However, this would mean that the domain classes would be contained in the Infrastructure project, wouldn't it? Is is possible to relocate the classes generated by Entity framework to the Domain project? Am I missing something fundamental here?
I think in the true sense it is a Data Model - not a Domain Model. Although people talk about having the Entity Framework Model as a domain concept, I don't see how you can easily retro fit Value objects such as say amount which would be represented in the true domain sense as such:
public class CustomerTransaction
{
public int Id { get; set; }
public string TransactionNumber { get; set; }
public Amount Amount { get; set; }
}
public class Amount
{
public decimal Value { get; }
public Currency Currency { get; }
}
As opposed to a more incorrect data model approach:
public class CustomerTransaction
{
public int Id { get; set; }
public string TransactionNumber { get; set; }
public int CurrencyType { get; set; }
public decimal Amount { get; set; }
}
Yes, the example is anaemic, but only interested in properties for clarity sake - not behaviour. You will need to change visibility of properties, whether you need default constructor on the "business/data object" for starters.
So in the domain sense, Amount is a value object on a Customer Transaction - which I am assuming as an entity in the example.
So how would this translate to database mappings via Entity Framework. There might be away to hold the above in a single CustomerTransaction table as the flat structure in the data model, but my way would to be add an additional repository around it and map out to the data structures.
Udi Dahan has some good information on DDD and ORM in the true sense. I thought somewhere he talked about DDD and ORM having the Data Model instance as a private field in the domain object but I might be wrong.
Also, that data model suffers from Primitive Obsession (I think Fowler coined it in his Refactoring book - although it Is in his book) Jimmy Bogard talks about that here.
Check out Udi Dahan stuff.
You should move your model to a different project. That is good practice. I don't quite get it what you meant by "moving to to Domain project" Normally entity framework generated classes are used as a domain model. No need for creating "different" domain model from this. This model should be use only near to database operations, whereas web(window) application should use only DTO (Domain transfer objects)
I don't know if you use it or not - but this is a nice tool allowing for recreating model from the database :
https://marketplace.visualstudio.com/items?itemName=SimonHughes.EntityFrameworkReversePOCOGenerator
This allows to store model in classes (instead of EDMX) Someone refers to it as "code first" but there is a misunderstanding. One can use this tool to create model and still be on "database first" This is done simply to omit using EDMX as a model definition.
You can relocate the entity classes by creating a new item in your Domain project: DbContext EF 6.x Generator (not sure of the name and you might have to install a plugin to get this item in the list, also exists for EF 5.x).
Once you have created this new item, you have to edit it to set the path of your EDMX at the very begining of the file. In my project for example it is:
const string inputFile = #"..\..\DAL.Impl\GlobalSales\Mapping\GlobalSalesContext.edmx";
You will also need to edit the DbContext.tt file to add the right using on top of the generated class. At each change you've done on the EDMX, you also will have to right click the generator and click: "Run custom tool" to generate the new classes.
That being said, is it a good practice? As you can see that's what I have done in my project. As long as you do not have EF specific annotations or stuff like that in the generated entity classes, I would said that it is acceptable.
If you need to change your ORM, you can just keep the generated classes and remove all the EF stuff (.tt files, etc) and the rest of your application will work the same. But that's opinion based.
If I have an entity:
public class User
{
public int UserId{get;set;}
}
And another entity:
public class Role
{
public int RoleId{get;set}
}
I want to model relation ship via EF Code First so I added:
User.cs
public virtual ICollection<Role> Roles {get;set;}
Role.cs
public virtual User User {get;set;}
This allow me to get user roles like:
context.Users.Roles.ToList();
But User is the main object in database. And it can have relations to 100 tables.
Is adding ICollection<T> and User object best practice or it is not always required (generally is there some rule of thumb for this)?
Sometimes I have feeling that I am creating too large objects and I wonder does this have some performance impact?
You are correct in thinking that dragging in 100 related tables to your dbcontext might not be the most performant solution and ef will drag in all tables that it can see either as a dbset, navigation property, or fluent configuration.
However if you need to navigate from roles to users in your dbcontext and the user entity has navigation properties that point to 100 of tables then your solution would be in this particular dbcontext to tell ef to ignore the tables you're not concerned with something like modelbuilder.ignore('orders') assuming from users you can navigate to orders in some way. in this way you can prune the graph to only consider the entities you need.
You then need another dbcontext for other spheres of interest: the concept is called the bounded context. (Ref Julie Lerman, Eric Evans DDD) You then need to do more work in your app to support multiple db contexts in the same app but it can be done - (See Julie Lerman on enterprise ef) However if you just want one dbcontext in your app where the scope of your model is limited to a subset of tables then the this will work.
I think you can use the ef power tools to view a read only diagram of the tables in your dbcontext model. You can then confirm how well your pruning is going.
I have a many-to-many relationship between Agents and AgentGroups (psuedocode, abbreviated).
public class Agent {
public virtual List<AgentGroup> AgentGroups { get; set; }
}
public class AgentGroup {
public virtual List<Agent> Agents { get; set; }
}
At some point in the code, I want to get all AgentGroups, and I want to prefetch/include the Agents for each group. I also want to pre-fill the AgentGroups collection on the Agents. This was working in EF 6 beta, but no longer works in EF 6 rc1:
List<AgentGroup> allGroups = context.AgentGroups.Include("Agents").Include("Agents.AgentGroups").ToList();
The error message I get is
Invalid object name 'dbo.AgentAgentGroups'.
And in fact, there isn't a table AgentAgentGroups, the table is dbo.AgentGroupAgents. Any ideas on getting this to work again?
I currently have no annotations and am not using the fluent API, it's all strictly the default code first conventions.
In rc1 there appears to be a change in naming convention of the junction table in many-to-many associations. When I try your model in various EF versions, this is what I see:
EF5 (stable): AgentGroupAgents
EF6 (beta): AgentGroupAgents
EF6 (rc1): AgentAgentGroups
It wouldn't be bad if the beta was different, but rc1 differs from the last RTM version, which makes this a breaking change. Good catch!
Edit
Answer from the EF team:
Hello,
Prior to EF6 there were some areas of model creation that were non-deterministic - depending on whether you were running on x86 or x64 you could get a different model. The many to many join table name was one of these areas.
In EF6 we addressed this to ensure the results would always be the same. Unfortunately, that does mean that for some models (depending on which architecture they were running on) upgrading to EF6 can cause a model change. But the model that is now calculated will be consistent across machines and future versions of EF.
If you want to continue using the original name, the best option is to use the Fluent API to specify the join table name.
~Rowan
I am using c# with Fluent NHibernate and auto mapping.
Here is some code (truncated for clarity), then I'll explain the problem.
public class Company
{
public virtual string Description { get; set; }
}
public class Stock
{
public virtual Product Product { get; set; }
public virtual Company Company { get; set; }
}
Mapping
mappings.Conventions.Add<CascadeConvention>()
.Conventions.Add<CustomForeignKeyConvention>()
.Conventions.Add<HasManyConvention>()
.Conventions.Add<VersionConvention>()
CascadeConvention just sets everything to All.
CustomForeignKeyConvention removes the _id that NHibernate usually
appends to foreign key id columns.
HasManyConvention sets all HasMany's to inverse.
VersionConvention convertion looks like this:
instance.Column("Version");
instance.Default(1);
The problem is that when I insert a new stock record, Nhibernate also updates the version number on the related Company.
If I had an IList<Stock> property on the Company then that would make sense but I don't.
I've done a lot of reading around:
NHibernate emitting extraneous update statements regardless of proper Inverse (fluent nhibernate) settings on relations
Cascade Save-Update in NHibernate and version column
NHibernate Reference - Chapter 17. Example: Parent/Child
Ayende # Rahien - NHibernate Mapping
From these, I've tried a whole bunch of things including adding .Not.OptimisticLock() all over the place. I even added an IList<Stock> property on Company so that I could specifically set it as Inverse, Not.OptimisticLock, etc. Nothing I do seems to make any difference.
We eventually sorted this by moving to a Session-per-request paradigm. Not sure why it was going wrong or why this fixed it. I wrote numerous unit tests to try and reproduce the behaviour in a more controlled environment without success.
In any case, it works now. There are good reasons session-per-request is often given as the best practice way to manage NHibernate sessions in a web application.