Tricks to generate List<double> in entity framework as data from similar set of columns - entity-framework

I am developing an silverlight application using WCF and EF.
I am using Database first as our database already exists.
I have a table that consists of 100 columns with datatype real. We want to generate a class which has a List<double> or List<float> instead of that 100 discrete variables in the class for each column.
Is this possible ?? Can someone give me an example?

There's no direct way. What you have to do is use reflection to convert it into a List<double>. Suppose your table names is called MyObject, then EF will generate a class MyObject to represent a row in that table. You can then do:
Type type = typeof(MyObject);
// Get properties (columns) through reflection
PropertyInfo[] properties = type.GetProperties();
List<List<double>> allRows = new List<List<double>>();
using(var dbContext = MyDB.GetContext())
{
foreach(var row in dbContext.MyRows)
{
List<double> columnValues = new List<double>();
foreach (PropertyInfo property in properties)
{
// The sql type REAL will map to either float or double
if(property.PropertyType == typeof(float))
{
columnValues.Add( (double) property.GetValue(row, null) );
}
}
allRows.Add(columnValues);
}
}
Hope you get the idea.

Related

Assign Mapped Object to Expression Result in LINQ to Entities

I have the following child object that we use an expression to map our 'entity' to our 'domain' model. We use this when specifically calling our ChildRecordService method GetChild or GetChildren:
public static Expression<Func<global::Database.Models.ChildRecord, ChildRecord>> MapChildRecordToCommon = entity => new ChildRecord
{
DateTime = entity.DateTime,
Type = entity.Type,
};
public static async Task<List<ChildRecord>> ToCommonListAsync(this IQueryable<global::Database.Models.ChildRecord> childRecords)
{
var items = await
childRecords.Select(MapChildRecordToCommon).ToListAsync().EscapeContext();
return items;
}
public async Task<List<ChildRecord>> GetChildRecords()
{
using (var uow = this.UnitOfWorkFactory.CreateReadOnly())
{
var childRecords= await uow.GetRepository<IChildRecordRepository>().GetChildRecords().ToCommonListAsync().EscapeContext();
return childRecords;
}
}
So that all works just fine. However we have another object that is a parent to that child, that in SOME cases, we also wish to get the child during the materialisation and mapping process.
In other words the standard object looks as such:
private static Expression<Func<global::Database.Models.Plot, Plot>> MapPlotToCommonBasic = (entity) => new Plot
{
Id = entity.Id,
Direction = entity.Direction,
Utc = entity.Utc,
Velocity = entity.Velocity,
};
However what I also want to map is the Plot.ChildRecord property, using the expression MapChildRecordToCommon I have already created. I made a second expression just to test this:
private static Expression<Func<global::Database.Models.Plot, Plot>> MapPlotToCommonAdvanced = (entity) => new Plot
{
ChildRecord = MapChildRecordToCommon.Compile() (entity.ChildRecord)
};
This fails:
System.NotSupportedException
The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.
Is there a way to reuse my existing expression for ChildRecord, to materialise the object of ChildRecord (ie. one to one/singular not multiple) on the Plot object? I think my trouble is caused by there being just one object and being unable to use the .Select(Map) method. I am not too great at expressions and have hit a wall with this.
For reference, there are actually up to 5 or 6 other child objects on the "Plot" object that I also want to make expressions for.
I resolved this by using the third party library LinqKit.
The library allowed the use of 2 methods, .AsExpandable() (which allows for the expressions to properly compile and be invoked as I understand), and .Invoke() as an extension method to an expression, rather than calling Expression.Invoke(yourexpression). I included a null check just in case.
My code now looks as follows:
public static async Task<List<Plot>> ToCommonListAsync(this IQueryable<global::Database.Models.Plot> plots)
{
var items = await
plots.AsExpandable().Select(MapPlotToCommon).ToListAsync().EscapeContext();
return items;
}
private static Expression<Func<global::Database.Models.Plot, Plot>> MapPlotToCommon = (entity) => new Plot
{
Id = entity.Id,
Direction = entity.Direction,
Utc = entity.Utc,
Velocity = entity.Velocity,
ChildRecord = entity.ChildRecord != null ? MapChildRecordToCommon.Invoke(entity.ChildRecord) : default
};
public static Expression<Func<global::Database.Models.ChildRecord, ChildRecord>> MapChildRecordToCommon = entity => new ChildRecord
{
DateTime = entity.DateTime,
Type = entity.Type,
};

Filehelpers and Entity Framework

