Entity framework - Return to list error? - vb.net

I got the problem - return to list from the entity framework. I need to return as a object. Here is my code:
Public Function GetHardwareDetail() As List(Of HardwareDetailApp)
Dim idList As New List(Of String)
Dim Data = (From p In DB.TT_HARDWARE Select New HardwareDetailApp With {.InternalNum = p.INTERNAL_NUM, .Description = p.DESCRIPTION, .TerminalModel = p.HARDWARE_MODEL, .HardwareInternalNum = p.HARDWARE_ID, .Status = p.ISACTIVE, .Firmware = Nothing, .SerialNum = Nothing})
If Data.Count > 0 Then
For Each row In Data
idList.Add(row.InternalNum)
Next
End If
Dim Data2 = (From p In DB.TT_TERMINAL_HARDWARE Where idList.Contains(p.HARDWARE_INTERNAL_NUM)
Select New HardwareDetailApp With
{.Firmware = p.HARDWARE_FIRMWARE, .SerialNum = p.HARDWARE_SERIAL_NUM, .InternalNum = Data.FirstOrDefault.InternalNum, .Description = Data.FirstOrDefault.Description, .TerminalModel = Data.FirstOrDefault.TerminalModel, .HardwareInternalNum = Data.FirstOrDefault.HardwareInternalNum, .Status = Data.FirstOrDefault.Status})
Return Data2.ToList
End Function
This is the error which I get:
The type 'HardwareDetailApp' appears in two
structurally incompatible initializations within a single LINQ to
Entities query. A type can be initialized in two places in the same
query, but only if the same properties are set in both places and
those properties are set in the same order.

in your code, you have created from HardwareDetailApp in two place, in every creation of that you must set the same property with same order.
for example if in linq to entity you Select something like:
Place1:
...
Select new MyClass()
{
PropA: 1,
}
...
and in that query you need to another Select from MyClass but with some other properties like PropB, Like:
Place2:
...
Select new MyClass()
{
PropB: 2,
}
...
you must change all Select from MyClass into same, and set the properties you dont need to them, to its default, and set the properties in same order like:
Place1:
...
Select new MyClass()
{
PropA: 1,
PropB: default(int),
}
...
and
Place2:
...
Select new MyClass()
{
PropA: default(int),
PropB: 2,
}
...
my codes are in c#..
in this part of your code Dim Data = (From p In DB.TT_HARDW .... try to set Firmware and SerialNum at first like the second select, (i have not checked other properties carefully)

Related

Return type of Linq on Datatable

I have a datatable with two columns ID & Role.
Same ID can have multiple roles.
I need to convert this table to a comma separated grouped table.
I am trying to use following query but unable to solve the issue.
LINQ:
From row As DataRow In dtData.Rows.Cast(Of DataRow)
Group row By id = row.Field(Of Integer)("ID") Into Group
Select ID, Role = String.Join(",", From i In Group Select i.Field(Of String)("Role"))
Issue
Any help will be appreciated.
Update 1:
Table structure
Needed table Structure
You could create a linq like in your comments just that this returns a list of arrays of string:
Here is the code:
(From row As DataRow In myDatatable
Group row By id = row.Field(Of String)("ID") Into Group
Select {id, String.Join(",", From i In Group Select i.Field(Of String)("Role"))}).ToList
If you need the result in a datatable you can build a new datatable
Make a for each of result and use the activity Add data row. In ArrayRow add the item and in DataTable the new data table
If you use the activity Output data table you can see the results
I am kind of confused by what you are wanting as the ultimate outcome. An idea that may guide you but not be exactly what you want is you can change a DataTable to an anonymous projection and then get what you want out of that. You can do a 'Select' off a DataTable which enters into an extension method of 'what' do you want to select. If I was to do a new {} without any class or container object after the 'new' I would be scoped to just a method or not. This is a good advantage when you want to mold something for a specific use in just a single method to use tailored to a specific view.
static void Main(string[] args)
{
DataTable d = new DataTable();
d.Columns.Add("ItemName", typeof(string));
d.Columns.Add("MinValue", typeof(float));
d.Columns.Add("MaxValue", typeof(float));
d.Rows.Add("Widget1", 0.1, 0.2);
d.Rows.Add("Widget2", 0.2, 0.4);
d.Rows.Add("Widget3", 0.1, 0.2);
var dataTable = d.AsEnumerable();
//What do you want to select? The new {} without an indicator means anonymous type projection. This will exist only in
// the scope listed.
var data = dataTable.Select(x => new { ItemName = x[0], MinValue = x[1], MaxValue = x[2] }).ToList();
//My 'data' type is now well typed for it's properties in the scope it's in.
var joined = String.Join(", ", data.Select(x => x.ItemName).ToList());
Console.WriteLine(joined);
Console.WriteLine($"{data.Count}");
Console.ReadLine();
}
EDIT 1-26-18
Strange I thought I updated the code yesterday. To get a reusable object you could bind your front end to, you just make a POCO like so:
public class Foo
{
public string Bar { get; set; }
public string MinAndMax { get; set; }
}
While you could make another DataTable, frankly DataTables are like WinForms. They get the job done, but they are archaic and not friendly to do Linq with as easily as just a well formed POCO. And if you get into using Linq it will play better with well formed objects and they are easy to create.
var data = dataTable.Select(x => new Foo { Bar = x[0].ToString(), MinAndMax = $"{x[1]} {x[2]}" }).ToList();
//My 'data' type is now well typed for 'Foo' class and it's properties in the scope it's in.
var joined = String.Join(", ", data.Select(x => $"{x.Bar} {x.MinAndMax}").ToList());

DataContext.ExecuteQuery<object> returns object {}

I'm trying to write function for selecting optional columns in linq(columns that may not exist). The problem is in linq like this:
using (DataDataContext db = new DataDataContext()){
var collection = from t in table
select new
{
Nonoptional = t.A;
Optional = IsInDB("table","B") ? t.B : -1; //this is optional column
}}
Unfortunately, this won't work because the fragment near Optional will be translated to case statement and error arises that column not exists.
So i decided to "cover" it with function:
using (DataDataContext db = new DataDataContext()){
var collection = from t in table
select new
{
Nonoptional = t.A;
Optional = IsInDB("table","B") ? OptionalColumnValue<int>("table","B","id_table",t.id_table) : -1; //this is optional column
}}
I want this function to be universal. It should work like that" If there is no value or column is nullable and value is null then return default value for type.
I came up with something like this:
//table,column - obvious,id_column - PK column of table, id - id of currently processing record
public static T OptionalColumnValue<T>(string table,string column,string id_columm,int id) T t = default(T);
DataDataContext db = new DataDataContext();
IEnumerable<object> value = db.ExecuteQuery<object>("select " + column + " from " + table + " where " + id_columm + " = " + id.ToString());
List<object> valueList = value.ToList();
if (valueList.Count == 1)//here is the problem
t = (T)valueList.First();
return t;
}
When there is null value db.ExecuteQuery return something like object{}. I'm assuming this is "empty" object,with nothing really in there. I was thinking about checking for "emptiness" of this object( BTW this is not DBull).
When i realised that this is no way either with concrete value in this column(it cannot cast it to return correct type), then I tried db.ExecuteQuery<T>. Then concrete value - OK, null - Exception.
I thought, maybe Nullable<T> as return value. Nop, because string also can be T.
I don't know what to do next. Maybe there's another solution to this problem.

