Get columns name from a table in database - c#

in my database i have many of tables one of Tables is nambed Company". and its columns names are as follows ("ComName","ComAddress","ComType" ..etc)
I have a function which get the columns name from Company table :
public List<string> GetColumnNames(string tablename)
{
var names = typeof(Company).GetProperties().Select(p => p.Name).ToArray();
return names.ToList();
}
Until now everything is good but what if i want to get the columns of other tables ? so i have to put the name of the new table (that is given in the function as "tablename") instead of "Company" in this sentence :
var names = typeof(Company).GetProperties().Select(p => p.Name).ToArray();
but it is string so its not working. any idea ?

typeof(Company).GetProperties() isn't fetching the names of the columns of your table at all though - it's fetching the names of the properties of the C# class/entity that you're using to represent that table. Do you already have a process to guarantee you have C# entities with property names that exactly match the column names of your tables? If so, you can do the same principle (but instead of using typeof(Company) you'd need to find the type by name, which there are various ways of doing). If not, you'll need to to actually query the database to find the column names.

As far as i know, you can use the Type parameter.
public List<string> GetColumnNames(Type tablename)
{
var names = tablename.GetProperties().Select(p => p.Name).ToArray();
return names.ToList();
}

Related

Get column by table name entity framework reflection

I need to get column name and value from a table which I know only the name.
I try lot of stuff but without success.
I use this for get my table :
var table = oSession.ctx.GetType()
.GetProperty("country")
.GetValue(oSession.ctx, null);
I'm lock with that I can't retrieve the informations of my columns ..
Perhaps I have already try this :
List<string> columnsNames = table.GetType().GetFields().Select(field => field.Name).ToList();
Thx for your help..
Use ((IQueryable)table).ElementType.GetProperties() as a starting point. Also you can get some ideas from Dynamic LINQ query to get Field value from Database
you can probably try using GetProperties()
table.GetType().GetProperties().Where(p => !(p.GetMethod.IsVirtual || p.GetIndexParameters().Length > 0)).ToList()

Mapping a view and a table to the same model in EF?

