Dapper Multi Mapping stops at 3rd lavel nesting - c#

I have taken a question and modified it to form my question. I have a query that is mapping correctly from the initial class, to the second class and elements attached to the second class (3rd) but not elements attached to the 3rd class. an example as follows.
public class Part {
public int Id { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
}
public class Address {
public int Id { get; set; }
public string Street { get; set; }
public SiteOu Ou { get; set; }
}
public class SiteOu {
public int Id { get; set; }
public SiteOuName SiteOuN { get; set; }
}
public class SiteOuName
{
public int Id { get; set; }
public string Name { get; set; }
}
Dapper:
public void TestSplitOn()
{
var conn = new SqlConnection(#"Data Source=.\SQLEXPRESS;Integrated Security=true;Initial Catalog=db");
conn.Open();
const string sql = "select Id = 1, Name = 'My Part', " +
"Id = 2, Street = 'My Street', " +
"Id = 1, SiteOuNameId = '1'" +
"Id = 1, Name = 'My Site', " +;
var result = conn.Query<Part, Address, SiteOu, SiteOuName, Part>(sql, (part, address, siteOu, siteOuName) =>
{
part.Address = address;
address.Ou = siteOu;
SiteOu.SiteOuName = siteOuName
return part;
},
commandType: CommandType.Text
).FirstOrDefault();
}
If I remove SiteOuName from the Dapper mapping, the code works but doesn't map the SiteOuName object, but when I leave it as it is, it shows me that the address object reference is null.

As far as I can see there are couple of issues in your Dapper code.
Firstly, your query should be like;
const string sql = "select Id = 1, Name = 'My Part', " +
"Id = 2, Street = 'My Street', " +
"Id = 1, SiteOuNameId = '1'," +
"Id = 1, Name = 'My Site'";
You are missing a comma next to the SiteOuNameId = '1' and you have extra comma and '+' sign next to Name = 'My Site'
Secondly, your mapping is wrong, it should be like
part.Address = address;
address.Ou = siteOu;
siteOu.SiteOuN = siteOuName;
return part;
In your code, S is upper case which makes complier think you are using the SiteOu class.
Also, SiteOu class doesn't have a property named SiteOuName, it should be SiteOuN.

Related

Database concurrency exception

we're working on a database for our study project, and we've got unexpected error while adding data to a table.
using (var ctx = new BazyDanychContext())
{
Osoba tmpTask = new Osoba { Imie = ImieLbl.Text, Nazwisko = NazwiskoLbl.Text, Telefon = TelefonLbl.Text, Adres = AdresLbl.Text, Mail = MailLbl.Text, IloscTransakcji = Int32.Parse(IloscLbl.Text), Typ = TypList.Text };
ctx.Osoba.Add(tmpTask);
ctx.SaveChanges();
}
We also tried:
To add new records using ExecuteSqlCommand, which worked just fine
ctx.Database.ExecuteSqlCommand("INSERT INTO dbo.Osoba VALUES('0','jan','nowak', '222222', 'adres', 'mail', '2', 'osoba')");
Using ctx.Entry(tmpTask).State = System.Data.Entity.EntityState.Added; (and also .Modified)
Typing the values manually (like in the ExecuteSqlCommand line)
No matter what we do, ctx.SaveChanges() gives us
OptimisticConcurrencyException: Store update, insert, or delete statement affected an unexpected number of rows (0).
Our classes:
namespace BazyDanych
{
public class BazyDanychContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
base.OnModelCreating(modelBuilder);
}
public BazyDanychContext() : base("ProjektBD")
{
Database.SetInitializer(new SQLdb());
}
public DbSet<Osoba> Osoba { get; set; }
}
}
namespace BazyDanych
{
public class Osoba
{
public int ID { get; set; }
public string Imie { get; set; }
public string Nazwisko { get; set; }
public string Telefon { get; set; }
public string Adres { get; set; }
public string Mail { get; set; }
public int IloscTransakcji { get; set; }
public string Typ { get; set; }
public override string ToString()
{
return "Imie: " + Imie + "\t Nazwisko: " + Nazwisko + "\t ID: " + ID.ToString();
}
}
}
Database table's structure looks like:
ID (int, null)
Imie (text, null)
Nazwisko (text, null)
Telefon (text, null)
Adres (text, null)
Mail (text, null)
IloscTransakcji (int, null)
Typ (text, null)
It looks like in your C# Entity Framework code, you are not including a value for ID, which would imply that you have it set to auto-increment as an IDENTITY value. However, in your INSERT statement, you include a value for it, which would not work if you had it set as an IDENTIY value. To solve your issue, you can do one of two things:
Change your ID column to auto-increment. This will allow you to keep your C# code as it is, and it should work as expected without supplying a value. Do this if you don't want to be directly responsible for the value inserted there.
Include a value in your EF code for the ID column:
using (var ctx = new BazyDanychContext())
{
Osoba tmpTask = new Osoba { ID = IDLbl.Text, Imie = ImieLbl.Text, Nazwisko = NazwiskoLbl.Text, Telefon = TelefonLbl.Text, Adres = AdresLbl.Text, Mail = MailLbl.Text, IloscTransakcji = Int32.Parse(IloscLbl.Text), Typ = TypList.Text };
ctx.Osoba.Add(tmpTask);
ctx.SaveChanges();
}

