I am using MVC4 C# Razor view and MS SQL Server. I need to insert a list/array value from controller to sql server. I am passing values from view to controller and getting the values in controller.
My data structures are -
{sid: "101", m1Qty: "1", m2Qty: "3", m3Qty: ""}
{sid: "102", m1Qty: "5", m2Qty: "6", m3Qty: ""}
{sid: "103", m1Qty: "8", m2Qty: "0", m3Qty: ""}
Above data needed to insert my table (tbl_monthqty) in the below order. ID auto generated -
ID SID MonthID mQty
1 101 1 1
2 102 1 5
3 103 1 8
4 101 2 3
5 102 2 6
If any value null or 0, need to ignore
MonthID is for example - m1Qty = 1, m2Qty = 2, m3Qty = 3
My controller (C#) is -
[HttpPost]
public JsonResult SaveQty(IList<AllQty> model)
{
var list = new [] { model };
var count = list.Count();
DataTable dt = new DataTable();
dt.Columns.Add("SID");
dt.Columns.Add("MonthID");
dt.Columns.Add("mQty");
for(int i=0; i<count; i++)
{
//dt.Rows.Add();
// Not sure what I will do here
}
return Json(new { success = true });
}
My class is -
public class AllQty
{
public int SID { get; set; }
public int MonthID { get; set; }
public int mQty { get; set; }
}
I am getting the list value in controller but not sure how I will insert those list/array values in my table. I have tried few asked questions like this but did not work.
First create data model that represent json data structure:
public class FirstModel
{
public int SID;
public string m1Qty;
public string m2Qty;
public string m3Qty;
}
Then data model that you want to store the data:
public class AllQty
{
public int SID { get; set; }
public int MonthID { get; set; }
public int mQty { get; set; }
}
Then convert the json to list of FirstModel objects (I assume you already did it), and finally convert data in List to List :
List<FirstModel> qtYs = new List<FirstModel>();
List<AllQty> allQties = new List<AllQty>();
foreach (FirstModel item in qtYs)
{
if (string.IsNullOrEmpty(item.m1Qty))
{
AllQty allQty = new AllQty
{
MonthID = 1,
mQty = int.Parse(item.m1Qty),
SID = item.SID
};
allQties.Add(allQty);
}
if (string.IsNullOrEmpty(item.m2Qty))
{
AllQty allQty = new AllQty
{
MonthID = 2,
mQty = int.Parse(item.m1Qty),
SID = item.SID
};
allQties.Add(allQty);
}
if (string.IsNullOrEmpty(item.m3Qty))
{
AllQty allQty = new AllQty
{
MonthID = 3,
mQty = int.Parse(item.m1Qty),
SID = item.SID
};
allQties.Add(allQty);
}
}
DataTable dt = new DataTable();
dt.Columns.Add("SID");
dt.Columns.Add("MonthID");
dt.Columns.Add("mQty");
foreach (AllQty allQty in allQties)
{
var row = dt.NewRow();
row["SID"] = allQty.SID;
row["MonthID"] = allQty.MonthID;
row["mQty"] = allQty.mQty;
dt.Rows.Add(row);
}
Related
suppose this base class
public class Contrat_Paye_Item
{
public int CPI_TYPE { get; set; }
public string CPI_TEXT { get; set; }
public decimal CPI_AMOUNT { get; set; }
}
in my view Model i have made a list from that class like this :
private ObservableCollection<Contrat_Paye_Item> listapayer;
public ObservableCollection<Contrat_Paye_Item> ListaPayer
{
get
{
return listapayer;
}
set
{
listapayer = value;
OnPropertyChanged("ListaPayer");
}
}
and in the VM constructor, just some testdata :
listapayer.Add(new Contrat_Paye_Item()
{
CPI_TYPE = 0,
CPI_TEXT = "Loyer",
CPI_AMOUNT = 100
});
listapayer.Add(new Contrat_Paye_Item()
{
CPI_TYPE = 1,
CPI_TEXT = "charge 1",
CPI_AMOUNT = 200
});
listapayer.Add(new Contrat_Paye_Item()
{
CPI_TYPE = 1,
CPI_TEXT = "Charges 2",
CPI_AMOUNT = 300
});
so the full list contain 3 item, the total Amount is 600 .
in my viewmodel i have added a sublist with only items that their CPI_TYPE = 1 and bound it to a listbox without problem like this :
public ICollectionView ListCharges
{
get
{
var sub= new CollectionViewSource { Source = listapayer }.View;
sub.Filter = p => (p as Contrat_Paye_Item).CPI_TYPE == 1;
return sub;
}
}
now what i want to display is a list of my items , with a row that contain a sum of amounts items of the sublist . a bind it to a second listbox
something like :
loyer 100
charges 500
any idea, tip is welcome .
thanks
How about adding something like this before returning the sub:
var d1 = (from a in sub.SourceCollection.OfType<Contrat_Paye_Item>() where a.CPI_TEXT == "Loyer" select a.CPI_AMOUNT).Sum();
var d2 = (from a in sub.SourceCollection.OfType<Contrat_Paye_Item>() where a.CPI_TEXT.ToLower().StartsWith("charge") select a.CPI_AMOUNT).Sum();
sub.AddNewItem(new Contrat_Paye_Item() { CPI_TEXT="Sum Loyer", CPI_AMOUNT = d1} );
sub.CommitNew();
sub.AddNewItem(new Contrat_Paye_Item() { CPI_TEXT = "Sum Charges", CPI_AMOUNT = d2 });
sub.CommitNew();
//Edit
Note this as a more flexible possibility:
Do not use _CollectionViewSource _. Instead use the following as the ItemsSource of the ListBox:
public ObservableCollection<Contrat_Paye_Item> ListCharges
{
get
{
return new ObservableCollection<Contrat_Paye_Item>((from a in listapayer where a.CPI_TYPE == 1 select a));
}
}
From Github:
Dapper allows you to map a single row to multiple objects. This is a
key feature if you want to avoid extraneous querying and eager load
associations.
Example:
Consider 2 classes: Post and User
> class Post {
> public int Id { get; set; }
> public string Title { get; set; }
> public string Content { get; set; }
> public User Owner { get; set; } }
>
> class User {
> public int Id { get; set; }
> public string Name { get; set; } }
Now let us say that we want to map a query that joins both the posts
and the users table. Until now
if we needed to combine the result of 2 queries, we'd need a new
object to express it but it makes more sense in this case to put the
User object inside the Post object.
When I do this (My classes are different names, but same construct), I get a Post and a User, a Post and a User. I'm using the Web API, so this is all JSON, if that matters. This is the way I'd see it if I did straight SQL in the Management Studio, you get the many rows and the corresponding User records
What if I want to send back the JSON that has the User once and all the posts in an array, then the next User, array of posts, etc.
id title content id name
1 Article1 Content1 55 Smith
2 Article2 Content2 55 Smith
3 Article3 Content3 55 Smith
I get the JSON back that has the User information over and over (as expected but not wanted). It's backwards.
What I want is a JSON object that has a format like this (I think this is correct):
{
"User": 55,
"Name": "Smith",
"Post": [
{
"id": 1,
"title": "title1",
"content":"MyContent1"
},
{
"id": 2,
"title": "title2",
"content":"MyContent2"
},
{
"id": 3,
"title": "title3",
"content":"MyContent2"
}
]
}
How do I do this? Right now I get the reverse. I thought I would simply change the classes around, but I did not because of the instructions on Github, the "makes more sense" part. I am using this,
(List<Post>)db.Query<Post, User, Paper>(sqlString, (post, user) => { post.user = user; return post; }, splitOn: "id");
I know I don't need the splitOn here, but in my real query the name is different than id.
This is pretty close:
https://www.tritac.com/developers-blog/dapper-net-by-example/
public class Shop {
public int? Id {get;set;}
public string Name {get;set;}
public string Url {get;set;}
public IList<Account> Accounts {get;set;}
}
public class Account {
public int? Id {get;set;}
public string Name {get;set;}
public string Address {get;set;}
public string Country {get;set;}
public int ShopId {get;set;}
}
var lookup = new Dictionary<int, Shop>()
conn.Query<Shop, Account, Shop>(#"
SELECT s.*, a.*
FROM Shop s
INNER JOIN Account a ON s.ShopId = a.ShopId
", (s, a) => {
Shop shop;
if (!lookup.TryGetValue(s.Id, out shop)) {
lookup.Add(s.Id, shop = s);
}
if (shop.Accounts == null)
shop.Accounts = new List<Account>();
shop.Accounts.Add(a);
return shop;
},
).AsQueryable();
var resultList = lookup.Values;
It makes the first object identifier. Not sure if I can use it like that or not. But this does do the array of books like I was asking, and I did not have to create a special object. Originally, it was supposed to be on Google Code, but I couldn't find this test on Github.
Since your SQL query is returning the flat records, i suggest you create a flat POCO and use dapper to map the result set to a collection of this. Once you have data in this collection, you can use LINQ GroupBy method to group it the way you want.
Assuming you have classes like
public class User
{
public int Id { set;get;}
public string Name { set;get;}
public IEnumerable<Post> Posts { set;get;}
}
public class Post
{
public int Id { set;get;}
public string Title{ set;get;}
public string Content { set;get;}
}
Now create the POCO for the flat result set row
public class UserPost
{
public int Id { set; get; }
public string Title { set; get; }
public string Content { set; get; }
public int UserId { set; get; }
public string Name { set; get; }
}
Now update your SQL query to return a result set with column name matching the above properties.
Now use Dapper to get the flat records
var userposts= new List<UserPost>();
using (var conn = new SqlConnection("YourConnectionString"))
{
userposts = conn.Query<UserPost>(query).ToList();
}
Now apply GroupBy
var groupedPosts = userposts.GroupBy(f => f.UserId, posts => posts, (k, v) =>
new User()
{
UserId = k,
Name = v.FirstOrDefault().Name,
Posts = v.Select(f => new Post() { Id = f.Id,
Title= f.Title,
Content = f.Content})
}).ToList();
Another option is to use .QueryMultiple
[Test]
public void TestQueryMultiple()
{
const string sql = #"select UserId = 55, Name = 'John Doe'
select PostId = 1, Content = 'hello'
union all select PostId = 2, Content = 'world'";
var multi = _sqlConnection.QueryMultiple(sql);
var user = multi.Read<User>().Single();
user.Posts = multi.Read<Post>().ToList();
Assert.That(user.Posts.Count, Is.EqualTo(2));
Assert.That(user.Posts.First().Content, Is.EqualTo("hello"));
Assert.That(user.Posts.Last().Content, Is.EqualTo("world"));
}
Update:
To return multiple users and their posts:
[Test]
public void TestQueryMultiple2()
{
const string sql = #"select UserId = 55, Name = 'John Doe'
select UserId = 55, PostId = 1, Content = 'hello'
union all select UserId = 55, PostId = 2, Content = 'world'";
var multi = _sqlConnection.QueryMultiple(sql);
var users = multi.Read<User>().ToList();
var posts = multi.Read<Post>().ToList();
foreach (var user in users)
{
user.Posts.AddRange(posts.Where(x => x.UserId == user.UserId));
}
Assert.That(users.Count, Is.EqualTo(1));
Assert.That(users.First().Posts.First().Content, Is.EqualTo("hello"));
Assert.That(users.First().Posts.Last().Content, Is.EqualTo("world"));
}
I have the following model:
public class Model {
public string Name { get; set; }
public List<int> Numbers { get; set; }
}
And an SQL query that returns the following dataset containing two nvarchar columns:
Name
Numbers
foo
1,2,3,4
bar
4,17
Is there a simple way to auto-assign the results of the query to a List<Model> using Dapper?
I know I could use multi-mapping and make the splitting myself in C# code, but I would rather get a simpler solution.
I'm not sure if you can call this "simpler", but something like this is an option:
public class Result
{
public string Name { get; set; }
public List<int> Numbers { get; set; }
}
public class DapperTests
{
[Test]
public void Test()
{
var conn = new SqlConnection(#"Data Source=.\sqlexpress; Integrated Security=true; Initial Catalog=mydb");
conn.Open();
var result = conn.Query<string, string, Result>(
"select Name = 'Foo', Numbers = '1,2,3' union all select Name = 'Bar', Numbers = '4,5,6'", (a, b) => new Result
{
Name = a,
Numbers = b.Split(',').Select(Int32.Parse).ToList()
}, splitOn: "*").ToList();
Assert.That(result.Count, Is.EqualTo(2));
Assert.That(result.FirstOrDefault(x => x.Name == "Foo").Numbers.Count, Is.GreaterThan(0));
Assert.That(result.FirstOrDefault(x => x.Name == "Bar").Numbers.Count, Is.GreaterThan(0));
}
}
An alternative option with multimapping... pretty ugly
public class Result
{
public string Name { get; set; }
public List<int> NumberList { get; set; }
public string Numbers { set { NumberList = value.Split(',').Select(Int32.Parse).ToList(); } }
}
public class DapperTests
{
[Test]
public void Test()
{
var conn = new SqlConnection(#"Data Source=.\sqlexpress; Integrated Security=true; Initial Catalog=mydb");
conn.Open();
var sql = #"
select Name = 'Foo', Numbers = '1,2,3';
select Name = 'Bar', Numbers = '4,5,6';";
var expectedResults = 2;
var results = new List<Result>();
using (var multi = conn.QueryMultiple(sql))
{
for (int i = 0; i < expectedResults; i++)
{
results.Add(multi.Read<Result>().Single());
}
}
Assert.That(results.Count, Is.EqualTo(2));
Assert.That(results.FirstOrDefault(x => x.Name == "Foo").NumberList.Count, Is.GreaterThan(0));
Assert.That(results.FirstOrDefault(x => x.Name == "Bar").NumberList.Count, Is.GreaterThan(0));
}
}
Is it possible to have a different number of subgroups in WPF DataGrid top-level groups?
I need to have something like tree view inside datagrid where expander-like tree nodes contains only some header and bottom-level leaves contains actual data rows. I've tried to accomplish this by a following approach:
public class User
{
public Token Token { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Gender { get; set; }
public string Country { get; set; }
}
public class Token
{
public string PrincipalName { get; set; }
public Token Child { get; set; }
public int Depth
{
get
{
return Child == null ? 1 : Child.Depth + 1;
}
}
public Token Add(string childPrincipalName)
{
var token = this;
while (token.Child != null)
{
token = token.Child;
}
var child = new Token { PrincipalName = childPrincipalName };
token.Child = child;
return this;
}
public static Token Create(string principalName)
{
return new Token { PrincipalName = principalName };
}
}
Then I create some entries like
var users = new List<User>{
new User{ Name="Hiro Nakamura", Gender="M", Age=24, Country="Japan",
Token = Token.Create("first")},
new User{ Name="Mohinder Suresh",Gender="M", Age=26, Country="India",
Token = Token.Create("second").Add("1")},
new User { Name = "Claire Bennette", Gender = "F", Age = 19, Country = "USA",
Token = Token.Create("second").Add("1")},
new User{ Name="Mohinder Suresh",Gender="M", Age=26, Country="India",
Token = Token.Create("second").Add("2")},
new User { Name = "Claire Bennette", Gender = "F", Age = 19, Country = "USA",
Token = Token.Create("second").Add("2")}
};
and form a collection view:
var collection = new ListCollectionView(users);
int tokenLength = users.Max(u => u.Token.Depth);
if (tokenLength > 0)
{
string token = "Token";
string child = ".Child";
string principalName = ".PrincipalName";
for (int i = 0; i < tokenLength; i++)
{
collection.GroupDescriptions.Add(new PropertyGroupDescription(token + principalName));
token += child;
}
}
The problem is the "second" group has two subgroup: "1" and "2" (which is correct) while the "first" has a subgroup with empty name (which is not what I need). I want the "first" group did not have a subgroup, only "Hiro Nakamura" user in itself.
I'm using multiple mapping for a current query and now I need to map another object on the initial query.
For example:
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 string Name { get; set; }
}
Dapper:
connection.Query<Part, Address, Part>(sql, (part, address) => {
part.Address = address;
});
How do I get the Address class to have the SiteOu information?
This example isn't what I'm actually doing because I've actually got
Query<T1,T2,T3,T4,T5,TResult>();
I'm doing 1 select and 5 joins in my query. So hopefully I don't need more overloads of Query.
Dapper allows you to map a single row to multiple objects, so you can just map SiteOu as part of the same query.
[Test]
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 = 3, Name = 'My Site'";
var result = conn.Query<Part, Address, SiteOu, Part>(sql, (part, address, siteOu) =>
{
part.Address = address;
address.Ou = siteOu;
return part;
},
commandType: CommandType.Text
).FirstOrDefault();
Assert.That(result, Is.Not.Null);
Assert.That(result.Address, Is.Not.Null);
Assert.That(result.Address.Ou, Is.Not.Null);
}
Important Note: Dapper assumes your Id columns are named "Id" or "id", if your primary key is different or you would like to split the wide row at point other than "Id", use the optional 'splitOn' parameter.
If you have more that 5 types to map, another out of the box option is to use QueryMultiple extension. Here is an example from the Dapper docs.
var sql =
#"
select * from Customers where CustomerId = #id
select * from Orders where CustomerId = #id
select * from Returns where CustomerId = #id";
using (var multi = connection.QueryMultiple(sql, new {id=selectedId}))
{
var customer = multi.Read<Customer>().Single();
var orders = multi.Read<Order>().ToList();
var returns = multi.Read<Return>().ToList();
...
}
Also check out this thread.