My Scenario
I have a table of items, each of which has a priority and some other information.
I also have a database view, which selects * from that table, but replaces the priorities based on some other attributes of the item
Both the view and the table contain exactly the same fields, with only the content of the priorities changing.
In my code -- names changed to protect the innocent :) -- I have:
[Table("schema.Items")] //The table
public class Item
{
//...all of the fields that exist in both the table and the view.
}
Question
Given this scenario, is there a way for me to pull from the view instead of the table but map it to the same model naturally? If so, how do I do that?
Clarifications:
The "view" I mention is a database view that reads from the table but interprets the priority differently.
So, in this situation, we'd like to just pull from the view.
The database view and database table have the same fields.
I'm trying to find a way to avoid having ClassA and ClassB, both with the same properties, just so I can pull one from the view and the other from the table.
I am referring only to reading data. There is no expectation of an update here.
If I understand you correctly, you can use SqlQuery to load from a query that is generated from view.
var db = ...; // instance of context
var q = db.Set<ViewModel>().Where(...).ToString();
var result = db.Database.SqlQuery<TableModel>(q);
PS
If the view name has a pattern like view_TableName, you can just use TableModel to generate the query then replace the table name with view name.
Here is an extension method that could achieve that.
public static string GetViewSql<T>(this DbContext db, IQueryable<T> q)
where T : class
{
const string prefix = "view_";
var tableName = Regex.Match(
db.Set<T>().ToString(),
#"FROM (\[.*\]\.\[.*\]) AS \[Extent1\]").Groups[1].Value;
var viewName = Regex.Replace(
tableName,
#"\[.*\]\.\[(.*)\]",
m => m.Groups[0].Value.Replace(
m.Groups[1].Value, prefix + m.Groups[1].Value));
var sql = q.ToString().Replace(tableName, viewName);
return sql;
}
Usage:
var query = db.Set<TableModel>().Where(...);
var sql = db.GetViewSql(query);
var result = db.Database.SqlQuery<TableModel>(sql);

Simple.Data - how to bi-drectionally join on the same table twice

I know I'm close with this one...
My structure is:
I have some companies
Each company has a primary user (account)
And a list of all users (accounts).
I've implemented this in the db as a companies table that has "Primary" as a foreign key to the Accounts table, and the Accounts table has a CompanyId which is a foreign key to the Companies table.
So there's only ever one primary user, and each user is associated with one company.
I want to retrieve a list of all companies, and plonk them in a c# object. What I have so far is:
public IEnumerable<Company> GetAllCompaniesList()
{
var allData = database.Companies.All()
.Join(database.Accounts).On(database.Accounts.Id == database.Companies.Primary)
.Join(database.Accounts).On(database.Accounts.CompanyId == database.Companies.Id)
.ToList<Company>();
return allData;
}
and it works in my tests using an in-memory adapter with the relevant keys set up, but not in the real version, crashing out with
The objects "dbo.Accounts" and "dbo.Accounts" in the FROM clause have
the same exposed names. Use correlation names to distinguish them.
I think this means I need to name each join (e.g. to make sql like "join a on a = b as c"), but I can't find the syntax to do that. Anyone know?
You can alias table names using the .As method. In addition to that, the Join method has an optional second out parameter which puts the reference to the aliased table in a dynamic variable; it just makes referring to the table easier.
So try this:
public IEnumerable<Company> GetAllCompaniesList()
{
dynamic primary;
var allData = database.Companies.All()
.Join(database.Accounts.As("PrimaryAccounts"), out primary)
.On(primary.Id == database.Companies.Primary)
.Join(database.Accounts)
.On(database.Accounts.CompanyId == database.Companies.Id)
.ToList<Company>();
return allData;
}
That should work fine.

How to specify/filter which table columns should be populated when working with LINQ to SQL?

I'm using LINQ to SQL and have a database table called Product with 20 columns. The Product table is mapped to the Product class in the LINQ to SQL metadata.
I'd like to use my dbContext and retrieve some product records but only populating 10 columns not all 20 columns.
How would that be possible to specify which columns should be returned/populated with LINQ to SQL (or EF)?
I know one way would be using stored procedures but that's this question is about.
Thanks,
You usually use an anonymous class for that:
db.Products.Where(... filter ...).Select(item => new
{
Field1 = item.Field1,
Field2 = item.Field2,
});
Only the fields you include will be selected. If you intend to pass this data structure to other functions or return it, you need a concrete class definition for sub field set, such as:
class SmallerEntity
{
public something Field1;
public something Field2;
}
And you can initialize this in your Select statement:
db.Products.Where(... filter ...).Select(item => new SmallerEntity
{
Field1 = item.Field1,
Field2 = item.Field2,
});
I don't recommend the practice of half-populating an existing class. That makes your state space unnecessarily complex and allows more bugs in your code. Try to contain smaller subsets of data in their own classes.

Retrieve an object from entityframework without ONE field

I'm using entity framework to connect with the database. I've one little problem:
I've one table which have one varbinary(MAX) column(with filestream).
I'm using SQL request to manage the "Data" part, but EF for the rest(metadata of the file).
I've one code which has to get all files id, filename, guid, modification date, ... of a file. This doesn't need at all the "Data" field.
Is there a way to retrieve a List but without this column filled?
Something like
context.Files.Where(f=>f.xyz).Exclude(f=>f.Data).ToList();
??
I know I can create anonymous objects, but I need to transmit the result to a method, so no anonymous methods. And I don't want to put this in a list of anonymous type, and then create a list of my non-anonymous type(File).
The goal is to avoid this:
using(RsSolutionsEntities context = new RsSolutionsEntities())
{
var file = context.Files
.Where(f => f.Id == idFile)
.Select(f => new {
f.Id, f.MimeType, f.Size, f.FileName, f.DataType,
f.DateModification, f.FileId
}).FirstOrDefault();
return new File() {
DataType = file.DataType, DateModification = file.DateModification,
FileId = file.FileId, FileName = file.FileName, Id = file.Id,
MimeType = file.MimeType, Size = file.Size
};
}
(I'm using here the anonymous type because otherwise you will get a NotSupportedException: The entity or complex type 'ProjectName.File' cannot be constructed in a LINQ to Entities query.)
(e.g. this code throw the previous exception:
File file2 = context.Files.Where(f => f.Id == idFile)
.Select(f => new File() {Id = f.Id, DataType = f.DataType}).FirstOrDefault();
and "File" is the type I get with a context.Files.ToList(). This is the good class:
using File = MyProjectNamespace.Common.Data.DataModel.File;
File is a known class of my EF datacontext:
public ObjectSet<File> Files
{
get { return _files ?? (_files = CreateObjectSet<File>("Files")); }
}
private ObjectSet<File> _files;
Is there a way to retrieve a List but without this column filled?
Not without projection which you want to avoid. If the column is mapped it is natural part of your entity. Entity without this column is not complete - it is different data set = projection.
I'm using here the anonymous type because otherwise you will get a
NotSupportedException: The entity or complex type 'ProjectName.File'
cannot be constructed in a LINQ to Entities query.
As exception says you cannot project to mapped entity. I mentioned reason above - projection make different data set and EF don't like "partial entities".
Error 16 Error 3023: Problem in mapping fragments starting at line
2717:Column Files.Data in table Files must be mapped: It has no
default value and is not nullable.
It is not enough to delete property from designer. You must open EDMX as XML and delete column from SSDL as well which will make your model very fragile (each update from database will put your column back). If you don't want to map the column you should use database view without the column and map the view instead of the table but you will not be able to insert data.
As a workaround to all your problems use table splitting and separate the problematic binary column to another entity with 1 : 1 relation to your main File entity.
I'd do something like this:
var result = from thing in dbContext.Things
select new Thing {
PropertyA = thing.PropertyA,
Another = thing.Another
// and so on, skipping the VarBinary(MAX) property
};
Where Thing is your entity that EF knows how to materialize. The resulting SQL statement shouldn't include the large column in its result set, since it's not needed in the query.
EDIT: From your edits, you get the error NotSupportedException: The entity or complex type 'ProjectName.File' cannot be constructed in a LINQ to Entities query. because you haven't mapped that class as an entity. You can't include objects in LINQ to Entities queries that EF doesn't know about and expect it to generate appropriate SQL statements.
You can map another type that excludes the VarBinary(MAX) column in its definition or use the code above.
you can do this:
var files = dbContext.Database.SqlQuery<File>("select FileId, DataType, MimeType from Files");
or this:
var files = objectContext.ExecuteStoreQuery<File>("select FileId, DataType, MimeType from Files");
depending on your version of EF
I had this requirement because I have a Document entity which has a Content field with the content of the file, i.e. some 100MB in size, and I have a Search function that I wanted to return the rest of the columns.
I chose to use projection:
IQueryable<Document> results = dbContext.Documents.Include(o => o.UploadedBy).Select(o => new {
Content = (string)null,
ContentType = o.ContentType,
DocumentTypeId = o.DocumentTypeId,
FileName = o.FileName,
Id = o.Id,
// etc. even with related entities here like:
UploadedBy = o.UploadedBy
});
Then my WebApi controller passes this results object to a common Pagination function, which applies a .Skip, .Take and a .ToList.
This means that when the query gets executed, it doesn't access the Content column, so the 100MB data is not being touched, and the query is as fast as you'd want/expect it to be.
Next, I cast it back to my DTO class, which in this case is pretty much exactly the same as the entity class, so this might not be a step you need to implement, but it's follows my typical WebApi coding pattern, so:
var dtos = paginated.Select(o => new DocumentDTO
{
Content = o.Content,
ContentType = o.ContentType,
DocumentTypeId = o.DocumentTypeId,
FileName = o.FileName,
Id = o.Id,
UploadedBy = o.UploadedBy == null ? null : ModelFactory.Create(o.UploadedBy)
});
Then I return the DTO list:
return Ok(dtos);
So it uses projection, which might not fit the original poster's requirements, but if you're using DTO classes, you're converting anyway. You could just as easily do the following to return them as your actual entities:
var dtos = paginated.Select(o => new Document
{
Content = o.Content,
ContentType = o.ContentType,
DocumentTypeId = o.DocumentTypeId,
//...
Just a few extra steps but this is working nicely for me.
For EF Core 2
I implemented a solution like this:
var files = context.Files.AsNoTracking()
.IgnoreProperty(f => f.Report)
.ToList();
The base idea is to turn for example this query:
SELECT [f].[Id], [f].[Report], [f].[CreationDate]
FROM [File] AS [f]
into this:
SELECT [f].[Id], '' as [Report], [f].[CreationDate]
FROM [File] AS [f]
you can see the full source code in here:
https://github.com/aspnet/EntityFrameworkCore/issues/1387#issuecomment-495630292
I'd like to share my attempts to workaround this problem in case somebody else is in the same situation.
I started with what Jeremy Danyow suggested, which to me is the less painful option.
// You need to include all fields in the query, just make null the ones you don't want.
var results = context.Database.SqlQuery<myEntity>("SELECT Field1, Field2, Field3, HugeField4 = NULL, Field5 FROM TableName");
In my case, I needed a IQueryable<> result object so I added AsQueryable() at the end. This of course let me add calls to .Where, .Take, and the other commands we all know, and they worked fine. But there's a caveat:
The normal code (basically context.myEntity.AsQueryable()) returned a System.Data.Entity.DbSet<Data.DataModel.myEntity>, while this approach returned System.Linq.EnumerableQuery<Data.DataModel.myEntity>.
Apparently this means that my custom query gets executed "as is" as soon as needed and the filtering I added later is done afterwards and not in the database.
Therefore I tried to mimic Entity Framework's object by using the exact query EF creates, even with those [Extent1] aliases, but it didn't work. When analyzing the resulting object, its query ended like
FROM [dbo].[TableName] AS [Extent1].Where(c => ...
instead of the expected
FROM [dbo].[TableName] AS [Extent1] WHERE ([Extent1]...
Anyway, this works, and as long as the table is not huge, this method will be fast enough. Otherwise you have no option than to manually add the conditions by concatenating strings, like classic dynamic SQL. A very basic example in case you don't know what I'm talking about:
string query = "SELECT Field1, Field2, Field3, HugeField4 = NULL, Field5 FROM TableName";
if (parameterId.HasValue)
query += " WHERE Field1 = " + parameterId.Value.ToString();
var results = context.Database.SqlQuery<myEntity>(query);
In case your method sometimes needs this field, you can add a bool parameter and then do something like this:
IQueryable<myEntity> results;
if (excludeBigData)
results = context.Database.SqlQuery<myEntity>("SELECT Field1, Field2, Field3, HugeField4 = NULL, Field5 FROM TableName").AsQueryable();
else
results = context.myEntity.AsQueryable();
If anyone manages to make the Linq extensions work properly like if it was the original EF object, please comment so I can update the answer.
I'm using here the anonymous type because otherwise you will get a
NotSupportedException: The entity or complex type 'ProjectName.File'
cannot be constructed in a LINQ to Entities query.
var file = context.Files
.Where(f => f.Id == idFile)
.FirstOrDefault() // You need to exeucte the query if you want to reuse the type
.Select(f => new {
f.Id, f.MimeType, f.Size, f.FileName, f.DataType,
f.DateModification, f.FileId
}).FirstOrDefault();
And also its not a bad practice to de-normalize the table into further, i.e one with metadata and one with payload to avoid projection. Projection would work, the only issue is, need to edit any time a new column is added to the table.
I tried this:
From the edmx diagram (EF 6), I clicked the column I wanted to hide from EF and on their properties you can set their getter and setter to private. That way, for me it works.
I return some data which includes a User reference, so I wanted to hide the Password field even though it's encrypted and salted, I just didn't want it on my json, and I didn't want to do a:
Select(col => new {})
because that's a pain to create and maintain, especially for big tables with a lot of relationships.
The downside with this method is that if you ever regenerate your model, you would need to modify their getter and setter again.
Using Entity Framework Power Tools you can do the following in efpt.config.json:
"Tables": [
{
"ExcludedColumns": [
"FileData"
],
"Name": "[dbo].[Attachment]",
"ObjectType": 0
}
]

Categories

Resources