I'm using Filehelpers to parse a very wide, fixed format file and want to be able to take the resulting object and load it into a DB using EF. I'm getting a missing key error when I try to load the object into the DB and when I try and add an Id I get a Filehelpers error. So it seems like either fix breaks the other. I know I can map a Filehelpers object to a POCO object and load that but I'm dealing with dozens (sometimes hundreds of columns) so I would rather not have to go through that hassle.
I'm also open to other suggestions for parsing a fixed width file and loading the results into a DB. One option of course is to use an ETL tool but I'd rather do this in code.
Thanks!
This is the FileHelpers class:
public class AccountBalanceDetail
{
[FieldHidden]
public int Id; // Added to try and get EF to work
[FieldFixedLength(1)]
public string RecordNuber;
[FieldFixedLength(3)]
public string Branch;
// Additional fields below
}
And this is the method that's processing the file:
public static bool ProcessFile()
{
var dir = Properties.Settings.Default.DataDirectory;
var engine = new MultiRecordEngine(typeof(AccountBalanceHeader), typeof(AccountBalanceDetail), typeof(AccountBalanceTrailer));
engine.RecordSelector = new RecordTypeSelector(CustomSelector);
var fileName = dir + "\\MOCK_ACCTBAL_L1500.txt";
var res = engine.ReadFile(fileName);
foreach (var rec in res)
{
var type = rec.GetType();
if (type.Name == "AccountBalanceHeader") continue;
if (type.Name == "AccountBalanceTrailer") continue;
var data = rec as AccountBalanceDetail; // Throws an error if AccountBalanceDetail.Id has a getter and setter
using (var ctx = new ApplicationDbContext())
{
// Throws an error if there is no valid Id on AccountBalanceDetail
// EntityType 'AccountBalanceDetail' has no key defined. Define the key for this EntityType.
ctx.AccountBalanceDetails.Add(data);
ctx.SaveChanges();
}
//Console.WriteLine(rec.ToString());
}
return true;
}
Entity Framework needs the key to be a property, not a field, so you could try declaring it instead as:
public int Id {get; set;}
I suspect FileHelpers might well be confused by the autogenerated backing field, so you might need to do it long form in order to be able to mark the backing field with the [FieldHidden] attribute, i.e.,
[FieldHidden]
private int _Id;
public int Id
{
get { return _Id; }
set { _Id = value; }
}
However, you are trying to use the same class for two unrelated purposes and this is generally bad design. On the one hand AccountBalanceDetail is the spec for the import format. On the other you are also trying to use it to describe the Entity. Instead you should create separate classes and map between the two with a LINQ function or a library like AutoMapper.

How to get the maximum length of a string from an EDMX model in code?

