I am using Entity Framwork with a database-first approach. I want to change the table name or view name dynamically based on conditions.
Here, I am using V_OVT_VLD_340B_DNA_CLD or V_OVT_B_table or V_OVT_c_table to get the records.
Based upon the source, I need to call the different table name and get the records. The whole code snippet is the same, except for the table name.
Please refer below code
private dOVT_OutlierViewEntities db = new dOVT_OutlierViewEntities();
if(source == "a")
{
var result = this.db.V_OVT_VLD_340B_DNA_CLD.Where(x => x.DNA_PGM_PRTN_ID == partitionId && x.CLIENT_ID == clientId).ToList().Select(y => new ValidationModel
{
claim_validation_test_id = new List<byte?> { y.CLAIM_VLD_TEST_ID },
claim_id = y.CLAIM_ID,
Provider_ID = y.Provider_ID,
}).Take(id).ToList();
}
if(source == "b")
{
var result = this.db.v_OVT_B_table.Where(x => x.DNA_PGM_PRTN_ID == partitionId && x.CLIENT_ID == clientId).ToList().Select(y => new ValidationModel
{
claim_validation_test_id = new List<byte?> { y.CLAIM_VLD_TEST_ID },
claim_id = y.CLAIM_ID,
Provider_ID = y.Provider_ID,
}).Take(id).ToList();
}
if(source == "c")
{
var result = this.db.v_OVT_C_table.Where(x => x.DNA_PGM_PRTN_ID == partitionId && x.CLIENT_ID == clientId).ToList().Select(y => new ValidationModel
{
claim_validation_test_id = new List<byte?> { y.CLAIM_VLD_TEST_ID },
claim_id = y.CLAIM_ID,
Provider_ID = y.Provider_ID,
}).Take(id).ToList();
}
I want to modify the above implementation by dynamically attaching the table name to db context based upon condition.
string tableName = string.empty
if(source == "a")
tableName = "aTable";
if(source == "b")
tableName="bTable";
this.db.tableName.where().....
Is that possible?
You can go with a switch condition to set the table type and use that with context
switch (tableName)
{
case "a":
tableType = typeof(V_OVT_VLD_340B_DNA_CLD);
break;
case "b":
tableType = typeof(v_OVT_B_table);
break;
default:
tableType = typeof(v_OVT_C_table);
break;
}
var query = context.Set(tableType);
var result = query.Find(); //filter with this query condition
You can do something like this..
string tableName = string.empty
if(source == "a")
tableName =db.GetTable("aTable");
if(source == "b")
tableName=db.GetTable("bTable");
and then query like..
tableName.where()
Related
I have a issue in EF7/asp.Net Core application. In my context I create a method Save:
public int Save()
{
ChangeTracker.DetectChanges();
var modifiedEntities = ChangeTracker.Entries()
.Where(p => p.State == EntityState.Modified || p.State == EntityState.Added || p.State == EntityState.Deleted || p.State == EntityState.Modified || p.State == EntityState.Detached).ToList();
var now = DateTime.UtcNow;
foreach (var change in modifiedEntities)
{
var entityName = change.Entity.GetType().Name;
var primaryKeyValue = GetPrimaryKeyValue(change.Entity);
foreach (var prop in change.Entity.GetType().GetTypeInfo().DeclaredProperties)
{
if (!prop.GetGetMethod().IsVirtual)
{
var currentValue = change.Property(prop.Name).CurrentValue;
var originalValue = change.Property(prop.Name).OriginalValue;
if (originalValue.ToString() != currentValue.ToString())
{
var changeLoged = new ChangeLog
{
PropertyName = prop.Name,
EntityName = entityName,
PrimaryKeyValue = primaryKeyValue,
DateChange = now,
OldValue = originalValue.ToString(),
NewValue = currentValue.ToString(),
ChangedBy = "test"
};
ChangeLog.Add(changeLoged);
}
}
}
}
return base.SaveChanges();
}
and method GetPrimaryKeyValue:
protected virtual int GetPrimaryKeyValue<T>(T entity)
{
var test = entity;
var test2 = test.GetType();
var keyName = this.Model.FindEntityType(test2).FindPrimaryKey().Properties
.Select(x => x.Name).Single();
var result = (int)entity.GetType().GetProperty(keyName).GetValue(entity, null);
if (result < 0)
return -1;
return result;
}
Unfortunatlly the change.Property(prop.Name).CurrentValue always equals OriginalValue, so the if
originalValue.ToString() != currentValue.ToString()
always return false.
Replace:
var originalValue = change.Property(prop.Name).OriginalValue;
to:
var originalValue = change.GetDatabaseValues().GetValue<object>(prop.Name);
This will not exactly answer your question since I cannot reproduce this issue but this may help you.
In EF Core, the PropertyEntry class has now an IsModified property which let you know if the value has been modified or not.
You should use it instead:
if (change.Property(prop.Name).IsModified)
{
var changeLoged = new ChangeLog
{
PropertyName = prop.Name,
EntityName = entityName,
PrimaryKeyValue = primaryKeyValue,
DateChange = now,
OldValue = originalValue.ToString(),
NewValue = currentValue.ToString(),
ChangedBy = "test"
};
ChangeLog.Add(changeLoged);
}
Disclaimer: I'm the owner of the project Entity Framework Plus
The Library has an Audit Feature (Supporting EF Core) which you may use or get inspired by to create your auditing (The code is Open Source).
Documentation: EF+ Audit
Based on the answers above I refactored my SetChanges method to this, and its working fine now! (EF 6 and .NET 6)
private void SetChanges()
{
TableName = Entry.Metadata.GetTableName();
var entsInDB = Entry.GetDatabaseValues();
foreach (PropertyEntry property in Entry.Properties)
{
if (property != null)
{
string propertyName = property.Metadata.Name;
if (property.Metadata.IsPrimaryKey())
{
KeyValues[propertyName] = property.CurrentValue ?? "";
continue;
}
switch (Entry.State)
{
case EntityState.Added:
NewValues[propertyName] = property.CurrentValue ?? "";
AuditType = AuditType.Create;
break;
case EntityState.Deleted:
OldValues[propertyName] = property.OriginalValue ?? "";
AuditType = AuditType.Delete;
break;
case EntityState.Modified:
if (property.IsModified)
{
var originalValue = entsInDB?.GetValue<object>(property.Metadata.Name);
if (originalValue?.ToString() != property.CurrentValue?.ToString())
{
ChangedColumns.Add(propertyName);
OldValues[propertyName] = originalValue?.ToString() ?? "";
NewValues[propertyName] = property.CurrentValue ?? "";
AuditType = AuditType.Update;
}
}
break;
}
}
}
}
Logging it to the database audit table:
I have a search form where the user can enter one to many parameters (Data, Status, Type, ID, Summary, Description) and leave the rest blank.
Here's my Linq to SQL code for my basic search. Is there a way to check each parameter within the Linq for zero, null or empty string?
List<RequestStatusModel> objRequestStatus = new List<RequestStatusModel>();
var query = from r in SimCareDB.Requests
where r.CustomerID == 31
select (new RequestStatusModel
{
RequestID = r.RequestID,
RequestTitle = r.RequestTitle,
DateAdded = r.DateAdded.ToString(),
DateChanged = r.DateChanged.ToString(),
RequestStatusID = r.StatusID
});
Thank you!
If it doesn't have to be in your linq statement you could just do it with classic if statements.
List<RequestStatusModel> objRequestStatus = new List<RequestStatusModel>();
var query = from r in SimCareDB.Requests
where r.CustomerID == 31
select (new RequestStatusModel
{
//...
});
if(data != null) //Replace with additional checks, if neccessary
{
query = query.where(x=> ...);
}
if(status != null)
{
query = query.where(x => ...)
}
If you want to only filter if certain criteria is passed, you should do something like this
var objRequestStatus = new List<RequestStatusModel>();
var query = from r in SimCareDB.Requests
where r.CustomerID == 31
if (String.IsNullOrEmpty(r.RequestID))
objRequestStatus = objRequestStatus.Where(x => x.RequestID == r.RequestID);
if (String.IsNullOrEmpty(r.RequestTitle))
objRequestStatus = objRequestStatus.Where(x => x.RequestTitle == r.RequestTitle);
//you other filters here
This sets up the expression to what you want based on which requests are passed
If you want to avoid all those ifs, you could do
List<RequestStatusModel> objRequestStatus = new List<RequestStatusModel>();
var query = from r in SimCareDB.Requests
where (r.CustomerID == 31) &&
(!String.IsNullOrEmpty(id) ? r.RequestID == id : true) &&
(!String.IsNullOrEmpty(status) ? r.StatusID == status : true)
/* And so on */
select (new RequestStatusModel
{
RequestID = r.RequestID,
RequestTitle = r.RequestTitle,
DateAdded = r.DateAdded.ToString(),
DateChanged = r.DateChanged.ToString(),
RequestStatusID = r.StatusID
});
I am able to filter the data with the following two parameters id1 and id2, and get accurate result of 10 records, from which have 9 with a price_type=cs and other with price-type=ms.
However, if I add price_type to the parameters id1 and id2 (id1=23456,567890&id2=6782345&price_type=ms), I get 3000 records instead of getting one record.
Am I missing something in the code. Any help would be very much appreciated.
var data = db.database_BWICs.AsQueryable();
var filteredData = new List<IQueryable<database_Data>>();
if (!string.IsNullOrEmpty(query.name))
{
var ids = query.name.Split(',');
foreach (string i in ids)
{
filteredData.Add(data.Where(c => c.Name != null && c.Name.Contains(i)));
}
}
if (!string.IsNullOrEmpty(query.id2))
{
var ids = query.id2.Split(',');
foreach (string i in ids)
{
filteredData.Add(data.Where(c => c.ID2!= null && c.ID2.Contains(i)));
}
}
if (!string.IsNullOrEmpty(query.id1))
{
var ids = query.id1.Split(',');
foreach (string i in ids)
{
filteredData.Add(data.Where(c => c.ID1!= null && c.ID1.Contains(i)));
}
}
if (query.price_type != null)
{
var ids = query.price_type.Split(',');
foreach (string i in ids)
{
filteredData.Add(data.Where(c => c.Type.Contains(i)));
}
}
if (filteredData.Count != 0)
{
data = filteredData.Aggregate(Queryable.Union);
}
Updated Code:
var data = db.database_BWICs.AsQueryable();
if (!string.IsNullOrEmpty(query.name))
{
var ids = query.name.Split(',');
data = data.Where(c => c.Name != null && ids.Contains(c.Name));
}
if (query.price_type != null)
{
var ids = query.price_type.Split(',');
data = data.Where(c => ids.Contains(c.Cover));
}
if (!String.IsNullOrEmpty(query.id1))
{
var ids = query.id1.Split(',');
data = data.Where(c => c.ID1!= null && ids.Contains(c.ID1));
}
Because you don't add filter to restrict, every filter adds datas to result.
It means you make OR between your filters, not AND.
And your usage of contains looks rather strange too : you're using String.Contains, while I would guess (maybe wrong) that you want to see if a value is in a list => Enumerable.Contains
You should rather go for something like this (withoud filteredData)
var data = db.database_BWICs.AsQueryable();
if (!string.IsNullOrEmpty(query.name))
{
var ids = query.name.Split(',');
data = data.Where(c => c.Name != null && ids.Contains(c.Name)));
}
//etc.
if (query.price_type != null)
{
var ids = query.price_type.Split(',');
data = data.Where(c => ids.Contains(c.Type));
}
EDIT
Well, if you wanna mix and or conditions, you could go for PredicateBuilder
Then your code should look like that (to be tested).
//manage the queries with OR clause first
var innerOr = Predicate.True<database_BWICs>();//or the real type of your entity
if (!String.IsNullOrEmpty(query.id1))
{
var ids = query.id1.Split(',');
innerOr = innerOr.Or(c => c.ID1!= null && ids.Contains(c.ID1));
}
if (!String.IsNullOrEmpty(query.id2))
{
var ids = query.id2.Split(',');
innerOr = innerOr.Or(c => c.ID2!= null && ids.Contains(c.ID2));
}
//now manage the queries with AND clauses
var innerAnd = Predicate.True<database_BWICs>();//or the real type of your entity
if (query.price_type != null)
{
var ids = query.price_type.Split(',');
innerAnd = innerAnd.And(c => ids.Contains(c.Type));
}
//etc.
innerAnd = innerAnd.And(innerOr);
var data = db.database_BWICs.AsQueryable().Where(innerAnd);
Here's my query for retrieving list of inactive/active users from my database. my problem is that, my query seems to be too complicated. so can you please give me a tip how to enhance my query.
here's my code
using (basecampcoreEntities dbs = ConfigAndResource.BaseCampContext())
{
//loads all user where isactive property has the same value as IsActive
var Users = from useritem in dbs.users
where useritem.useraccount.IsActive.Equals(IsActive)
orderby useritem.useraccount.CreatedDate
select useritem;
//check if users count is greater than 0
if (Users.Count() > 0)
{
List<user> CasebookUser = new List<user>();
switch (SearchBy)
{
case DTO::SearchBy.FirstName:
{
CasebookUser = Users.Where(item => item.FirstName.ToUpper().Equals(SearchText.ToUpper())).Skip(skip).Take(take).ToList();
} break;
case DTO::SearchBy.LastName:
{
CasebookUser = Users.Where(item => item.LastName.ToUpper().Equals(SearchText.ToUpper())).Skip(skip).Take(take).ToList();
} break;
case DTO::SearchBy.LoginID:
{
CasebookUser = Users.Where(item => item.LoginID.ToUpper().Equals(SearchText.ToUpper())).Skip(skip).Take(take).ToList();
} break;
case DTO::SearchBy.None:
CasebookUser = Users.Skip(skip).Take(take).ToList();
{
} break;
}
//transform the data into DTO class
return (from item in CasebookUser
select new DTO::User
{
LoginID = item.LoginID,
FirstName = item.FirstName,
LastName = item.LastName,
MiddleName = item.MiddleName,
Birhtday = item.userinfo != null ? item.userinfo.Birthday : DateTime.UtcNow
}).ToList();
}
return null;
Leverage the power of delayed execution here...
// first filter by whether user is active or not
var query = dbs.users.Where(x => x.useraccount.IsActive == IsActive);
// next filter by specific search field
switch (SearchBy)
{
case DTO::SearchBy.FirstName:
{
query = query.Where(x => string.Equals(x.FirstName, SearchText, StringComparison.InvariantCultureIgnoreCase));
break;
}
case DTO::SearchBy.LastName:
{
query = Users.Where(x => string.Equals(x.LastName, SearchText, StringComparison.InvariantCultureIgnoreCase));
break;
}
...
}
// then apply paging
query = query.Skip(skip).Take(take);
// finally, order by CreatedDate (ascending)
query = query.OrderBy(x => x.useraccount.CreatedDate);
// now fetch the records!
return (from item in query
select new DTO::User
{
LoginID = item.LoginID,
FirstName = item.FirstName,
LastName = item.LastName,
MiddleName = item.MiddleName,
Birhtday = item.userinfo != null ? item.userinfo.Birthday : DateTime.UtcNow
}).ToList();
This code will give you what you need in a more optimal way (only 1 DB trip) - and it's a bit more readable into the bargain.
I would:
Remove the sort from the original query definition
Do not convert ToList() and to Skip()' andTake()` prematurely
Do not cast ToList() twice. Only do it when you are creating your final collection.
Make IsActive comparison more clear in the initial query
Rewrite first query as one line linq expression
Use Any() instead of Count() > 0
Convert your search text ToUpper() just one time. Makes your search cases more concise and readable (same with using == instead of Equals)
This code might help:
using (basecampcoreEntities dbs = ConfigAndResource.BaseCampContext())
{
//loads all user where isactive property has the same value as IsActive
var Users = db.Users.Where(x => x.useraccount.IsActive == IsActive);
if (Users.Any())
{
var searchText = SearchText.ToUpper();
switch (SearchBy)
{
case DTO::SearchBy.FirstName:
Users = Users.Where(item => item.FirstName.ToUpper() == searchText);
break;
case DTO::SearchBy.LastName:
Users = Users.Where(item => item.LastName.ToUpper() == searchText);
break;
case DTO::SearchBy.LoginID:
Users = Users.Where(item => item.LoginID.ToUpper() == searchText);
break;
}
// apply sort and skip/take
Users = Users.OrderBy(x => x.useraccount.CreateDate).Skip(skip).Take(take);
//transform the data into DTO class
return (from item in Users
select new DTO::User
{
LoginID = item.LoginID,
FirstName = item.FirstName,
LastName = item.LastName,
MiddleName = item.MiddleName,
Birthday = item.userinfo != null ? item.userinfo.Birthday : DateTime.UtcNow
}).ToList();
}
return null;
}
I'm defining a variable within an if statement and then trying to use that variable outside the context of that statement. How can I initialize an empty dbSet outside of the if statements so that the viewModel can see/populate it?
This is what I'm trying to accomplish:
if (order.type.ToString() == "Rush")
{
var filteredOrders = db.Orders.Where(a => a.rushID == order.rushID).ToList();
}
else if (order.type.ToString() == "Standard")
{
var filteredOrders = db.Orders.Where(a => a.standardID == order.standardID).ToList();
}
else
{
return HttpNotFound();
}
var viewModel = new OrderDetailsViewModel
{
PastOrders = filteredOrders, // filteredOrders doesn't exist here
Order = order;
};
IEnumerable<Order> filteredOrders; // or IList<Order>, etc., as your prefer
switch (order.type.ToString())
{
case "Rush":
filteredOrders = db.Orders.Where(a => a.rushID == order.rushID).ToList();
case "Standard":
filteredOrders = db.Orders.Where(a => a.standardID == order.standardID).ToList();
default::
return HttpNotFound();
}
var viewModel = new OrderDetailsViewModel
{
PastOrders = filteredOrders,
Order = order;
};
or better:
Func<Order, bool> filter;
switch (order.type.ToString())
{
case "Rush":
filter = a => a.rushID == order.rushID;
case "Standard":
filter = a => a.standardID == order.standardID;
default::
return HttpNotFound();
}
var viewModel = new OrderDetailsViewModel
{
PastOrders = db.Orders.Where(filter).ToList(),
Order = order;
};