When getting types from an assembly, is there a way to determine if type is an Anonymous Type?

I added an Anonymous type to my project:
'Put the courses from the XML file in an Anonymous Type
Dim courses = From myCourse In xDoc.Descendants("Course")
Select New With
{
.StateCode = myCourse.Element("StateCode").Value, _
.Description = myCourse.Element("Description").Value, _
.ShortName = myCourse.Element("ShortName").Value, _
.LongName = myCourse.Element("LongName").Value, _
.Type = myCourse.Element("Type").Value, _
.CipCode = CType(myCourse.Element("CIPCode"), String) _
}
For Each course In courses
If Not UpdateSDECourseCode(acadYear, course.StateCode, course.Description, course.Type, course.ShortName, course.LongName, course.CipCode) Then errors.Add(String.Format("Cannot import State Course Number {0} with Year {1} ", course.StateCode, acadYear))
Next
After doing so, a Unit Test failed:
Public Function GetAreaTypeList() As List(Of Type)
Dim types As New List(Of Type)
Dim asmPath As String = IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "My.Stuff.dll")
For Each t As Type In Reflection.Assembly.LoadFrom(asmPath).GetTypes()
If t.Namespace.StartsWith("My.Stuff.BatchUpdater") Then
If t.BaseType Is GetType(My.Stuff.BatchUpdater.Area) Then
types.Add(t)
End If
End If
Next
Return types
End Function
It fails because a new type has been added to the project (VB$AnonymousType_0`6) and it does not have a property called Namespace.
I fixed by making the following change to the IF Statement:
If Not t.Namespace Is Nothing AndAlso t.Namespace.StartsWith("My.Stuff.BatchUpdater") Then
Since I don't fully understand what's happening, I feel leery about my code change.
Why is the Namespace Nothing for the Anonymous type?
Would you fix your Unit Test in the same fashion? Or should it be something more specific (e.g. If Not t.Names = "VB$AnonymousType_0`6")
UPDATE
decyclone gave me the info I needed to create a better test:
For Each t As Type In Reflection.Assembly.LoadFrom(asmPath).GetTypes()
'Ignore CompilerGeneratedAttributes (e.g. Anonymous Types)
Dim isCompilerGeneratedAttribute = t.GetCustomAttributes(False).Contains(New System.Runtime.CompilerServices.CompilerGeneratedAttribute())
If Not isCompilerGeneratedAttribute AndAlso t.Namespace.StartsWith("My.Stuff.BatchUpdater") Then
'...Do some things here
End If
Next
Honestly, it could be improved more with a LINQ query, but this suits me.
Anonymous methods and types are decorated with CompilerGeneratedAttribute. You can check for their existance to identify an anonymous type.
var anonymous = new { value = 1 };
Type anonymousType = anonymous.GetType();
var attributes = anonymousType.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false);
if (attributes.Any())
{
// Anonymous
}
You can filter these out in your test.
It is also possible to mark a user defined type with CompilerGeneratedAttribute. So maybe you can combine it with checking if Namespace is null or not
var anonymous = new { value = 1 };
Type anonymousType = anonymous.GetType();
var attributes = anonymousType.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false);
if (attributes.Any() && anonymousType.Namespace == null)
{
// Anonymous
}

