I wonder, if there is any way ,
to use Database-first approach with manually generated classes (models) in advance(just like Code-first approach),
but without using auto-generated code which Entity Framework creates using Database-first approach?
I have 3 Classes(first two of them Student and Courses have many to many relationship), which represents models:
First one is Student:
public class Student
{
public int StudentID { get; set;}
public string Name { get; set;}
public DateTime BirthDate { get; set;}
public ICollection<StudentToCourse> StudentToCourses { get; set; }
public Student()
{
StudentToCourses = new List<StudentToCourse>();
}
}
Then Course:
public class Course
{
public int CourseID { get; set; }
public string CourseName { get; set; }
public ICollection<StudentToCourse> StudentToCourses { get; set; }
public Course()
{
StudentToCourses = new List<StudentToCourse>();
}
}
And Relation/Intermediate Class with additional properties StudentToCourse:
public class StudentToCourse
{
[Key, Column(Order = 0)]
public int StudentID { get; set; }
[Key, Column(Order = 1)]
public int CourseID { get; set; }
[Key, Column(Order = 2)]
public DateTime Date { get; set; }
public virtual Student Student { get; set; }
public virtual Course Course { get; set; }
//public ICollection<Student> Students { get; set; }
//public ICollection<Course> Courses { get; set; }
public int Grade { get; set; }
}
Also, i created Database, using LocalDb feature in VS 2013
I have 3 Tables:
Courses:
CREATE TABLE [dbo].[Courses]
(
[CourseID] INT NOT NULL PRIMARY KEY IDENTITY,
[CourseName] NVARCHAR(100) NOT NULL,
)
Students:
CREATE TABLE [dbo].[Students]
(
[StudentID] INT NOT NULL PRIMARY KEY IDENTITY,
[Name] NVARCHAR(50) NOT NULL,
[BirthDate] DATETIME NOT NULL,
)
Relation Table StudentsToCourses:
CREATE TABLE [dbo].[StudentsToCourses]
(
[StudentID] INT REFERENCES Students(StudentID) NOT NULL,
[CourseID] INT REFERENCES Courses(CourseID) NOT NULL,
[Date] DATETIME NOT NULL,
[Grade] INT NOT NULL,
PRIMARY KEY (StudentID, CourseID, Date)
)
Unfortunately, i have no luck with this approach, i do get students' data but i don't receive data from relational table and i can't receive all related grades per student.
I searched for related topics in google and in stackoverflow , but all those topics weren't helpful for me, although the example above i found in this topic.
As I suspected, the problem is not whether or not you can have a database and a class model independently. Of course you can! All these generation tools and migration stuff only serve one goal: making life easier, help you keeping both models in sync. But you can do that job yourself just as well. The end result is always: two models that – at runtime – don't interact with each other whatsoever. (Don't interact? No, not as such. There must be a middleman, an ORM, to connect both worlds.)
The reason why you don't get data is because lazy loading does not occur. Your statement is
var listOfGrades = _context.Students.Where(s => s.Name.StartsWith("J"))
.FirstOrDefault().StudentToCourses;
This requires lazy loading, because the FirstOrDefault() statement executes the first part of the query. It renders a Student of which subsequently the StudentToCourses are accessed. But these don't load because the collection is not virtual. It should be
public virtual ICollection<StudentToCourse> StudentToCourses { get; set; }
This enables EF to override the collection in a dynamic proxy object that is capable of lazy loading.
But of course is is more efficient to get the collection in one statement, for example:
var listOfGrades = _context.Students.Include(s => s.StudentToCourses)
.Where(s => s.Name.StartsWith("J"))
.FirstOrDefault().StudentToCourses;
Yes, you can. You just need a context with no initialization strategy (so it doesn't try to create or migrate your existing database):
public class ExistingDatabaseContext : DbContext
{
public ExistingDatabaseContext()
: base("ExistingDatabaseConnectionStringName")
{
Database.SetInitializer<ExistingDatabaseContext>(null);
}
// DbSets here for your "code-first" classes that represent existing database tables
}
Just bear in mind that this context will not be capable of doing migrations or any other form of initialization, so if you have actual true code-first tables in there as well, you'll need a separate context to manage those.
Related
I have a solution which uses Entity Framework to insert invoices to a database table. These invoices reference an order, which in turn also references an order item collection.
In this instance I am trying to add an order to the database, however the code is inside a new DbContext and so I need to attach the order and order items to the context, as these already exist in the database and shouldn't be re-added.
I've cut down the model properties for the sake of demonstration:
public class Invoice {
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int InvoiceId { get; set; }
public string OrderNumber { get; set; }
...
public virtual List<InvoiceLineItem> LineItems { get; set; }
}
public class InvoiceLineItem {
[Key]
public int Id { get; set; }
...
public ShopifyOrderItem { get; set; }
}
public class ShopifyOrder {
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public long Id { get; set; }
public int OrderNumber { get; set; }
...
public OrderInvoiceStatus InvoiceStatus { get; set; }
public virtual List<ShopifyOrderItem> OrderItems { get; set; }
}
public class ShopifyOrderItem {
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public long Id { get; set; }
...
[Required]
public virtual ShopifyOrder ShopifyOrder { get; set; }
}
In the invoice engine, I'm running the following code for each invoice to add it to the database:
ShopifyOrder order = await db.ShopifyOrders.SingleOrDefaultAsync(x => x.OrderNumber.ToString() == inv.OrderNumber);
if (order != null) {
// Attach marketplace entity to the invoice to avoid duplicate primary key exceptions
db.Marketplaces.Attach(inv.Marketplace);
db.Invoices.Add(inv);
order.InvoiceStatus = OrderInvoiceStatus.InProgress;
}
I've tried a number of methods to try and attach the states, however they all throw errors.
inv.LineItems.ForEach(li => {
db.Entry(li).State = EntityState.Unchanged;
db.Entry(li.ShopifyOrderItem).State = EntityState.Unchanged;
db.Entry(li.ShopifyOrderItem.ShopifyOrder).State = EntityState.Modified;
});
The above code returns the following error on save:
EntityFramework: Saving or accepting changes failed because more than one entity of type 'TorroModels.ShopifyOrder' have the same primary key value. Ensure that explicitly set primary key values are unique. Ensure that database-generated primary keys are configured correctly in the database and in the Entity Framework model.
What is the best way to attach the LineItems/ShopifyOrderItems without trying to attach the ShopifyOrder connected property multiple times?
Sorry to say but it seems that you need to follow the best practice first when constructing a relationship. You may follow this link :
http://www.entityframeworktutorial.net/entity-relationships.aspx
In short :
Avoid using only "Id" in every entity, or you can use attributes to map between the physical name and the property name
It seems that you have circular references here, so maybe you could simplify it first
Next, you can read this link :
http://www.entityframeworktutorial.net/EntityFramework5/attach-disconnected-entity-graph.aspx
if you need to know more about what's the best practice of attaching entities, but in my opinion, just don't abuse this feature, because using normal CRUD should be sufficient most of the time.
I'm sorry I cannot help you more than this, because of lack of information I may need, and with my reputation I still cannot comment directly in your post to ask for it.
I have a database with a lot of tables, and table X has many foreign keys to table Y. When I use Entity Framework Code First to generate C# model corresponding to may database the class X has a lot of properties of type Y called Y, Y1, ... Yn. For example:
public virtual Author Author { get; set; }
public virtual Author Author1 { get; set; }
public virtual Author Author2 { get; set; }
And I want to have something like:
public virtual Author Writer { get; set; }
public virtual Author Supervisor { get; set; }
public virtual Author Reviewer { get; set; }
Coresponding to mai SQL table columns:
[WriterId] [int] NOT NULL,
[SupervisorId] [int] NOT NULL,
[ReviewerId] [int] NOT NULL,
I know that I can add partial classes to add extra properties like in below example:
public virtual Author AuthorWriter
{
get
{
return this.Author1;
}
set
{
this.Author1 = value;
}
}
but I would have to much code to write. So my question is if is there a way in Visual Studio 2013 to configure how virtual properties names are generated by Entity Framework?
I mention that I do not want to change the generation of C# model way because the database is larger and I do not have access to modify its structure, moreover if the database is changed I should regenerate my C# model.
Thanks in advance
I am trying to use the extension in MvvmCross 4. What I am trying to do is simple: I have two tables with a one to many relationship and and want to access this.
I have two classes, BusLine and BusLineGroup. Each BusLine has one Group as foreign key. What I do in code is run a simple LINQ-Query to get all Buslines:
var testQuery =
from busLine in this._connection.Table<BusLine>()
select busLine;
The query itself works, but if I check the fields of the returned objects, the Group is always null!. See below for the class and table definitions.
What am I doing wrong? Why is the group always null? Thanks for your help.
The classes in code:
public class BusLine
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey(typeof(BusLineGroup))]
public int BusLineGroup { get; set; }
[ManyToOne]
public BusLineGroup LineGroup { get; set; }
}
public class BusLineGroup
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
public string Color { get; set; }
public string MainStations { get; set; }
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<BusLine> BusLines { get; set; }
}
The two tables:
CREATE TABLE "BusLineGroup" (
`Id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
`Name` TEXT NOT NULL,
`Color` TEXT NOT NULL,
`MainStations` TEXT NOT NULL
);
CREATE TABLE "BusLine" (
`Id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
`Name` TEXT NOT NULL,
`BusLineGroup` INTEGER NOT NULL,
FOREIGN KEY(`BusLineGroup`) REFERENCES `BusLineGroup`(`Id`)
);
Installed Nuget-Packages:
MvvmCross.Plugin.SQLitePCL
SQLiteNetExtensions
Note: The MvvmCross package automatically includes SQLite.Net-PCL. So both of those two use the same PCL.
You are not using any SQLite-Net Extensions method there, you are using plain sqlite.net methods that know nothing about your relationships. You have to use the WithChildren methods to read and write relationships from database.
For example, your query can be replaced with this line:
var testQuery = this._connection.GetAllWithChildren<BusLine>();
That will also fetch first-level relationships from database.
I'd recommend you to take a look at the SQLite-Net Extensions documentation and the sample project for more examples.
My web application is built with MVC and Entity Framework Code First. To explain my question, I'm describing it with a simplified Album - Song example:
public abstract class MusicStoreEntity
{
[Display(AutoGenerateField = false), Key, Required, Column(Order = 0)]
public Guid Id { get; set; }
}
[Table(TableName), DataContract]
public class Album : MusicStoreEntity
{
public const string TableName = "Albums";
public virtual Collection<AlbumSong> Songs { get; set; }
}
[Table(TableName), DataContract]
public class Song : MusicStoreEntity
{
public const string TableName = "Songs";
public virtual Collection<AlbumSong> Albums { get; set; }
}
The two entities are connected many-to-many with a separate entity that contains two foreign keys, as well as an unique identifier:
[Table(TableName), DataContract]
public class AlbumSong : MusicStoreEntity
{
public const string TableName = "AlbumSongs";
[DataMember, Required]
public Guid AlbumId{ get; set; }
public virtual Album Album { get; set; }
[DataMember, Required]
public Guid SongId { get; set; }
public virtual Song Song { get; set; }
}
With an incoming API call, you can create a new entity if the two entities aren't connected yet:
public void SetAlbumSong(Guid albumId, Guid songId) {
var albumSong = DBContext.Set<AlbumSong>().SingleOrDefault(a => a.AlbumId == albumId && a.SongId == songId);
if(albumSong == null) {
var albumSong = new AlbumSong {
AlbumId = albumId,
SongId = songId
}
DBContext.Set<AlbumSong>().Add(albumSong);
DBContext.SaveChanges();
} else {
// update existing albumSong
}
}
But, when two API calls come in at approximately the same time with the same entity id's, there is a window that enables adding two AlbumSong entities between the same album and song.
One solution is making a Composite Key that consists of both foreign id's in the AlbumSong entity. This way no two duplicate AlbumSongs can be made and an exception will be thrown.
A similar solution is adding an extra property to AlbumSong that combines the two id's and requiring the column to be unique.
However, I wondered if there are other (better, cleaner) solutions to this problem, as above solutions bring unwanted changes for my specific application.
(My web application is built with MVC 4 and Entity Framework 5.)
A composite key is a good idea, and can be viewed as better database design, because the AlbumID and SongID are what truly identify the record as unique.
EF provides concurrency checking, and wouldn't require many changes other than updating your model and adding a try/catch to your else statement. This article can take you through the process, but will add a field to your database. This article uses concurrency checking, but does not add a field to the database.
We want to use NHibernate as our persistence layer in our application. We are also using Fluent NHibernate for the mappings.
We get Person data from a 3rd party and we need to save the data to our database. It works better in the code to have all properties on one object, but it makes more sense to save the data to 2 tables in our database.
Our object looks like this:
public class Person
{
public virtual long VersionNumber { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string IdentificationNumber { get; set; }
}
Our database tables look like this:
CREATE TABLE PersonVersion (
[PK] VersionNumber bigint NOT NULL,
[FK] PersonDemographicsId int NOT NULL
)
CREATE TABLE PersonDemographics (
[PK] PersonDemographicsId int NOT NULL,
IdentificationNumber nvarchar(9) NOT NULL,
FirstName nvarchar(100) NOT NULL,
LastName nvarchar(100) NOT NULL
)
When we receive new data, the version number can change, but the rest of the demographics could be the same. What we need NHibernate to do is save a new record to the PersonVersion table which links to the existing PersonDemographics record. If the demographics have changed, then we will create a new record in both tables. But most of the time, once we've downloaded the initial data, the demographics won't change as often as the version number. We need to keep track of all version numbers so that's why it's necessary to create a new PersonVersion record.
How would we accomplish this using NHibernate and mappings using Fluent NHibernate?
Also, as you can see, our Person object currently does not have a PersonDemographicsId because it is not needed by our application at all; it is just an ID for the table relationship which is needed in the database. In order to properly map this in NHibernate, do we have to add a PersonDemographicsId property on our Person object?
Thanks for the help!
This article http://ayende.com/blog/2327/multi-table-entities-in-nhibernate explains a way to map a single class to two tables in the database.
just an idea, maybe has to be tweaked
public class Person
{
public virtual int Id { get; set; }
internal protected virtual IList<long> VersionNumbers { get; set; }
public virtual long VersionNumber {
get { return VersionNumbers[VersionNumbers.Count - 1]; }
set { VersionNumbers.Add(value); }
}
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string IdentificationNumber { get; set; }
}
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Table("PersonDemographics");
Id(p => p.Id, "PersonDemographicsId").GeneratedBy.Assigned();
Map(p => p.FirstName);
Map(p => p.LastName);
Map(p => p.IdentificationNumber);
HasMany(p => p.VersionRecord)
.Table("PersonVersion")
.KeyColumn("PersonDemographicsId")
.Element("VersionNumber")
.OrderBy("VersionNumber")
.Cascade.All();
}
}