Unable to show the contents of the second table in Fiddler

I am using Linq to join 2 tables. I am able to get the contents of the first table but getting the second table as null. How can I extract the contents of the second table also into a single JSON object. My code is below:
public static IEnumerable<Tbl_Students> GetAllStudents()
{
StudentDBEntities dataContext = new StudentDBEntities();
var query = (from student in dataContext.Tbl_Students
join subject in dataContext.Tbl_Subjects on student.Roll_Number equals subject.Roll_Number
select new
{
Roll_Number = student.Roll_Number,
FirstName = student.FirstName,
LastName = student.LastName,
Class = student.Class,
Gender = student.Gender,
Science = subject.Science,
Social = subject.Social,
Mathematics = subject.Mathematics,
Total = subject.Total
}).ToList().Select(s => new Tbl_Students
{
Roll_Number = s.Roll_Number,
FirstName = s.FirstName,
LastName = s.LastName,
Class = s.Class,
Gender = s.Gender
});
return query;
}
The two table structures are:
Student
public class Student
{
public int Roll_Number { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Class { get; set; }
public string Gender { get; set; }
}
Subject
class Subject
{
public int Roll_Number { get; set; }
public int Science { get; set; }
public int Social { get; set; }
public int Mathematics { get; set; }
public int Total { get; set; }
}
I am getting everything except the subjects in Fiddler.
Adding the image
Problem is that you are never populating Tbl_Subjects from your query. I have updated your query to populate Tbl_Subjects.
public static IEnumerable<Tbl_Students> GetAllStudents()
{
StudentDBEntities dataContext = new StudentDBEntities();
var query = (from student in dataContext.Tbl_Students
join subject in dataContext.Tbl_Subjects on student.Roll_Number equals subject.Roll_Number
select new
{
Roll_Number = student.Roll_Number,
FirstName = student.FirstName,
LastName = student.LastName,
Class = student.Class,
Gender = student.Gender,
Science = subject.Science,
Social = subject.Social,
Mathematics = subject.Mathematics,
Total = subject.Total
}).ToList().Select(s => new Tbl_Students
{
Roll_Number = s.Roll_Number,
FirstName = s.FirstName,
LastName = s.LastName,
Class = s.Class,
Gender = s.Gender,
Tbl_Subjects = new Tbl_Subjects ()
{
Science = s.Science,
Social = s.Social,
Mathematics = s.Mathematics,
Total = s.Total
Roll_Number = s.Roll_Number
};
});
return query;
}

Cassnadra large collection insert with nested object in C#

I'm writing this question after long fight with Cassandra database. I would like to insert large collection (~1000000) of Movie objects:
public class Movie
{
public Guid Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public int Year { get; set; }
public string Genres { get; set; }
public int Rating { get; set; }
public string OriginalLanguage { get; set; }
public string ProductionCountry { get; set; }
public int VotingsNumber { get; set; }
public Director Director { get; set; }
}
with nested field Director:
public class Director
{
public Guid Id { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public int Age { get; set; }
}
I'm using DataStax C# Driver and I tied different ways, but still nothing. Currently my code looks like this:
private void CreateSchema()
{
Session.Execute("CREATE KEYSPACE IF NOT EXISTS test WITH replication " +
"= {'class':'SimpleStrategy', 'replication_factor':3};");
Session.Execute("CREATE TYPE IF NOT EXISTS test.director (" +
"firstname text," +
"lastname text," +
"age int," +
");");
Session.Execute("CREATE TABLE IF NOT EXISTS test.Movies (" +
"id uuid," +
"title text," +
"description text," +
"year int," +
"genres text," +
"rating int," +
"originallanguage text," +
"productioncountry text," +
"votingsnumber int," +
"director frozen<director>," +
"PRIMARY KEY (id)" +
");");
}
public string TimeOfCollectionInsert(int collectionEntriesNumber)
{
var watch = new Stopwatch();
try
{
IList<Movie> moviesList = Movie.CreateMoviesCollectionForCassandra(collectionEntriesNumber);
var preparedStatements = new List<PreparedStatement>();
var statementBinding = new List<BoundStatement>();
for (int i = 0; i < collectionEntriesNumber; i++)
{
preparedStatements.Add(Session.Prepare("INSERT INTO test.Movies (id, title, description, year, genres, rating, originallanguage, productioncountry, votingsnumber, actors) VALUES (?,?,?,?,?,?,?,?,?,{ 'director': { firstname: 'DirectorName', lastname: 'DirectorLastname', age: 50 }});"));
}
for (int i = 0; i < collectionEntriesNumber; i++)
{
statementBinding.Add(preparedStatements[i].Bind(moviesList[i].Id, moviesList[i].Title, moviesList[i].Description, moviesList[i].Year, moviesList[i].Genres, moviesList[i].Rating, moviesList[i].OriginalLanguage, moviesList[i].ProductionCountry, moviesList[i].VotingsNumber));
}
watch.Start();
for (int i = 0; i < collectionEntriesNumber; i++)
{
Session.Execute(statementBinding[i]);
}
watch.Stop();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return watch.ElapsedMilliseconds.ToString();
}
Both methods runs successfuly, but I would like to create directors dynamically.
I will be grateful for any help.
btw. Is this good way for measure cassandra bulk insert performace?
Thanks,
P
You have to map your Cassandra UDT (user defined type) director to your Director C# class.
For more information, you should read the documentation :
http://docs.datastax.com/en/developer/csharp-driver/2.7/csharp-driver/reference/21features/mappingUdts.html

Access the contents of an IQueryable/System.Collections.Generic.List?

I have an MVC5/Code-First application I'm developing using Entity Framework. Currently I'm trying to add Export to Excel functionality to output user selected properties of my INV_Assets model dynamically. Using the EPPlus Libary and Linq.Dynamic I have managed to export my data to excel, but not quite correctly.
I've gotten the Headers to export into Row 1, but I'm still having difficulty getting the data to export. Currently the data for my selected fields all comes across, but each value is exported as a long string into it's own row in Column A. For example, if I select the following fields (Status, ip_address, mac_address, note, owner, cost, po_number, and description) I get the following:
Row1: [Status][ip_address][mac_address][note][owner][cost][po_number][description]
Row2: [{Status=SIGNEDOUT, ip_address=10.10.121.25, mac_address=10.10.134.11, note=, owner=John Smith, cost=35.00, po_number=G348, description=This is a description of the item.}][][][][][][][]
Here is a visual of the output I'm currently getting in Excel:
When I set my IQueryable variable (selectStatement) as a Watch variable in VS2013 I am able to drill down into the contents, but I can't figure out how to access these contents individually in code:
Currently I use my IQueryable and load it into Excel via the EPPlus LoadFromCollection() method, but if I can figure out how to access individual contents of my IQueryable I can then set up some counters and loops to appropriately set the cells I want instead of everything dumping into ColumnA.
Can anyone assist with this? Full code for my ExportController below:
public ActionResult ExportUsingEPPlus(ExportAssetsViewModel model)
{
ExcelPackage package = new ExcelPackage();
var ws = package.Workbook.Worksheets.Add("TestExport");
var exportFields = new List<string>();
foreach (var selectedField in model.SelectedFields)
{
// Adds selected fields to [exportFields] List<string>
exportFields.Add(model.ListOfExportFields.First(s => s.Key == selectedField).Value);
}
IQueryable selectStatement = DynamicSelectionColumns(exportFields);
// Loops to insert column headings into Row 1 of Excel
for (int i = 0; i < exportFields.Count(); i++)
{
ws.Cells[1, i + 1].Value = exportFields[i].ToString();
}
// Place contents of IQueryable into Excel -- currently dumps selected value for each record into rows with all values for the row as a long string in ColumnA
if (selectStatement.Count() > 0)
{
ws.Cells["A2"].LoadFromCollection(selectStatement.Cast<object>(), true);
}
int cnt = 20;
foreach (var item in selectStatement)
{
ws.Cells["A" + cnt].LoadFromCollection(selectStatement.Cast<object>(), false);
cnt++;
}
var memoryStream = new MemoryStream();
package.SaveAs(memoryStream);
string fileName = "Exported-InventoryAssets-" + DateTime.Now + ".xlsx";
string contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
memoryStream.Position = 0;
return File(memoryStream, contentType, fileName);
}
public IQueryable DynamicSelectionColumns(List<string> fieldsForExport)
{
using (var db = new InventoryTrackerContext())
{
string fieldIds = "," + "4,5,3,2,6,17,11,12" + ",";
var taskColum = Enum.GetValues(typeof(EnumTasks)).Cast<EnumTasks>().Where(e => fieldIds.Contains("," + ((int)e).ToString() + ",")).Select(e => e.ToString().Replace("_", ""));
//string select = "new ( TaskId, " + (taskColum.Count() > 0 ? string.Join(", ", taskColum) + ", " : "") + "Id )";
string select = "new ( " + string.Join(", ", fieldsForExport) + ")";
//return db.INV_Assets.ToList().Select(t => new DynamicColumns() { Id = t.Id, TaskId = Project != null ? Project.Alias + "-" + t.Id : t.Id.ToString(),
return db.INV_Assets.ToList().Select(t => new DynamicColumns()
{
Id = t.Id,
Manufacturer = Convert.ToString(t.Manufacturer.manufacturer_description),
Type = t.Type.type_description,
Location = t.Location.location_room,
Vendor = t.Vendor.vendor_name,
Status = t.Status.status_description,
ip_address = t.ip_address,
mac_address = t.mac_address,
note = t.note,
owner = t.owner,
//Module = t.Module != null ? t.Module.Name : "",
cost = t.cost,
po_number = t.po_number,
description = t.description,
invoice_number = t.invoice_number,
serial_number = t.serial_number,
asset_tag_number = t.asset_tag_number,
acquired_date = t.acquired_date,
disposed_date = t.disposed_date,
verified_date = t.verified_date,
created_date = t.created_date,
created_by = t.created_by,
modified_date = t.modified_date,
modified_by = t.modified_by
}).ToList().AsQueryable().Select(select);
}
}
}
public class DynamicColumns : INV_Assets
{
public string Model { get; set; }
public string Manufacturer { get; set; }
public string Type { get; set; }
public string Location { get; set; }
public string Vendor { get; set; }
public string Status { get; set; }
public string ip_address { get; set; }
public string mac_address { get; set; }
public string note { get; set; }
public string owner { get; set; }
public decimal cost { get; set; }
public string po_number { get; set; }
public string description { get; set; }
public int invoice_number { get; set; }
public string serial_number { get; set; }
public string asset_tag_number { get; set; }
public DateTime? acquired_date { get; set; }
public DateTime? disposed_date { get; set; }
public DateTime? verified_date { get; set; }
public DateTime created_date { get; set; }
public string created_by { get; set; }
public DateTime? modified_date { get; set; }
public string modified_by { get; set; }
}
public enum EnumTasks
{
Model = 1,
Manufacturer = 2,
Type = 3,
Location = 4,
Vendor = 5,
Status = 6,
ip_address = 7,
mac_address = 8,
note = 9,
owner = 10,
cost = 11,
po_number = 12,
description = 13,
invoice_number = 14,
serial_number = 15,
asset_tag_number = 16,
acquired_date = 17,
disposed_date = 18,
verified_date = 19,
created_date = 20,
created_by = 21,
modified_date = 22,
modified_by = 23
}
The output you are getting in your non-header cells:
Status=SIGNEDOUT, ip_address=10.10.121.25, mac_address=10.10.134.11, note=, owner=John Smith, cost=35.00, po_number=G348, description=This is a description of the item.
looks like the object represented as property=value, separated by commas. I would guess that the object's ToString() method is overridden and creating that output.
Since you are casting your strongly typed objects to System.Object
selectStatement.Cast<object>()
that is probably the best that EPPlus can do.
Try not casting it to System.Object, e.g.
ws.Cells["A2"].LoadFromCollection(selectStatement, true);
Here's an article that shows proper use of LoadFromCollection
http://www.sitecorecleveland.com/resources/blogs-posts/easy_excel_interaction_pt5
UPDATE
The error
The type arguments for method 'OfficeOpenXml.ExcelRangeBase.LoadFromCollection(System.Collection.Generic.IE‌​numerable, bool)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
is telling you that you need to specify the actual type returned by the query (rather than specifying object).
ws.Cells["A2"].LoadFromCollection(selectStatement.Cast<DynamicColumns>(), true);

Complex type mapping via linq to xml

I have a list of contacts in XML file.
Each contact have a few properties and mdpr:connection in it.
Connection is separate object.
I read this list and get all contacts to a list with standard proeprties but how to map this Connection to object.
<?xml version="1.0" encoding="UTF-8"?>
<mdpr:Data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mdpr="http://...">
<mdpr:contactList>
<mdpr:contact ID="{123456}" classID="Customer">
<mdpr:Name>data1</mdpr:Name>
<mdpr:TransportCode>data2</mdpr:TransportCode>
<mdpr:connection connectionIndex="0" fromID="{12345}" toID="{123456}">
<mdpr:status>1-5</mdpr:status>
<mdpr:startDate>2012-03-13T10:23:00Z</mdpr:startDate>
<mdpr:endDate>2013-03-13T13:44:00Z</mdpr:endDate>
</mdpr:connection>
</mdpr:contact>
</mdpr:contactList>
...
Classes:
public class Contact
{
public string Name { get; set; }
public string TransportCode { get; set; }
public Connection Connection { get; set; }
public TransportPlan()
{
this.Connection = new Connection();
}
}
public class Connection
{
public string status{ get; set; }
public string startDate{ get; set; }
public string endDate { get; set; }
}
Code to read data:
XNamespace mdpr = "http://...";
var contacts = from c in xdoc.Root.Element(mdpr + "contactList")
.Elements(mdpr + "contact")
select new Contact {
TransportCode = (string)c.Element(mdpr + "TransportCode"),
Name = (string)c.Element(mdpr + "Name")
};
So the question is how to read mdpr:connection?
You can access the elements directly by adding another '.Element'. I added a variable for better readability.
var contacts = from c in xdoc.Element(mdpr + "Data")
.Element(mdpr + "contactList")
.Elements(mdpr + "contact")
let contact = c
let connection = contact.Element(mdpr + "connection")
select new Contact
{
TransportCode = (string)contact.Element(mdpr + "TransportCode"),
Name = (string)contact.Element(mdpr + "Name"),
Connection = new Connection
{
status = (string)connection.Element(mdpr + "status"),
startDate = (string) connection.Element(mdpr + "startDate"),
endDate = (string)connection.Element(mdpr + "endDate"),
},
};
If you want to allow multiple connections (in order to make the scenario more complex)
public class Contact
{
public string Name { get; set; }
public string TransportCode { get; set; }
public List<Connection> Connections { get; set; }
}
Code to parse multiple connections
var contacts = from c in xdoc.Element(mdpr + "Data")
.Element(mdpr + "contactList")
.Elements(mdpr + "contact")
let contact = c
let connections = contact.Elements(mdpr + "connection")
select new Contact
{
TransportCode = (string)contact.Element(mdpr + "TransportCode"),
Name = (string)contact.Element(mdpr + "Name"),
Connections = connections.Select( connection =>
new Connection
{
status = (string)connection.Element(mdpr + "status"),
startDate = (string) connection.Element(mdpr + "startDate"),
endDate = (string)connection.Element(mdpr + "endDate"),
}).ToList(),
};

Categories