EF4: Get EntitySet specified as string - c#

I have a database with many single isolated tables, and I need to fill their contents to their Entities.
Currently I have for each and every table something like this:
try
{
using(DBContext context = new DBContext())
{
var vehicleTypes = context.VehicleTypes;
return vehicleTypes;
}
}
catch(Exception ex)
{
//handle error
}
What I'm seeking for is best described as something like this:
var vehicleTypes = context.GetEntitySet(VehicleEntity);
var buildingTypes = context.GetEntitySet(BuildingEntity);
where VehicleEntity, BuildingEntity (...) are entities from entity model.
I know I don't have this option explicitly, but something in that similar way would be nice. Extension method is also an option...
I'm working with EntityFramework 4.0, POCO Self Tracking Entities (without proxy).
Thanks
edit: My latest try was this:
public static IEnumerable<TEntity> GetTableContent<TEntity>() where TEntity:class
{
try
{
using (var context = new DBEntities())
{
var result = context.ExecuteStoreQuery<TEntity>("SELECT * FROM " + typeof(TEntity).Name); //the table names correspond to Entity type class names
return result;
}
}
catch (Exception ex)
{
//handle error...
}
}
but I get the error:
The data reader is incompatible with the specified 'DBEntities.MyEntity'. A member of the type, 'Id', does not have a corresponding column in the data reader with the same name.
and that's true - the names don't match - but I figured it would've mapped the table from query based on edmx definition? How can I get this right?

Start with code which actually works before trying to make it "generic"
result = context.ExecuteStoreQuery<Something>("SELECT SOMETHING_ID AS ID, ..., FROM SOMETHING...");

Related

LinqToSql - Explicit construction of entity type '{0}' in query is not allowed