How do I return the column names of a LINQ entity

I am using LINQ to SQL queries to return data in my application. However I find it is now needful for me to return the column Names. Try as I might I have been completely unable to find out how to do this on the internet.
So if my LINQ entity table has the properties (Last_Name, First_name, Middle_Name) I need to return:
Last_name
First_Name
Middle_name
rather than the usual
Smith
John
Joe
You could certainly do it with some LINQ-To-Xml directly against the ".edmx" file or the embedded model resources in the compiled assembly.
The below query gets the field (not column) names. If you need the columns then just change the query to suit.
var edmxNS = XNamespace.Get(#"http://schemas.microsoft.com/ado/2007/06/edmx");
var schemaNS = XNamespace.Get(#"http://schemas.microsoft.com/ado/2006/04/edm");
var xd = XDocument.Load(#"{path}\Model.edmx");
var fields =
from e in xd
.Elements(edmxNS + "Edmx")
.Elements(edmxNS + "Runtime")
.Elements(edmxNS + "ConceptualModels")
.Elements(schemaNS + "Schema")
.Elements(schemaNS + "EntityType")
from p in e
.Elements(schemaNS + "Property")
select new
{
Entity = e.Attribute("Name").Value,
Member = p.Attribute("Name").Value,
Type = p.Attribute("Type").Value,
Nullable = bool.Parse(p.Attribute("Nullable").Value),
};
Lets assume you're talking about the Contact Table in the assembly named YourAssembly in a Context called MyDataContext
Using Reflection against a Table
You can use reflection to get the properties like you would any type
var properties = from property in
Type.GetType("YourAssembly.Contact").GetProperties()
select property.Name
;
foreach (var property in properties)
Console.WriteLine(property);
As shaunmartin notes this will return all properties not just Column Mapped ones. It should also be noted that this will return Public properties only. You'd need to include a BindingFlags value for the bindingAttr Parameter of GetProperties to get non-public properties
Using the Meta Model
You can use the Meta Model System.Data.Linq.Mapping to get the fields ( I added IsPersistant to only get the Column Mapped properties)
AttributeMappingSource mappping = new System.Data.Linq.Mapping.AttributeMappingSource();
var model = mappping.GetModel(typeof (MyDataContext));
var table = model.GetTable(typeof (Contact));
var qFields= from fields in table.RowType.DataMembers
where fields.IsPersistent == true
select fields;
foreach (var field in qFields)
Console.WriteLine(field.Name);
Using Reflection from a query result
If on the other hand you wanted it from a query result you can still use reflection.
MyDataContextdc = new MyDataContext();
Table<Contact> contacts = dc.GetTable<Contact>();
var q = from c in contacts
select new
{
c.FirstName,
c.LastName
};
var columns = q.First();
var properties = (from property in columns.GetType().GetProperties()
select property.Name).ToList();
I stumbled upon this answer to solve my own problem and used Conrad Frix 's answer. The question specified VB.NET though and that is what I program in. Here are Conrad's answers in VB.NET (they may not be a perfect translation, but they work):
Example 1
Dim PropertyNames1 = From Prprt In Type.GetType("LocalDB.tlbMeter").GetProperties()
Select Prprt.Name
Example 2
Dim LocalDB2 As New LocalDBDataContext
Dim bsmappping As New System.Data.Linq.Mapping.AttributeMappingSource()
Dim bsmodel = bsmappping.GetModel(LocalDB2.GetType())
Dim bstable = bsmodel.GetTable(LocalDB.tblMeters.GetType())
Dim PropertyNames2 As IQueryable(Of String) = From fields In bstable.RowType.DataMembers
Where fields.IsPersistent = True
Select fields.Member.Name 'IsPersistant to only get the Column Mapped properties
Example 3
Dim LocalDB3 As New LocalDBDataContext
Dim qMeters = From mtr In LocalDB3.tblMeters
Select mtr
Dim FirstResult As tblMeter = qMeters.First()
Dim PropertyNames3 As List(Of String) = From FN In FirstResult.GetType().GetProperties()
Select FN.Name.ToList()
To display the results:
For Each FieldName In PropertyNames1
Console.WriteLine(FieldName)
Next
For Each FieldName In PropertyNames2
Console.WriteLine(FieldName)
Next
For Each FieldName In PropertyNames3
Console.WriteLine(FieldName)
Next
Please also read Conrad's answer for notes on each method!

Cannot add an entity that already exists. (LINQ to SQL)

in my database there are 3 tables
CustomerType
CusID
EventType
EventTypeID
CustomerEventType
CusID
EventTypeID
alt text http://img706.imageshack.us/img706/8806/inserevent.jpg
Dim db = new CustomerEventDataContext
Dim newEvent = new EventType
newEvent.EventTypeID = txtEventID.text
db.EventType.InsertOnSubmit(newEvent)
db.SubmitChanges()
'To select the last ID of event'
Dim lastEventID = (from e in db.EventType Select e.EventTypeID Order By EventTypeID Descending).first()
Dim chkbx As CheckBoxList = CType(form1.FindControl("CheckBoxList1"), CheckBoxList)
Dim newCustomerEventType = New CustomerEventType
Dim i As Integer
For i = 0 To chkbx.Items.Count - 1 Step i + 1
If (chkbx.Items(i).Selected) Then
newCustomerEventType.INTEVENTTYPEID = lastEventID
newCustomerEventType.INTSTUDENTTYPEID = chkbxStudentType.Items(i).Value
db.CustomerEventType.InsertOnSubmit(newCustomerEventType)
db.SubmitChanges()
End If
Next
It works fine when I checked only 1 Single ID of CustomerEventType from CheckBoxList1. It inserts data into EventType with ID 1 and CustomerEventType ID 1. However, when I checked both of them, the error message said
Cannot add an entity that already exists.
Any suggestions please? Thx in advance.
Did you change the EventID before you pressed the button again. To me it looks as you did not. This would result that the code tries to insert the event with the ID 1 into the database, although, it is already there.
Maybe try increasing the event ID automatically or check whether the event is alreay present before trying to insert it.
Edit:
OK, here is what I think you want to do ... if I understood it correctly (it's in C# as I am more fluent in that language - however you should be able to easily convert the algorithm to VB - so just take it as pseudo code):
var db = new CustomerEventDataContext();
var newEvent = db.EventTypeSingleOrDefault(x => x.EventTypeId == txtEventID.Text);
if (newEvent != null) {
newEvent = new EventType();
newEvent.EventTypeId = txtEventID.Text;
db.EventType.InsertOnSubmit();
}
var chkbx = (CheckBoxList) form1.FindControl("CheckBoxList1");
for (int i = 0; i < chkbx.Items.Count; i++) {
var value = chkbxStudentType.Items(i).Value;
if (db.CustomerEventTypes.SingleOrDefault(x => x.EventTypeId == newEvent.EventTypeId) != null) {
// item already exists
} else {
var newCustomerEventType = new CustomerEventType();
newCustomerEventType.INTEVENTTYPEID = newEvent.EventTypeId;
newCustomerEventType.INTSTUDENTTYPEID = value;
db.CustomerEventType.InsertOnSubmit(newCustomerEventType);
}
}
db.SubmitChanges();
Two things I noticed:
You were adding the new EventType and then selecting the last event type based upon the id. This may not result in the item you just added.
You do not have to call SubmitChanges after each InsertOnSubmit. The DataBaseContext implementaton holds the inserted objects for you and you can reference them. Then you do a single submit to commit all changes. Note, however, in some complex circumstances a separate SubmitChanges is necessary, but this is rarely the case.