I have a List of custom object, which consist of a custom list.
class person{
string name;
int age;
List<friend> allMyFriends;
}
class friend{
string name;
string address;
}
I'trying to bind a list of these objects to a GridView and the Grid should create for each friend a column and write the name in it. If some people have the same frined the grid shouldn't create a seperate column, but use the existing one. You know what I mean.
(The classes are just some sample classes to simplify my case)
Is there a way to dynamically customize the binding?
I can change the class definitions and so on, if they need to inherit from some interfaces or so on.
I googled a lot, but no example really seemed to cover this case.
Could the use of a objectSourceControl solve my problem in some way?
Update:
To give some more information:
In the end I have a list of persons, while each person in the list has a list of friends.
List<person> allPerson = new List<person>();
// fill the list
Grid.DataSource = allPerson;
Grid.DataBind()
The table should have columns for each friend and the rows are the person. Where a person has a friend a cross (or whatever) needs to be placed in the grid.
friend1 friend2
x peter
x x adam
At the moment a intercept the RowDataBound event and since the binding only creates the rows with the names and not the columns, because the only property on my person object is the name. Is there a way to force the binding to look through the List Property in the person objects and create a column for each of them.
I was able to solve this using a DataTable as your datasource for the Grid. I don't like the idea of moving from a nice clean object to a DataTable, but it provides support for the dynamic binding you need. I modified your friend object to have a few constructors. This allowed me to cleanup the static code declaration but might not be necessary in your implmentation.
The basic idea is that you will step through all possible friends, add their name as a DataColumn in a DataTable, then fill in the data for all person objects and their respective friends. This could probably be written to work in a single iteration of the allPerson object but I preferred two iterations to make the code easier to read.
The solution is written for c# 3.5 but could be converted for older versions by changing the static data declaration. I hope this helps.
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// setup your person object with static data for testing
List<person> allPerson = new List<person>()
{
new person()
{
name = "Dan",
age = 21,
allMyFriends = new List<friend>() { new friend("James"), new friend("John"), new friend("Matt") }
},
new person()
{
name = "James",
age = 21,
allMyFriends = new List<friend>() { new friend("Dan"), new friend("Matt"), new friend("Tom") }
},
new person()
{
name = "John",
age = 21,
allMyFriends = new List<friend>() { new friend("Dan") }
},
new person()
{
name = "Matt",
age = 21,
allMyFriends = new List<friend>() { new friend("Dan"), new friend("James") }
},
new person()
{
name = "Tom",
age = 21,
allMyFriends = new List<friend>() { new friend("James") }
}
};
System.Data.DataTable dt = new System.Data.DataTable();
dt.Columns.Add("Name");
dt.Columns.Add("Age");
foreach (person p in allPerson)
{
// step through each person and look at their friends
foreach (friend f in p.allMyFriends)
{
// look to see if this friend has a column already
if (!dt.Columns.Contains(f.name))
{
dt.Columns.Add(f.name);
}
}
}
foreach (person p in allPerson)
{
// create the datarow that represents the person
System.Data.DataRow dr = dt.NewRow();
dr["Name"] = p.name;
dr["Age"] = p.age;
// find the friends and mark them
foreach (friend f in p.allMyFriends)
{
dr[f.name] = "X";
}
dt.Rows.Add(dr);
}
// fill the list
this.Grid.DataSource = dt;
this.Grid.DataBind();
}
}
public class person
{
public string name;
public int age;
public List<friend> allMyFriends = new List<friend>();
}
public class friend
{
public string name;
public string address;
public friend()
{
}
public friend(string name)
{
this.name = name;
}
public friend(string name, string address)
{
this.name = name;
this.address = address;
}
}
Edit:
I forgot to add how this is rendered.
-------------------------------------------------
| Name | Age | James | John | Matt | Dan | Tom |
-------------------------------------------------
| Dan | 21 | X | X | X | | |
| James | 21 | | | X | X | X |
| John | 21 | | | | X | |
| Matt | 21 | X | | | X | |
| Tom | 21 | X | | | | |
-------------------------------------------------
It sounds like you are trying to display a matrix / crosstab in GridView. You might find it easier to grab your retrieve your data in a format more compatible to this. You could consider writing a crosstab query if you are using SQL server.
If you must work with the objects in their current form, Creating a merged list of friends before starting could also help by providing the column list. You could then bind to each column to a function call which could attempt to find the column person in the rows friend list.
Not beautiful, but could work...
You could also just use the RowDataBound event handler to do complex bindings.
use the following :
DataBinder.Eval(Container.DataItem,"PPP.PPP")
Related
Nested records
Are records similar to dictionaries where it's a tree of objects with names?
Or records are just a list of simple types
let r = { b = { a = 2 } } // is this possible? if not, how to achieve? is this useful in f#?
For me with discriminated unions it's possible to achieve kind of similar behavior (code below)
// Discriminated union for a card's suit
type Suit = | Heart | Diamond | Spade | Club
let suits = [Heart; Diamond; Spade; Club]
suits
// Discriminated union for playing cards
type PlayingCard =
| Ace of Suit
| King of Suit
| Queen of Suit
| Jack of Suit
| ValueCard of int * Suit
// generating a deck of cards
let deckOfCards =
[
for suit in [Spade; Club; Heart; Diamond] do
yield Ace(suit)
yield King(suit)
yield Queen(suit)
yield Jack(suit)
for value in 2..10 do
yield ValueCard(value, suit)
]
It's kind of similar to a dictionary in python or idk. The code below is dummy
type Customer =
{
FirstName : string
Contacts =
{
WorkPhone : string
MobilePhone : string
}
}
Nested types can be created using anonymous records:
type Customer =
{
FirstName : string
Contacts :
{|
WorkPhone : string
MobilePhone : string
|}
}
let customer =
{
FirstName = "John"
Contacts =
{|
WorkPhone = "123-456-7890"
MobilePhone = "234-567-8901"
|}
}
You can see some patterns in the code, but records are not similar to dictionaries. You can think of them as of classes rather, with strongly typed public properties. If you need to create a dictionary, you have to use one of the available map/dictionary classes or implement your own. Have a look at the Map type for example.
https://fsharp.github.io/fsharp-core-docs/reference/fsharp-collections-fsharpmap-2.html
type Contact =
{
WorkPhone : string
MobilePhone : string
}
type Customer =
{
FirstName : string
Contacts : Contact
}
let cust : Customer =
{
FirstName = "Joe"
Contacts = { WorkPhone="1800131313"; MobilePhone="0863331311" }
}
The code above shows that you can nest the record types. Aside of using anonymous records as #brianberns suggested, you can declare the data types you plan to nest.
Yes, you can have nested records, but just like in your example with discriminated unions, you need to give a name to the nested type:
type CustomerConracts =
{
WorkPhone : string
MobilePhone : string
}
type Customer =
{
FirstName : string
Contacts: CustomerConracts
}
let c = { FirstName = "John", Contacts = { WorkPhone = "123", Mobile phone = "098" } }
I have list of class which class has 3 properties like this.
public string attributeName { get; set; }
public string strFormId { get; set; }
public string strValue { get; set; }
I am adding my database data into it through list like this
List<myAttributeData> attributesData = new List<myAttributeData>();
var result = db.ExecuteQuery<myAttributeData>(query, new object[0]);
// attributesData.Clear();
foreach (myAttributeData item in result.ToList())
{
if (item.attributeName == "Province ")
{
var Loc = from d in db.tblLocations
where d.LocationId == Convert.ToInt32(item.strValue)
select new
{
d.LocationName
};
foreach (var item1 in Loc.ToList())
{
attributesData.Add(new myAttributeData()
{
attributeName = item.attributeName,
strFormId = item.strFormId,
strValue = item1.LocationName
});
}
}
The problem is its taking so much time right now i have 70 thousands record into my database table which is take more than half hour any suggestion about this situation thanks. I have to add my data into list because I need it to use it back when it is needed can anybody give me solutions to cut the time of adding data into string.
One word: caching.
The problem with this code is that you're iterating through your 70,000 records, and for each one, you're going back to the database, to read in extra information.
foreach (myAttributeData item in result.ToList())
{
if (item.attributeName == "Province ")
{
var Loc = from d in db.tblLocations
where d.LocationId == Convert.ToInt32(item.strValue)
You'll find your code flies if you can cache the list of locations (before calling your foreach loop)
List<Location> cachedLocations = db.tblLocations.ToList();
..and then set your Loc variable from there:
var Loc = from d in cachedLocations
where d.LocationId == Convert.ToInt32(item.strValue)
Always keep the number of calls back to the database as low as possible.
Good luck!
I have an application which stores tasks and I want to add attachments to those tasks.
I have tried three different ways of doing this and don't know if any of them are correct and am looking for advice on where to go:
For example, simplified I have used a table:
+----------------------------------------------------------------------------+
| TaskID Description attachmentString |
+----------------------------------------------------------------------------+
| 1 Task1 "FileName1:::fileLocation;FileName2:::fileLocation" |
| 2 Task2 "FileName3:::fileLocation;FileName4:::fileLocation" |
+----------------------------------------------------------------------------+
This is similar to how profile data is stored in ASP.NET membership.
I have also tried:
+---------------------------+
| TaskID Description |
+---------------------------+
| 1 Task1 |
| 2 Task2 |
+---------------------------+
+------------------------------------------------------+
| AttachmentId Description Location TaskId |
+------------------------------------------------------+
| 1 FileName1 FileLocation 1 |
| 2 FileName2 FileLocation 1 |
+------------------------------------------------------+
If I use the first option, I can just select tasks and get all the attachment data in one SQL call; but it seems cluncky to me to have to then parse the string. Its also not very "relational"
However using an attachment Id, if I want to get the attachments, I either JOIN both tables on attachmentId and then have number of attachments x number of tasks returned. I can have up to 5 attachments so for 50 tasks, it could return 250 rows of which the first columns (from the task table side of the JOIN) are repeated and this seems like a waste. Obviously I have a little more than just description in my table!!!
I have also considered just getting the task data and then just getting the attachment data separately and then joining them in my application. This returns less data than the second option, but requires two calls to the database and that seems wrong too.
I am doing this wrong? Is there a better way? Does anyone have any thoughts on the best way to do this.
I'm not very confident with SQL and maybe I have missed something huge so any pointers would be gratefully received.
The right design is obviously two tables. Having only one table violates the first normal form.
Relating to the load problem, both approaches are correct.
Joining the tables in the sql statement is what most ORM's do to eagerly load related objects. Obviously there is some network traffic overhead, but I think it is acceptable.
Executing two separate sql statements is also correct. You can send them together in one batch to SQL Server to save roundtrips. It has a disadvantage although, you need to perform the join at the client side.
So, are you willing to write more code to save some network traffic?
EDIT:
Given the following table and data:
CREATE TABLE Tasks
(
TaskId int IDENTITY(1,1) PRIMARY KEY,
TaskDescription nvarchar(500) NOT NULL
)
CREATE TABLE TaskAttachments
(
AttachmentId int IDENTITY(1,1) PRIMARY KEY,
TaskId int NOT NULL REFERENCES Tasks(TaskId),
[FileName] nvarchar(500) NOT NULL,
[FileLocation] nvarchar(500) NOT NULL
)
GO
INSERT INTO Tasks VALUES
('Task1'), ('Task2')
INSERT INTO TaskAttachments VALUES
(1, 'FileName1', 'File location 1'),
(1, 'Filename2', 'File location 2'),
(2, 'FileName3', 'File location 3'),
(2, 'Filename4', 'File location 4')
The following classes:
public class TaskAttachment
{
public int AttachmentId { get; set; }
public string FileName { get; set; }
public string FileLocation { get; set; }
}
public class AppTask
{
public int TaskId { get; set; }
public string TaskDescription { get; set; }
public List<TaskAttachment> Attachments { get; set; }
public AppTask()
{
this.Attachments = new List<TaskAttachment>();
}
}
The following class loads the tasks with its attachments by executing two select statements in one single batch:
public class DataLayer
{
private readonly SqlConnection connection;
public DataLayer(SqlConnection connection)
{
this.connection = connection;
}
public List<AppTask> GetTasks()
{
var commandText = #"
SELECT TaskId, TaskDescription FROM Tasks;
SELECT AttachmentId, TaskId, [FileName], FileLocation FROM TaskAttachments;
";
using (var cmd = new SqlCommand(commandText, connection))
using (var reader = cmd.ExecuteReader())
{
var tasks = new List<AppTask>();
while (reader.Read())
{
var task = new AppTask
{
TaskId = reader.GetInt32(0),
TaskDescription = reader.GetString(1)
};
tasks.Add(task);
}
var taskDic = tasks.ToDictionary(x => x.TaskId);
reader.NextResult();
while (reader.Read())
{
var attachment = new TaskAttachment
{
AttachmentId = reader.GetInt32(0),
TaskId = reader.GetInt32(1),
FileName = reader.GetString(2),
FileLocation = reader.GetString(3)
};
var task = taskDic[attachment.TaskId];
task.Attachments.Add(attachment);
}
return tasks;
}
}
}
You can use the above class like this:
using (var cn = new SqlConnection("Data Source=(local);Initial Catalog=Tests;Integrated Security=SSPI"))
{
var dataLayer = new DataLayer(cn);
cn.Open();
var tasks = dataLayer.GetTasks();
}
I have a TableView<DataModelClass> table which has only one TableColumn name in my DataModelClass there is a ArrayList<String> nameList that can contain one or multiple names. Now, when I run my code, the Name column looks like this, [name1,name2,name3..]but I want to show each of name1 name2 name3 in a separate line. Like This,
-+-----------------+- -+-----------------+-
| Name | << I am Getting | Name |
-+-----------------+- Instade Like This >> -+-----------------+-
| [name1,name2,..]| | name1 |
-+-----------------+- | name2 |
| | | name3 |
-+-----------------+- -+-----------------+-
| |
-+-----------------+-
I hope You Get the Idea, But How Can I achive That?
Code Of DataModelClass
public class DataModelClass {
private ArrayList<String>nameList;
private DataModelClass(ArrayList<String> nameList) {
this.nameList = nameList;
}
public String getNameList() {
return nameList;
}
public void setNameList(ArrayList<String> nameList) {
this.nameList = nameList;
}
}
Code of TableVied
private TableView<DataModelClass> table = new TableView<DataModelClass>();
TableColumn colName = new TableColumn("Name");
colName.setMinWidth(80);
colName.setCellValueFactory(
new PropertyValueFactory<GroupDataForTable, ArrayList<String> >("nameList"));
private ObservableList<DataModelClass> groupData = FXCollections.observableArrayList();
groupData.add(new DataModelClass(someNonEmptyArrayList));
table.setItems(groupData);
Although your design is kinda weird, yet..
You can use streams and convert your list to a String object and return it from the getter method.
nameList.stream().collect(Collectors.joining("\n"));
This will give you each element in a newline, yet it is one complete String.
public class DataModelClass {
private List<String> nameList;
...
public String getNameList() {
return nameList.stream().collect(Collectors.joining("\n"));
}
...
}
In your class, where you are using TableView,
TableColumn<DataModelClass, String> name = new TableColumn<DataModelClass, String>("Name");
name.setCellValueFactory(new PropertyValueFactory<DataModelClass, String>("nameList"));
The type you specify for the TableView is the type of the object represented by each row of the table. Since you only have a single object of DataModelClass, that is not the correct type to use for your table view.
As your question stands, each row contains only a string, so you should have
private TableView<String> table = new TableView<String>();
and then you can fill the items from your DataModelClass instance simply by
DataModelClass data = new DataModelClass(someNonEmptyArrayList);
table.getItems().setAll(data.getNameList());
Now you just need to make sure the column displays the correct value:
TableColumn colName = new TableColumn("Name");
colName.setMinWidth(80);
colName.setCellValueFactory(cellData -> new ReadOnlyStringWrapper(cellData.getValue()));
table.getColumns().add(colName);
Can I convert a IQueryable result to a injected object on the fly?
I know I can do this with the help of Valueinjecter:
usercategory uc1 = new usercategory(example);
usercategoryViewData ucVD1 = new usercategoryViewData();
ucVD1.injectFrom(uc1);
So instead of this:
from u in db.usercategories
where u.id==id
select new usercategoryViewModel {id = u.id, name = u.id, description = u.id};
I would like to use something like:
from u in db.usercategories
where u.id==id
select new usercategoryViewModel.InjectFrom(u);
The other alternative I have atm is to loop through a IEnumerable and create one with injected objects instead.
here I show 2 ways of doing this:
public class Foo
{
public string Name { get; set; }
}
[Test]
public void TestMethod1()
{
var bars = new[] { new { Name = "aaa" }, new { Name = "bbb" } };
IEnumerable<Foo> foos = bars.Select(o => new Foo().InjectFrom(o)).Cast<Foo>();
IEnumerable<Foo> foos2 = from bar in bars
select new Foo().InjectFrom(bar) as Foo;
Assert.AreEqual(bars.First().Name, foos.First().Name);
Assert.AreEqual(bars.First().Name, foos2.First().Name);
}
Whilst that might be possible, if there's any complexity in u then I think it's a bad idea.
At some point the ORM you're using (Linq-to-SQL? EF?) needs to switch from executing on the database to executing in .NET. At that boundary it needs to work out what data it needs from the database. In the first example, that's completely clear: it only needs u.id. In the second it has no idea: it doesn't know what properties InjectFrom will read from it, so it will need to load all the values from the UserCategories table, and maybe related objects too, just in case.