I have 2 classes, Order and Address as following:
public class Order
{
public string OrderId { get; set; }
public Address ShippingAddress { get; set; }
public Address BillingAddress { get; set; }
}
and
public class Address
{
public string Street { get; set; }
public string Town { get; set; }
public string Zip { get; set; }
}
The database stores the orders and address in a single table like this:
CREATE TABLE Orders
(
OrderId NVARCHAR(56) PRIMARY KEY,
BillingStreet NVARCHAR(256),
BillingTown NVARCHAR(256),
BillingZip NVARCHAR(256),
ShippingStreet NVARCHAR(256),
ShippingTown NVARCHAR(256),
ShippingZip NVARCHAR(256)
)
How can i get dapper to map this to the Order class?
Here's how you can do that by making the query generalize the billing and shipping columns and using the version of Query that takes multiple types and telling it to split when it sees a column called "Address". Then you just assign the Address objects to the appropriate property on the Order object.
connection.Query<Order, Address, Address, Order>(
#"SELECT OrderId,
BillingAddress As Address,
BillingTown As Town,
BillingZip As Zip,
ShippingAddress As Address,
ShippingTown As Town,
ShippingZip As Zip,
FROM Orders",
(o, ba, sa) =>
{
o.BillingAddress = ba;
o.ShippingAddress = sa;
return o;
},
splitOn: "Address");
I don't think it is possible with Dapper since it treats row as a single object. It would be possible if you would change your table structure:
CREATE TABLE Orders
(
OrderId NVARCHAR(56) PRIMARY KEY,
BillingAddressId INT
ShippingAddressId INT
)
Then you would have to change your class to:
public class Order
{
public string OrderId { get; set; }
public int ShippingAddressId {get; set;}
public virtual Address ShippingAddress { get; set; }
public int BillingAddressId {get; set;}
public virtual Address BillingAddress { get; set; }
}
And just use multi mapping.
Another option is to use Dapper extensions like Dapper-FluentMap or Dapper Extensions which will help you map columns to classes.
Related
I'm using dapper to get data from database which looks as follows (SQL Server)
Column1 column2 column3
Type1 (1,sub1,sub1 descreption),(2,sub2,sub2 description) test
Type2 (3,sub3,sub3 descreption),(4,sub4,sub4 description) test
Classes looks as follows:
public class Types
{
public string Column1 { get; set; }
public IEnumerable<Subs> Column2 { get; set; }
public string Column3 { get; set; }
}
public class Subs
{
public int id { get; set; }
public string name { get; set; }
public string description { get; set; }
}
I'm trying to cast Column2 as IEnumerable and unable to do so. Can someone suggest a way to do it? Do i need to change the way how data is selected from database?
Actually, column 2 should be a table itself with many relations with your current table and what you are currently doing is a very bad practice and can have many downsides.
The right way: Create a table that has Types table Id as foreign key:
CREATE TABLE Subs(
id int,
typeId int foreign key references [Types](id), // you must also add an id could to Types table
name nvarchar(128), //any size that you need it
description nvarchar(max)
)
and the class:
public class Sub
{
public int id { get; set; }
public int typeId {get; set; }
public string name { get; set; }
public string description { get; set; }
}
For how to get one to many relationships with dapper, please see this question:How to query an object with one-to-many relationship using Dapper?
I'm attempting to create an Address table which will contain a Country entity.
The Address table can only contain 1 Country entry. I'd like to have another table which contains a list of countries. On the UI I want a drop down list which will list all of the countries (which reside in the Countries table). When a country is selected, the Country entity in the Address table will be populated.
I'm unsure about the relationship between the Address table and the Country table. It seems to me that the Countries table is not a dependent table (stand alone).
I'm unsure how to annotate my properties to get the desired result.
Currently this is what my Address model looks like.
public class Address
{
[Key]
[DatabaseGenerated( DatabaseGeneratedOption.Identity )]
public int AddressId { get; set; }
[Required]
[MaxLength( 255 )]
public string AddressLine1 { get; set; }
[Required]
[MaxLength( 50 )]
public string Region { get; set; }
[Required]
public Country Country { get; set; }
}
And the Country model.
public class Country
{
[Key]
[DatabaseGenerated( DatabaseGeneratedOption.Identity )]
public int CountryId { get; set; }
[Required]
public string CountryName { get; set; }
public Address Address { get; set; }
[ForeignKey( nameof( AddressId ) )]
public int AddressId { get; set; }
}
And the context
public sealed class LodgingCrmContext : DbContext
{
public LodgingCrmContext( DbContextOptions<LodgingCrmContext> options )
: base( options )
{
}
public DbSet<Country> Countries { get; set; }
public DbSet<Address> Addresses { get; set; }
}
The Address FK and the Address navigation property in the Country model makes no sense to me. You wouldn't navigate from a country to an address. Furthermore the Countries table should be a standalone table, not dependent on the Address table. How can I accomplish this?
I'm a little confused and could use some help.
I am trying to retrieve a subset of properties in in Entity Framework Core (1.1.1) for an optional one-to-one relationship, but my query attempts result in all columns being retrieved in the query for both tables (verified with SQL Profiler).
For an example, lets say I have these models bound up to my DbContext:
public class Student
{
public int StudentId { get; set; }
public string StudentName { get; set; }
public string Birthday { get; set; }
public string FavoriteIceCream { get; set; }
public string OtherRandomInfoForExample { get; set; }
public virtual StudentAddress Address { get; set; }
}
public class StudentAddress
{
public int StudentId { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public int Zipcode { get; set; }
public string State { get; set; }
public string Country { get; set; }
public virtual Student Student { get; set; }
}
When I try projecting this into a new model choosing only the columns I want like the following:
var studentsCityInfos = _dbContext.Student.AsNoTracking().Select(s => new StudentCityInfo
{
Id = s.StudentId,
Name = s.StudentName,
City = s.Address.City
}).ToList();
The result is a query that returns all columns both tables. I have also tried the following with a null check on the navigation property and that leads to the same results:
var studentsCityInfos = _dbContext.Student.AsNoTracking().Select(s => new StudentCityInfo
{
Id = s.StudentId,
Name = s.StudentName,
City = s.Address == null ? null : s.Address.City
}).ToList();
Is this maybe just a projection bug in EF Core 1.1.1 that should be optimized?
Any alternative approaches anyone else can think of?
As a fall back I can break this up into two queries or use FromSql to write my own, but I would like to know the correct approach to this scenario.
For completeness I have no control over the schema and both tables have many more columns and roughly a million rows.
Thank you for taking the time to read and provide answers!
EDIT: as requested, here is an example of the sql profiler results (manually made to reflect the results from my scenario):
SELECT [s].[StudentId], [s].[StudentName], [s].[Birthday], [s].[FavoriteIceCream], [s].[OtherRandomInfoForExample], [s].[BusinessFullName], [s].[BusinessLastName], [s.Address].[StudentId], [s.Address].[Address1], [s.Address].[Address2], [s.Address].[City], [s.Address].[Zipcode], [s.Address].[State], [s.Address].[Country]
FROM [Student] AS [s]
LEFT JOIN [StudentAddress] AS [s.Address] ON [s].[StudentId] = [s.Address].[StudentId]
ORDER BY [s.Address].[StudentId]
I currently have an employee model
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<StateLicenseType> Licenses { get; set; }
and a License Type Model
public class StateLicenseType
{
public int StateLicenseTypeId { get; set; }
public string State { get; set; }
public string LicenseName { get; set; }
public virtual Employee Employee { get; set; }
}
This relationship can be one to many, but I also need to add some information to the license when saved. I need to be able to store the employees unique license number and have not been able to find out how to do this while searching around. Is there a way to have Entity Framework add a column to a join table and then even if I have to, update it myself?
Is there a better/different way to model this relationship with EF?
In an old DB the table was created like this,
CREATE TABLE `nmlsstatelicenses` ( `peopleid` int(11) DEFAULT NULL, `statelicensetypeid` int(11) DEFAULT NULL, `licensenumber` varchar(25) DEFAULT NULL)
You need to create a third entity which will be a linking entity (like a linking table in many-to-many relationships in database. Here is an example: many-to-many relationships with additional information.
So you would have the following entities in your model:
public Employee
{
public string EmployeeId { get;set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<LicenseRegistration> RegisteredLicenses { get; set; }
}
public LicenseType
{
public int StateLicenseTypeId { get; set; }
public string State { get; set; }
public string LicenseName { get; set; }
public virtual ICollection<LicenseRegistration> RegisteredLicenses { get; set; }
}
public LicenseRegistration
{
//properties for the additional information go here
/////////////////////////////////////////////////////
public int EmployeeId {get;set;}
[ForeignKey("EmployeeId")]
public Employee Employee {get;set;}
public int LicenseTypeId {get;set;}
[ForeignKey("LicenseTypeId")]
public LicenseType {get;set;}
}
Then, in your DBContext file, you will need to define 1-to-many relationship between Employee and LicenseRegistration, and between LicenseType and LicenseRegistration.
Hope this helps!
UPDATE
Here is how you would set up the relationships:
modelbuilder.Entity<LicenseRegistration>()
.HasRequired(lr => lr.LicenseType)
.WithMany(lt => lt.RegisteredLicenses)
.HasForeignKey(lr => lr.LicenseTypeId);
modelbuilder.Entity<LicenseRegistration>()
.HasRequired(lr => lr.Employee)
.WithMany(e => e.RegisteredLicenses)
.HasForeignKey(lr => lr.EmployeeId);
I have a simple scenario, but can't figure out how to setup a ViewModel. I have a CustomerController.cs, Create.cshtml, and Edit.cshtml. On these views I have fields to take in basic customer information like Name, Phone, Fax, Email.
There is a foreign key in my Customer table called AddressId. The Address table consists of columns like AddressId, Address1, Address2, City, State, Zip.
I am using Entity Framework so I can simply get a customer's address by doing Customer.Address
My form consists of both the customer and address fields which is where my confusion comes in. Do I need all of the address fields, do I at least need AddressId which is the foreign key, do I not need AddressId, but simply Address, etc..
Here is what I currently have in my CustomerInfoViewModel:
public string Name { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string Fax { get; set; }
public int AddressId { get; set; }
//Address fields
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
[Display(Name = "State")]
public int StateId { get; set; }
[Display(Name = "Zip Code")]
public string ZipCode { get; set; }
[Display(Name = "Country")]
public int CountryId { get; set; }
I have a State and Country dropdown on the page so I figured I would only need the Ids of State and Country. So I am confused on whether I do all of the above or rather than add all of the Address fields simply add:
public Address Address { get; set; }
You should able to do it either. The biggest deciding factor is if you want to pull in and bind properties to the view model or the entity model. So whether you decide:
[HttpPost]
public ActionResult Edit(CustomerInfoViewModel vm)
Or
[HttpPost]
public ActionResult Edit(Customer model)
It's really more about code style.