I've created an EDMX object from a database I'm programming against.
I need to get input from a user and save it to a row in the database table. The problem is that I need to limit the length of input strings to the width of the corresponding VARCHAR column in the database.
When I browse the model, I can clearly see in the properties window that the model knows the max length of the string, but I don't know how to access this data in code.
If I want to write something like this:
Entities entities = new Entities();
myTable = entities.myTable.First();
if (userInput.length > myTable.columnA.MaxLength)
{
// tell the user that the input is too long.
}
else
{
myTable.columnA = userInput;
}
How do I write it?
Update: I would like to point out that the IObjectContextAdapater mentioned in the answers below is in the System.Data.Entity.Infrastructure namespace.
Here are two methods by which you can read the meta data:
int? GetMaxLength(DbContext context, string tableName, string propertyName)
{
var oc = ((IObjectContextAdapter)context).ObjectContext;
return oc.MetadataWorkspace.GetItems(DataSpace.CSpace).OfType<EntityType>()
.Where(et => et.Name == tableName)
.SelectMany(et => et.Properties.Where(p => p.Name == propertyName))
.Select (p => p.MaxLength)
.FirstOrDefault();
}
int? GetMaxLength<T>(DbContext context, Expression<Func<T, object>> property)
{
var memberExpression = (MemberExpression)property.Body;
string propertyName = memberExpression.Member.Name;
return GetMaxLength(context, typeof(T).Name, propertyName);
}
So you can either enter the table name and property name, or an expression that specifies the property you're interested in.
Another approach could be to create a MetaData class and use the MaxLength attribute.
It's not very pretty; reading edmx properties at runtime is not something Microsoft exposed easily or documented well (or in some cases, at all). context is your DBContext.
var objectContext = ((IObjectContextAdapter)context).ObjectContext;
var entityType = objectContext.MetadataWorkspace.GetItems<EntityType>(DataSpace.CSpace).Where(e => e.Name == "your entity name").First();
var facets = entityType.Properties["your property name"].TypeUsage.Facets;
facets will look something like this, so you'll need to look for the MaxLength Name(may not exist, depending on the underlying field type) and get the Value:
Count = 5
[0]: Nullable=false
[1]: DefaultValue=null
[2]: MaxLength=250
[3]: Unicode=false
[4]: FixedLength=false
If you modify the T4 template you can add your own attribute to the properties that have MaxLength set.
If you can find the right place to add it, it's something as simple as this:
var lengthAttributeText = edmProperty.MaxLength.HasValue
? string.Format("[MaxLength({0})] ", edmProperty.MaxLength.Value)
: "";
And then add this into the text for the property line. (Tricky to be more detailed since I've already modified my .tt file a lot; also the lack of proper IDE support for .tt files makes this a lot harder than it could be.)

EF Codefirst and RDLC Reports

I'm working on a MVC4 web app with EF 6. I'm using EF code first approach (on a brand new database). So i have the model classes which i use in EF.
Now i need to create some RDLC Reports. To do this i need to create a Dataset. So how can i create a dataset using my model classes? There are relationships between models classes which i need to carry to the dataset.
My ultimate goal is to design and populate data to my report using my ef models.
Thanks in Adance
Generally EF does not support DataSets. If you want to populate DataSets with data loaded with EF then you've got to provide your own functionality for that. Here I present an example how to populate DataTable object from result obtained with query:
public static class IEnumerableExtensions
{
public static DataTable ToDataTable<TEntity>(this IEnumerable<TEntity> entities)
{
DataTable table = new DataTable();
IEnumerable<PropertyInfo> properties = typeof(TEntity)
.GetProperties()
.Where(p => !p.PropertyType.IsClass || p.PropertyType == typeof(string))
foreach(string propertyName in properties.Select( p => p.Name))
{
table.Columns.Add(propertyName);
}
foreach(object item in entities)
{
List<object> propertiesValues = new List<object>();
foreach (PropertyInfo property in properties)
{
propertiesValues.Add(property.GetValue(item));
}
table.Rows.Add(propertiesValues.ToArray());
}
return table;
}
}
You might use then this extension method as follows:
DataTable table = context.People.ToDataTable();
If you want to implement relationships between tables then the logic you have to do will be more complicated. You should use ChildRelations property of DataTable objects to bind them with relations. Then your DataTable objects you might insert into DataSet.

Best Way to convert one Edmx Entity to one Business entity

I am developing one application in which data is access from edmx entities and from that we have to fill each business entity after retriving data from edmx entity like:-
var tblproducts = tblproductsData
.Select(t => new tblProduct()
{
CategoryID = t.CategoryID,
Description = t.Description,
ID = t.ID,
Image = t.Image,
InsDt = t.InsDt,
Price = t.Price,
Quantity = t.Quantity,
Status = t.Status,
Title = t.Title,
tblCategory = new EFDbFirst.Models.tblCategory()
{
ID = t.tblCategory.ID,
status = t.tblStatus.StatusID,
Title_Category = t.tblCategory.Title_Category
},
tblStatu = new EFDbFirst.Models.tblStatu()
{
StatusDescription = t.tblStatus.StatusDescription
,
StatusID = t.tblStatus.StatusID
}
});
I am fadeup with this because everytime i have to convert one to another while getting data and setting data in db,
Is there any good way to create some common mehod which takes one anonymous type and converts it to another anonymous type.
Thanks in Advance
Your example isn't that clear.
First of all, EF doesn't work with anonymous types inside itself, it works with the EF types you have defined either using edmx file or code first. You can however create anonymous types yourself by defining an Select statement.
E.g:
var products = context.tblProductsData
.Select(r => new { Description = r.Description }); //new without typename is an
//anonymous object
The tblProduct, tblCategory and tblStatu objects, are they EF types? If so, you don't need to write a Select, EF will generate objects for you when you execute it.
E.g:
var products = context.tblProductsData.ToList();
This will automatically generate tblProduct objects for you. When you try to navigate to tblProduct.tblCategory or tblProduct.tblStatu, lazy loading will retrieve them for you. If you want to explicit load them during first query (eager-loading) use the Include function.
E.g:
var products = context.tblProductsData.Include(r => r.tblCategory)
.Include(r => r.tblStatu).ToList();
However if tblProducts, tblCategory and tblStatu is business objects and NOT EF types, there isn't any other way to do this, you have to explicit create them in a Select statement.