I'm facing a very strange problem on two easy webapi methods:
I have lot of methods that are used to retrive masterdata from db.
They are all similars and they all read tables with same structures.
I really don't know why for example this method works:
this works
[HttpGet]
//[Authorize]
[Route("offertsource/get")]
public IHttpActionResult api_MasterData_OffertSource_GET()
{
var res = new List<mdOffertSource>();
try
{
res = (from c in db.mdOffertSources.OrderBy(x => x.OffertSource)
select new mdOffertSource() { idOffertSource = c.idOffertSource, Code = c.Code, OffertSource = c.OffertSource }).ToList();
return Ok(res);
}
catch (Exception e)
{
return BadRequest(e.Message);
}
}
and this doesn't work:
NOT working code
[HttpGet]
//[Authorize]
[Route("offertstatus/get")]
public IHttpActionResult api_MasterData_OffertStatus_GET()
{
var res = new List<mdOffertStatus>();
try
{
res = (from c in db.mdOffertStatus.OrderBy(x => x.intOrder).ThenBy(x => x.OffertStatus)
select new mdOffertStatus() { idOffertStatus = c.idOffertStatus, Code = c.Code, OffertStatus = c.OffertStatus }).ToList();
return Ok(res);
}
catch (Exception e)
{
return BadRequest(e.Message);
}
}
I have lot of methods similar to the first one working and only the second one below is not working.
If I test it, i get this error:
Explicit construction of entity type 'XionDB.mdOffertStatus' in query is not allowed.
I have already tried to remove the table mdOffertStatus from the dbml file and to add again it but I face the same problem...
This is very strange.
Thanks to support
Your OfferSources table isn't empty, is it? That would cause the Select not to be called and avoid the error thrown in the second method.
(Would leave this as a comment but don't have enough points.)
I solved my problem by deleting the dbml file and adding it again...
Before I tryied to remove and add again the table mdOffertStatus but nothing changed, dropping the dbml and adding it again fixed the problem.
May be there was some mess in the solution...
Thanks to support

Azure storage table delete row by row key

I am trying to delete row from azure storage filter by only rowkey value.
But I dont see any overload for delete operation where we can filter with only rowkey. Is there any alternative option to delete row from azure storage table for records with specific rowkey?
RemoveEntityByRowKey('123456');
public static void RemoveEntityByRowKey(string myRowKey)
{
try
{
CloudTable table = _tableClient.GetTableReference("MyAzureTable");
TableOperation delteOperation = TableOperation.Delete(myRowKey);
table.Execute(delteOperation);
}
catch (Exception ex)
{
LogError.LogErrorToAzure(ex);
throw;
}
}
In order to delete an entity, you would need both PartitionKey and RowKey (Delete Entity REST API). So what you would need to do is first fetch the entity with matching RowKey. Once you have fetched this entity, you should be able to call TableOperation.Delete as mentioned in the answers.
However, fetching entity by RowKey is not recommended because it will do a Full Table Scan. It may not be a problem if your table size is small but would be an issue where your table contains large number of entities. Furthermore, a RowKey is unique in a Partition i.e. in a table there can be only one entity with a PartitionKey/RowKey combination. In other words, you can potentially have entities with same RowKey in different Partitions. So when you fetch entities by RowKey only, you may get more than one entity back. You need to ensure that you're deleting correct entity.
If you know the PartitionKey as well as the RowKey, you don't need to retrieve the entire entity to delete it. You could adapt your code as follows:
public static void RemoveEntityByRowKey(string myRowKey)
{
try
{
var entity = new YourEntity
{
PartitionKey = "partition",
RowKey = myRowKey,
ETag = "*"
}
CloudTable table = _tableClient.GetTableReference("MyAzureTable");
TableOperation delteOperation = TableOperation.Delete(entity);
table.Execute(delteOperation);
}
catch (Exception ex)
{
LogError.LogErrorToAzure(ex);
throw;
}
}
If you're targeting .NET Core, you'll need to use the ExecuteQuerySegmentedAsync method to execute the filter condition query. ExecuteQuery is deprecated.
var cloudTableClient = _cloudStorageAccount.CreateCloudTableClient();
var myTable = cloudTableClient.GetTableReference("MyTable");
var query = new TableQuery<MyEntity>().Where(TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.Equal, "myRowKey"));
var segment = await myTable.ExecuteQuerySegmentedAsync(query, null);
var myEntities = segment.Results;
By row do you mean a record ?
TableOperation.Delete accepts a Table Entity. See here: https://msdn.microsoft.com/en-us/library/microsoft.windowsazure.storage.table.tableoperation.delete.aspx
In order to delete that entity, you must first retrieve it by specifying its Partition key and/or Row key.
Look into TableQuery class here https://msdn.microsoft.com/en-us/library/microsoft.windowsazure.storage.table.tablequery_methods.aspx
Once you retrieve it, pass it to Delete method.
There is no method
TableOperation.Delete(String rowKey),
only method
public static TableOperation delete(final TableEntity entity)
in TableOperation. For details, see Get started with Azure Table storage using .NET
With reference to Franks pointers, I am posting answer so that it would be useful to others who face the similar issue.
RemoveEntityByRowKey('123456');
public static void RemoveEntityByRowKey(string myRowKey)
{
try
{
CloudTable table = _tableClient.GetTableReference("MyAzureTable");
TableQuery<myEntity> query = new TableQuery<myEntity>()
.Where(TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.Equal, myRowKey));
foreach (var item in table.ExecuteQuery(query))
{
var oper = TableOperation.Delete(item);
table.Execute(oper);
}
}
catch (Exception ex)
{
LogErrorToAzure(ex);
throw;
}
}
mpl is my table row entity and is required to delete the record from the db.
I have added this answer to show an async (with result check)
if (result)
{
//delete the lead from the storage table
TableOperation delRow = TableOperation.Delete(mpl);
TableResult tr = await table.ExecuteAsync(delRow);
if (((int)tr.HttpStatusCode) < 400)
log.LogInformation($"Table Record: {mpl.RowKey} deleted");
else
log.LogError($"Table Record: {mpl.RowKey} NOT deleted");
}
The Windows.Azure.Storage namespace is now deprecated in favour of the Azure.Data.Tables namespace. As such the TableOperation.Delete method is also deprecated. You should now use a TableClient and it's DeleteEntity method:
TableClient tableClient = new TableClient(connectionString, Table);
tableClient.DeleteEntity(PartitionKey, RowKey);
There is also the async version DeleteEntityAsync if you wish too.

DomainDataService (ViewModel) loading an entity via a string name

