I've successfully setup a simple MVC4 internet app that stores model data onto a LocalDb v11.0 SQL server. I generated the database using a code-first approach, however, the Table fields in the database are different than the model database context.
public class Record
{
public int Id { get; set; }
public string PlantLocation { get; set; }
public Plant PlantType { get; set; }
public string Description { get; set; }
}
public class Plant
{
public int Id { get; set; }
public string Name { get; set; }
}
public class RecordContext : DbContext
{
public DbSet<Record> Records { get; set; }
public DbSet<Plant> Plants { get; set; }
}
dbo.Records Table
[Id] INT IDENTITY (1, 1) NOT NULL,
[PlantLocation] NVARCHAR (MAX) NULL,
[Description] NVARCHAR (MAX) NULL,
**[PlantType_Id] INT NULL,**
When i pull-up the table data, every field is populated correctly, with the PlantType_ID showing the Id of the selected Plant.
How am i supposed to used the PlantType_ID to display the ID of the Plant(or even use this stored data) in my View if PlantType_ID is not in my RecordContext?
I have tried the following to try an get at the ID but to no avail:
#Html.DisplayFor(modelItem => item.PlantType.Id)
I'm not getting build errors or runtime errors if anyone is wondering. Any insight is appreciated.
Add virtual to the plant property to allow lazyloading:
public virtual Plant PlantType { get; set; }
To avoid N+1 issues, you can instruct EF to bring back the PlantType record as part of the same initial query. Something like:
var record = db.Records.Include("PlantType").First();
We have an extension method to make it a little nicer using lambdas:
var record = c.Records.Include(i => i.PlantType).First();
public static class Extensions
{
public static IQueryable<T> Include<T, TProperty>(this IQueryable<T> source, Expression<Func<T, TProperty>> path) where T : class
{
return System.Data.Entity.DbExtensions.Include(source, path);
}
}
Edit
Alternatively you could add the following property to the Record model if all you are after is the id:
public int PlantTypeId { get; set; }
Your database structure will largely stay the same (constraint names will change) except now you can change your razor syntax to be:
#Html.DisplayFor(modelItem => item.PlantTypeId)
You do not need to add the include comments now either.
If you want to display the plant name, you could directly use:
#Html.DisplayFor(modelItem => item.PlantType.Name)
You are using primary key for display for. That's why its not showing as above said you can use display for for plant type name and other fields.
Related
I have a table :
create table scheduled_task
(
scheduled_task_id serial primary key,
description varchar(255),
predicate varchar(10485760),
path varchar(255),
scheduled_task_type_id int references scheduled_task_type (scheduled_task_type_id)
);
My data object :
[Table("scheduled_task")]
public class DOScheduledTask
{
[Key]
[Column("scheduled_task_id")]
public long? ScheduledTaskId { get; set; }
[Column("description")]
public string Description { get; set;}
[Column("predicate")]
public string Predicate { get; set; }
[Column("path")]
public string Path { get; set; }
[Column("scheduled_task_type_id")]
public long? ScheduledTaskTypeId { get; set; }
}
I inserted one record into the table, that populates all fields.
I perform this in code :
var schedules = conn.Connection.Query<DOScheduledTask>("select * from scheduled_task ;");
var schedulesb = conn.Connection.GetList<DOScheduledTask>();
The first line yields a record, with everything but scheduled_task_id and scheduled_task_type_id are both null. For the second query, it is populated in full.
What am I doing wrong?
The problem is that the .Query and .GetList are from two different implementations; the one is from Dapper, and the other is from SimpleCRUD.
The solution was to create a custom mapper, due to the naming of the columns being different in code than in the DB.
I am using SQLite-Net PCL together with SQLite-Net extensions for the development of an application using Xamarin.
In my model I have an entity (let's call it A) which is connected to other four entities through one-to-many relationships (that are represented as lists in the model). In order to populate the tables recursively when inserting an object of A in the database I have defined the relations to use Cascade on both read, insert and delete.
In order to test if I did everything correctly I created an object of type A and populated the including lists, and finally I have inserted it into the database. The strange thing is that, for 2 of the 4 including lists the insertion went well, and all the connected objects are inserted. For other 2, instead, only the first object of the list is inserted in the database. To be clear, I am checking the database content directly with a db browser.
The following is an example of one of the objects for which only the first element of the list is inserted.
public class Username : Entity
{
public string Name
{
get;
set;
}
[ForeignKey(typeof(A))]
public int AId
{
get;
set;
}
public Username(string username)
{
Name = username;
}
}
This is instead one of the objects for which the insertion is correct.
public class AnAddress: Entity
{
public string Address
{
get;
set;
}
public AddressType Type
{
get;
set;
}
[ForeignKey(typeof(A))]
public int AId
{
get;
set;
}
}
To be clear, the base object Entity contains the definition of the primary key:
public abstract class Entity
{
[PrimaryKey, AutoIncrement]
public int Id
{
get;
set;
}
public Entity()
{
Id = -1;
}
}
And this is the way the relationships are defined:
public class A : Entity
{
public string Name
{
get;
set;
}
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<AnAddress> Addresses
{
get;
set;
}
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<Username> Usernames
{
get;
set;
}
}
I then create an A object by initialising it with two lists (List and List) in the same identical way.
I finally insert the object in the database with
c.InsertWithChildren(entity, recursive: true));
Where entity is of type A and c is the connection object.
Do you have any clue about the motivation of this strange behaviour?
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.
I'm building a Windows 8 C#/XAML app that uses SQLite as a storage database, and I'm trying to create multiple tables using the SQLite-net syntax.
From what I've researched so far, a table is created based off of a class. First, I've created an "Account" class via:
public class Account
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public string Name { get; set; }
public string Type { get; set;}
}
And then create a table and enter in initial data later on in the code via:
private static readonly string _dbPath =
Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "data.sqlite");
using (var db = new SQLite.SQLiteConnection(_dbPath))
{
db.CreateTable<Account>();
db.RunInTransaction(() =>
db.Insert(new Account()
{
Name = "MyCheckingAccount",
Type = "Checking",
})
);
}
I want to create multiple account tables, but the db.CreateTable<Account>() syntax just creates a table and the data is inserted into the columns with db.Insert(). I don't see where to enter the name of the table itself.
How do I create multiple tables, i.e. one named "BusinessAccounts" and another "PersonalAccounts" based off of the Account class?
Is there a way to do this with SQLite-net? Or do I need to write out the SQLite command explicitly somehow?
This answer seems to be outdated, in SQLite-net you can now use an attribute on a class to ovverride the table name, for example:
[SQLite.Table("table_customers")]
public class Customer
{
[MaxLength(3)]
public string code { get; set; }
[MaxLength(255)]
public string name { get; set; }
}
So it will create/update that table.
Sqlite-Net uses the class name to create the table, as well as to update the data. To do what you want, you'll need to create separate classes. One way to get around repeating common fields is to use inheritance:
public class Account
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public string Name { get; set; }
public string Type { get; set;}
}
public class BusinessAccounts : Account { }
public class PersonalAccounts : Account { }
To create tables:
db.CreateTable<BusinessAccounts>();
db.CreateTable<PersonalAccounts>();
To insert data:
db.Insert(new BusinessAccounts() {...});
db.Insert(new PersonalAccounts() {...});
Just a note that the above code is untested. You'll want to make sure that the tables are created correctly (e.g. with the proper primary key and autoincrement field).
Just to add that with SQLite-net, you can change the attribute of the class by implementing an initialisation overload and setting the SQLite.TableAttribute like this:
[Table("Account")]
public class Account
{
[PrimaryKey]
[AutoIncrement]
public int ID { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public Account(string name = null)
{
if (!string.IsNullOrEmpty(name))
{
TableAttribute attrname = TypeDescriptor.GetAttributes(this)(0);
attrname.Name = name;
}
}
}
The name defaults to account, but if you initialize the class with a string, it sets the attribute thus you can then create a table with that name.
I currently have an Entity Framework model that collects data from a legacy database and I am currently using an int on my Id properties
I am attempting to build a search box with autocomplete capabilities and want to have the autocomplete function to return a subset of records based on whether the sample id either contains or starts with (final design decision not made yet) and I am running into problems with converting the integer id to a string as I would normally use a recs.Id.toString().StartsWith(recordId) but this is apparently not supported by the Entity Framework
Is there a way around this limitation ?
My code looks like the following
Model:
public class Sample
{
public Sample()
{
Tests = new List<Test>();
}
public int Id { get; set; }
public DateTime SampleDate { get; set; }
public string Container { get; set; }
public string Product { get; set; }
public string Name { get; set; }
public string Status { get; set; }
public virtual SamplePoint SamplingPoint { get; set; }
public virtual SampleTemplate SampleTemplate { get; set; }
public Customer ForCustomer { get; set; }
public virtual ICollection<Test> Tests { get; set; }
}
and the query I am currently trying to apply to this model
[HttpGet]
public JsonResult AutoComplete(string partialId)
{
var filteredSamples =
repo.AllSamples.Where( s =>
String.Compare(s.Status, "A", false) == 0
&& (s.Id.ToString()).StartsWith(partialId)
).ToList();
return Json(filteredSamples, JsonRequestBehavior.AllowGet);
}
Any ideas would be awesome I am out of ideas at this point
No matter what you do, this is going to result in some awful performance on large datasets, because you will not be able to use any indices. My recommendation would be to use a trigger or scheduled task to store the leading digit in a separate field and filter on that.
I ended up adding a view for autocomplete data and converting the data to string in the select statement and this solved my issue
Wild thought: how about your create a computed, persisted column on your database table, that converts your ID (INT) into a string?
Then you could:
put an index on that column
use a simple string comparison on that string column
Basically, you need this:
ALTER TABLE dbo.YourTable
ADD IDAsText AS CAST(ID AS VARCHAR(10)) PERSISTED
Now update you EF model - and now you should have a new string field IDAsText in your object class. Try to run your autocomplete comparisons against that string field.