Read object from c# database into classes directly - c#

What I am trying to do is read a database, row by row, and use the data from each row to initialize an object of the type that data represents. In this case I am reading rows of the Device table and trying to create Device objects with that data. I saw this SO link:
and I tried this snippet:
using(var dc = new DataContext(connectionString))
{
List<Person> people = dc.ExecuteQuery(#"
SELECT Id, Name Address
FROM [People]
WHERE [Name] = {0}", name).ToList(); // some LINQ too
}
But it is telling me
The type arguments for this usage cannot be inferred from the usage
Is this in principal correct or should I be using the BondIO serializer/deserializer? as mentioned here
Also the order of the members in the object may not be the same as the order of the columns in the database, is this relevant?
Later that same day....
I now have a DBContext with all my database objects defined like this:
public class MyContext : DBContext
{
public dbSet<Device>{ get; set;}
etc...
}
And I now try to get object using this snippet:
using (var db = new MyContext(ConnectionString))
{
var res = db.Device.Find(ID);
}
However this gives an exception message
Could not load type 'System.Data.Entity.ModelConfiguration.Conventions.AttributeToColumnAnnotationConvention`2
I have checked the database and it should return 1 value based on the PrimaryKey ID that I am passing. Anybody have any hints what I'm still doing wrong.

You cannot, because ExecuteQuery is for executing statements, not for querying database. You should use SqlQuery instead
What you can do is, to create a new class with the properties you want to set in your query, means a simplified version of your query. In your case
public class Device
{
public int Id {get;set}
public string Name {get;set}
public string Address {get;set}
}
then use it as
var people = dc.ExecuteQuery<Device>(#"
SELECT Id, Name Address
FROM [People]
WHERE [Name] = {0}", name).ToList();

Related

Insert entity with one related entity which existed in the database

I am stuck at the operation when using Entity Framework Core 2 to perform an insert of a new entity with one related entity which already existed in the database. For example, I have two objects with one to many relationship:
public Class OrderDetail
{
public int ParentID {get;set;}
public string OrderDetailName {get;set;}
public int ProductID {get;set;}
public virtual Product ProductFK {get;set;}
}
public Class Product
{
public int ProductID {get;set;}
public string ProductName {get;set;}
public virtual Collection<OrderDetail> OrderDetails {get;set;} = new Collection<OrderDetail>();
}
I would like to add a new OrderDetail with an existing Product (with productID = 3) into the database, so I perform like:
private void AddOrderDetail(int productID)
{
OrderDetail newOrderDetail = new OrderDetail();
newOrderDetail.Name = "New OrderDetail";
// Here I have no clue what would be the correct way...Should I do
// Approach A(pick related entity from database then assign as related entity to base entity):
var ProductFromDB = DbContext.Products.SingleOrDefault(p => p.ProductID == productID);
newOrderDetail.ProductFK = ProductFromDB;
// Approach B(directly assign the foreign key value to base entity)
newOrderDetail.ProductID = productID
DbContext.SaveChange();
}
By using approach (A), my newOrderDetail failed to save and I looked into SQL resource, looks like it considered the one that I retrieved from the database (ProductFromDB) as a new object and was trying to insert it again. I feel it's redundant job for picking ProductFromDB first then assign to the inserted entity...
By using approach (B), my newOrderDetail still failed to save, and I am getting an exception like "insert A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.", however, this exception does not happen constantly. I looked into the SQL and found the SQL Script, by running it individually in SQL Server it worked, however when running an application side, it's not working...
So what would be the correct way to deal with above scenario?
If you don't need access to the complete Product object right away, you could try to set just the foreign key column value of your newOrderDetail. Also, you need to ADD your newly created OrderDetail object to the DbContext before saving - something like this:
private void AddOrderDetail(int productID)
{
OrderDetail newOrderDetail = new OrderDetail();
newOrderDetail.Name = "New OrderDetail";
newOrderDetail.ProductID = productID
// OF COURSE, you need to ADD the newly created object to the context!
DbContext.OrderDetails.Add(newOrderDetail);
DbContext.SaveChange();
}
Next time around, when you actually fetch the OrderDetail, it will resolve the linked product and you should be fine.:
using (var DbContext = new YourDbContext())
{
OrderDetail od = DbContext.OrderDetails.FirstOrDefault(x => x.Name = "New OrderDetail");
....
}

EF 6 SaveChanges() fails with Cannot insert the value NULL into column

I have a table that is set to a varchar that cannot be null. Within my code, I am pulling the data from the database, updated the object and then completing a SaveChanges() on the connection.
This gives me the following error:
Cannot insert the value NULL into column 'FileReference', table
'dbo.Files'; column does not allow nulls. INSERT fails. The
statement has been terminated.
FileReference is populated, as I have checked, but within the exception I get:
Exception.Entries.source[0].Entity.FileReference is showing as null.
Confused on this one.
Code as per request
using (var conn = new MagmaContext())
{
var result = from p in conn.Files
where p.fileId == 1
select p;
var rec = result.FirstOrDefault();
// code changed to use correct var
rec.UpdateFileData(model);
conn.SaveChanges();
}
I have an extension class called ObjectTransform.
public static class ObjectTransform
{
public static File UpdateFileData(this File file, valueObject model)
{
file.FileReference = model.File.FileReference;
file.LastUpdate = model.File.LastUpdate;
}
}
And the viewmodel looks like this
public class valueObject
{
public int Id {get;set;}
public string Description {get;set;}
public File File {get;set;}
}
public class File
{
public int Id {get;set;}
public string FileReference {get;set;}
}
Fixed Code as per comment
Thanks to all for all responses, but I came up with the solution. The reason was way simple in the end. All it was, the File object not initialised correctly. Hmm sorry, but worth a meantion anyways for other budding coders.

Pass table name to dbContext and get values from table in Entity Framework

I have a DbContext class and I'm using code first apporach in my application. I have few common standard tables that contains "Id" and "Value" columns and i want to query them passing the table name and column name but there is no option in entity framework to pass.
Example:
Common tables:
Client_ClientDepartment (Id, Value)
Client_ClientDesignation (Id, Value)
Client_ClientCompany (Id, Value)
What I want to do is to pass table name and Id to get the value. I have created a common method as
public string GetClientValue(string id, string tableName)
{
DatabaseContext dbContext = new DatabaseContext();
//Query the database and get value based on table and id.
string value = dbContent. ("query here")
return value ;
}
Can I do it in entity framework? Is it possible?
using ( DatabaseContext dbContext = new DatabaseContext())
{
var blogs = dbContext.Database.SqlQuery<>("query here").ToList();
}
I believe you can run a custom query like this
using (var context = new BloggingContext())
{
var blogNames = context.Database.SqlQuery<string>(
"SELECT Name FROM dbo.Blogs").ToList();
}
Source: https://msdn.microsoft.com/en-us/library/jj592907(v=vs.113).aspx
Sorry I had to answer instead of comment, but don't got the badge yet.
Actually, you normally don't pass table and column names in EF.
You have classes and properties, which become tables and columns in the resulting database.
Your context should look something like this:
public class DatabaseContext : DbContext
{
public DatabaseContext(): base(YourWebConfigConnectionStringName){}
public DbSet<Client_ClientDepartment> ClientDepartment { get; set; }
public DbSet<Client_ClientDesignation> ClientDesignation { get; set; }
With this you are basically registering your "table" classes.
You then address them in code like this:
using (var context=new DatabaseContext())
{
var department = context.ClientDepartment.First(d => d.Id == someIdVariable);
Which is the analogy to a SQL query SELECT TOP 1 department WHERE ID=someId
You can also pass SQL statements as described in the other answers, but that too will only work if you properly registered your classes as DBSets in your DatabaseContext class.
P.S: I left out the Database initializer in the DBContext class, which is something you also need in code first.

EntityFrameWork: Map selection columns to an entity

I have an entity like this
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public int Id { get; set; }
}
And have a stored procedure as
CREATE PROCEDURE [dbo].[GetPersons]
AS
SELECT Id,FirstName FROM [dbo].[Persons]
When I call this stored procedure in My DbContext
var dataResult1 = dbContext.SqlQuery<Person>("[dbo].[GetPersons]");
The data reader is incompatible with the specified '..'. A member of the type, 'LastName', does not have a corresponding column in the data reader with the same name
I know if I define a new entity that has Id, FirstName and map stored procedure result to it everything is worke.
Now is there any way that I map my stored procedure result to Person Entity without define a new entity?
You could have you query look like this:
CREATE PROCEDURE [dbo].[GetPersons]
AS
SELECT Id, FirstName, '' as LastName, 0 as Age FROM [dbo].[Persons]
You aren't pulling them from the DB although they do still go across the network.
However you are now using Person to represent two different things, and this is almost always a bad idea. I think you are better off with two separate objects and maybe create and interface on Id and FirstName if you have code that needs to work with both.
I also wonder what you are doing that pulling the two extra columns has been identified as being a performance bottleneck, what is the difference between pulling and not pulling the columns? Or is it a premature optimization?
You have options (though I don't understand the purpose):
You could simply create a new Entity class that would only map those
2 columns.
You could use dynamic as the type (then you would lose
intellisense on the result set at least).
Instead of an SP you could
create that as an inline table valued function:
CREATE FUNCTION [dbo].[GetPersons] ()
RETURNS TABLE AS RETURN
(
SELECT Id,FirstName FROM [dbo].[Persons]
);
Then your code could simply look like this:
var dataResult1 = dbContext.SqlQuery<Person>(#"Select Id, FirstName,
'' as LastName, 0 as Age
FROM [dbo].[GetPersons]()");
OTOH an SP like this is questionable in the first place.

Linq Select * from Table ExecuteQuery

First let me start by saying that I don't have a complete understanding of Linq. I am trying to dynamically query a database, The first query uses LINQ-SQL which works fine, but the second dynamic call is what fails in run time
public void getTables()
{
foreach (var c in dc.GetTable<TableListing>())
{
List<TableData> res = tableBrowse(c.TableName);
}
}
public List<TableData> tableBrowse(string tablename)
{
string sql = "Select * from " + tablename;
var results = dc.ExecuteQuery<TableData>(sql);
return results.ToList();
}
public class TableData
{
public int Time { get; set; }
public string Value { get; set; }
}
I query the "master table" and this retrieves a list of tables to query. They all have the same structure, as defined in the class TableData. I get a runtime error about Specified cast is not valid. I'm not really looking for code as much as I am looking for what I am doing wrong and how to fix it. Thanks.
You might try decorating your class properties with ColumnAttributes, specifying the column name and type so that LINQ to SQL knows how to do the version of the column data to the properties. You may also need to set other attribute properties to make it work correctly. I would also specify the column names in the SQL instead of using *. Put the column names in the same order as your properties appear in the class as I believe that it processes the result values in the same order as the properties are defined. Not sure it this will work or not, but essentially you're recreating what the designer would do for you.
public List<TableData> tableBrowse(string tablename)
{
string sql = "Select [time], [value] from " + tablename;
var results = dc.ExecuteQuery<TableData>(sql);
return results.ToList();
}
public class TableData
{
[Column( Name="Time", DbType="DateTime NOT NULL", ... )]
public int Time { get; set; }
[Column( Name="Value", DbType="VARCHAR(255) NOT NULL", ... )]
public string Value { get; set; }
}
You aren't explicitly converting the return value from dc.ExecuteQuery<TableData>(sql) to the TableData type that you've defined. I expect that the ExecuteQuery is complaining because it doesn't know what the TableData type is.
The ExecuteQuery helper needs to return a DBML (LINQ-to-SQL generated) type as defined in your database.
But I would suggest that you don't go down this route. If you want to get records from a table, say Customers, just use content.Customers - the point of LINQ-to-SQL is that it already contains all these accessors to save you time.
Actually I found out what the problem was, I was missing a table definition. There was a third data type in one of the tables. Once I defined that table class and checked for the data type it worked fine. Sadly the compiler just didn't give that much information on just what was wrong.

Categories

Resources