So I'm attempting to dynamically load my domain data service where the table name is the string ... Here's what I've got so far: Normally I'd load like this:
theDomainDataService.Load(theDomainDataService.getUsersQuery());
so I'm trying to automate which entity is loaded by the string name.
String theVariableEntityName = "Users";
Type t = theDomainDataService.GetType();
MethodInfo stuff = t.GetMethod("Get" + theVariableEntityName + "Query");
var theQuery = stuff.Invoke(theDomainDataService, null);
theDomainDataService.Load((EntityQuery<MySite.Web.Models.User>)theQuery);
---------------------------------------------------------^ Problem
This is in fact loading my domainDataService correctly, but what I need is a dynamic way to infer the type of the EntityQuery (without explicitly declaring it's going to be a User), because in fact it could be anything.
I have tried this from the DomainDataService Class with no luck, it isn't finding method's "Set" or "Entry".
public List<object> void PopulateEntity(string theEntityName)
{
Type theEntity = Type.GetType("MySiteMaintenance.Web.Models." + theEntityName);
using (var db = new DatingEntities())
{
IQueryable query = db.Set(theEntity);
foreach (var item in query)
{
var entry = db.Entry(item);
}
}
}
Remember, all I need is a populated entity (when all I have is the name of the entity) populated Client side... so I can say
DomainServiceClass theClass = new DomainServiceClass();
theClass.Load(theClass.GetEntityNameQuery());
so I can reference the appropriately loaded entity with...
theClass.Entity (users... questions, etc..)
I'm still not sure I follow, but...
I have a Post entity in my Sandbox namespace which I get from my DbContext instance using the entity type name in a string to start with...
// Get my entity type (if in same assembly, else you'll have to specify)
Type postType = Type.GetType("Sandbox.Post");
using (var db = new StackOverflowEntities()) {
// not required
db.Configuration.ProxyCreationEnabled = false;
IQueryable query = db.Set(postType);
foreach (var item in query) {
DbEntityEntry entry = db.Entry(item);
}
}
Which results in retrieving any DbSet based on a Entity type string. Below a breakpoint in the item foreach loop - revealing the values.

bulk delete in entity framework

I'd like to bulk delete records from a table using linq.
There's a post that describes how to do it:
Bulk-deleting in LINQ to Entities
var query = from c in ctx.Customers
where c.SalesPerson.Email == "..."
select c;
query.Delete();
But the function "Delete" doesn't exist in my var variable.
Furthermore, the function "SubmitChanges" doesn't exist on my context.
There is an interesting NuGet package that lets you do batch deletes and updates:
There is no currently supported bulk delete baked into Entity Framework. Its actually one of the features being discussed on codeplex now EF is open-source.
EntityFramework.Extended provides batch delete support (you can find this in nuget) however my experience is that it has some performance issues.
This code adds a simple extension method to any DbContext that will bulk delete all data in any table referenced within the entity framework query you provide. It works by simply extracting all table names involved in the query, and attempting to delete the data by issuing a "DELETE FROM tablename" SQL query, which is common across most types of database.
To use, simply do this:
myContext.BulkDelete(x => x.Things);
which will delete everything in the table linked to the Things entity store.
The code:
using System.Linq;
using System.Text.RegularExpressions;
namespace System.Data.Entity {
public static class DbContextExtensions {
/// <summary>
/// Efficiently deletes all data from any database tables used by the specified entity framework query.
/// </summary>
/// <typeparam name="TContext">The DbContext Type on which to perform the delete.</typeparam>
/// <typeparam name="TEntity">The Entity Type to which the query resolves.</typeparam>
/// <param name="ctx">The DbContext on which to perform the delete.</param>
/// <param name="deleteQuery">The query that references the tables you want to delete.</param>
public static void BulkDelete<TContext, TEntity>(this TContext ctx, Func<TContext, IQueryable<TEntity>> deleteQuery) where TContext : DbContext {
var findTables = new Regex(#"(?:FROM|JOIN)\s+(\[\w+\]\.\[\w+\])\s+AS");
var qry = deleteQuery(ctx).ToString();
// Get list of all tables mentioned in the query
var tables = findTables.Matches(qry).Cast<Match>().Select(m => m.Result("$1")).Distinct().ToList();
// Loop through all the tables, attempting to delete each one in turn
var max = 30;
var exception = (Exception)null;
while (tables.Any() && max-- > 0) {
// Get the next table
var table = tables.First();
try {
// Attempt the delete
ctx.Database.ExecuteSqlCommand(string.Format("DELETE FROM {0}", table));
// Success, so remove table from the list
tables.Remove(table);
} catch (Exception ex) {
// Error, probably due to dependent constraint, save exception for later if needed.
exception = ex;
// Push the table to the back of the queue
tables.Remove(table);
tables.Add(table);
}
}
// Error error has occurred, and cannot be resolved by deleting in a different
// order, then rethrow the exception and give up.
if (max <= 0 && exception != null) throw exception;
}
}
}
I do it like this which seems to work fine. Please let know if there is a reason why this is bad practice in any way.
var customersToDelete = await ctx.Customers.Where(c => c.Email == "...").ToListAsync();
foreach (var customerToDelete in customersToDelete)
{
ctx.Customers.Remove(customerToDelete);
}
await ctx.SaveChangesAsync();
I was experiencing the same problem with EF executing thousands of DELETE queries after SaveChanges call. I wasn't sure that EntityFramework.Extensions commercial library would help me so I decided to implement bulk DELETE myself and came up with something similar to BG100's solution!
public async Task<List<TK>> BulkDeleteAsync(List<TK> ids)
{
if (ids.Count < 1) {
return new List<TK>();
}
// NOTE: DbContext here is a property of Repository Class
// SOURCE: https://stackoverflow.com/a/9775744
var tableName = DbContext.GetTableName<T>();
var sql = $#"
DELETE FROM {tableName}
OUTPUT Deleted.Id
// NOTE: Be aware that 'Id' column naming depends on your project conventions
WHERE Id IN({string.Join(", ", ids)});
";
return await #this.Database.SqlQuery<TK>(sql).ToListAsync();
}
If you have something like generic repository that should work for you just fine. At least you could try to fit it into your EF infrastructure.
I also tweaked it a bit more and was able to execute queries on multiple chunks of entities. It would help you if there are any restrictions of query size in your DB.
const int ChunkSize = 1024;
public async Task<List<TK>> BulkDeleteAsync(List<TK> ids)
{
if (ids.Count < 1) {
return new List<TK>();
}
// SOURCE: https://stackoverflow.com/a/20953521/11344051
var chunks = ids.Chunks(ChunkSize).Select(c => c.ToList()).ToList();
var tableName = DbContext.GetTableName<T>();
var deletedIds = new List<TK>();
foreach (var chunk in chunks) {
var sql = $#"
DELETE FROM {tableName}
OUTPUT Deleted.Id
WHERE Id IN({string.Join(", ", chunk)});
";
deletedIds.AddRange(DbContext.Database.SqlQuery<TK>(sql).ToListAsync());
}
return deletedIds;
}

Updating a N-1 columns of a N columned table using QueryOver<T>() method

I want to update only some columns say N-1 columns in a table containing N columns using NHibernate QueryOver syntax.
The query I tried is something like this.
public T UpdatePost(Object DateUpdated, object DateExpires, object Id)
{
using (var session=sessionFactory.OpenSession())
{
using (var transaction=session.BeginTransaction())
{
session.Update(DateUpdated, Id);
session.Update(DateExpires, Id);
transaction.Commit();
return session.Get<T>(Id);
}
}
}
Calling method is
obj.UpdatePost(DateTime.Now, DateTime.Now.AddDays(30), 3);
Error is
There is a problem with your mappings. You are probably trying to map a System.ValueType to a which NHibernate does not allow or you are incorrectly using the IDictionary that is mapped to a . A ValueType (System.DateTime) can not be used with IdentityKey.
How to achieve this?
Your UpdatePost method makes no sense. NHibernate's session.Update expects an entity object that should be updated. Documentation for the overload you are trying to use is:
Updates the persistent state associated with the given identifier.
First argument should be an entire entity object.
If you try to analyze your code, there's no way for NHibernate to know which entity do you want to update. Update method is not generic, neither is the Session. You are just trying to give it a date value and an id. How would NHibernate know which table and which column to update?
In order to do partial updates with NHibernate, you would need to use HQL update queries (DML-style operations).
Here's how it would look like in your case:
public T UpdatePost(Object DateUpdated, object DateExpires, object Id)
{
using (var session=sessionFactory.OpenSession())
{
using (var transaction=session.BeginTransaction())
{
string hqlUpdate =
"update Post p set p.DateUpdated = :dateUpdated, p.DateExpires = :dateExpires where p.id = :id";
session.CreateQuery(hqlUpdate)
.SetDateTime("dateUpdated", DateUpdated)
.SetDateTime("dateExpires", DateExpires)
.SetParameter("id", Id)
.ExecuteUpdate();
transaction.Commit();
return session.Get<T>(Id);
}
}
}
On a side note, since you are already getting the entity itself after the update, you could simply load the entity first, change its properties and save it. You would still have two database operations.
public T UpdatePost(DateTime DateUpdated, DateTime DateExpires, object Id)
{
using (var session=sessionFactory.OpenSession())
{
using (var transaction=session.BeginTransaction())
{
T post = session.Get<T>(Id);
post.DateUpdated = DateUpdated;
post.DateExpires = DateExpires;
session.Update(post);
transaction.Commit();
return post;
}
}
}
If you really want to force NHibernate to update only the columns that are changed, you can specify dynamic-update="true" in class mapping declaration.
dynamic-update (optional, defaults to false): Specifies that UPDATE SQL should be generated at runtime and contain only those columns whose values have changed.
This will be the optimal solution as SetDateTime accepts only DateTime types as its parameter values. and while declaring this method generically, we must format the query according to its entity type.
public T UpdatePost(DateTime DateUpdated, DateTime DateExpires, object Id) <-- DateTime parameters
{
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
string hqlUpdate = string.Format(
"update {0} p set p.DateUpdated = :dateUpdated, p.DateExpires = :dateExpires where p.id = :id", typeof(T)); <-- passing the type of the entity.
session.CreateQuery(hqlUpdate)
.SetDateTime("dateUpdated",DateUpdated)
.SetDateTime("dateExpires", DateExpires)
.SetParameter("id", Id)
.ExecuteUpdate();
transaction.Commit();
return session.Get<T>(Id);
}
}
}

Categories

Resources