Entity Framework - search on encrypted columns - entity-framework-6

I have this table:
public class Person
{
public int Id { get; set; }
[Encrypted]
public string Name { get; set; }
}
and this attribute:
public class EncryptedAttribute: Attribute { }
Now, I want every property with this Encrypted attribute will be saved with the encrypted value. but when I want to do query using LINQ I don't want to manipulate the search value. example:
string searchTerm = "Sean";
// I want to search like this
var results = ctx.Persons.where(p => p.Name == searchTerm).ToList();
//and not like this
// var results = ctx.Persons.where(p => p.Name == EncryptMethod(searchTerm)).ToList();
is it posible to do something like this?
thanks.

Related

Return last value of a column

I am trying to get the last value of a record from the database. I am using entity framework. I am trying to get the last value of the balance and deduct the amount user enters to get the new balance. I am new to this and trying to create a simple expense management system.
My controller
public ActionResult Create([Bind(Include = "ExpenseId,ExpenseFor,DateTime,Amount,Balance,RowVersion")] Expense expense)
{
if (ModelState.IsValid)
{
var a = db.Expenses.Select(b => b.Balance);
var c = a.Last();
expense.Balance = c - expense.Amount;
db.Expenses.Add(expense);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(expense);
}
return View(expense);
}
My model looks like this
public class Expense
{
public int ExpenseId { get; set; }
public string ExpenseFor { get; set; }
public DateTime DateTime { get; set; }
public Decimal? Amount { get; set; }
public Decimal? Balance { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
}
When I try to create new records, it says that the Method cannot be translated into a store expression. I would really appreciate any help with this.
If you use SQL server, it's no wonder .Last()function is not going to work.
There is no such things as (select last) in SQL server, so Entity basicaly fails to translate it to a database SQL server language. This is what you have to do :
var v = db.Expenses.OrderByDescending(t => t.ColumnName).First();
Or something similar, depending on what you want.
Try to think of a way to turn the query around and use First() ... or FirstOrDefault() if your are afraid of potential null values.
Your own solution :
var v = db.Expenses.OrderByDescending(t => t.ExpenseId).First();

The specified type member 'UsersCount' is not supported in LINQ to Entities

I have such POCO entity
public class Product : Entity
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string Name { get; set; }
public virtual ICollection<Order> Orders { get; set; }
[NotMapped]
public int UsersCount
{
get
{
return Orders.Count();
}
}
}
Product access method
public IQueryable<Product> GetAll()
{
return _context.Product.Include(I=>I.Orders);
}
When I load all products into View
var model = _productService.GetAll().Select(p => new AdminProductViewModel
{
Active = p.Active,
Id = p.Id,
Name = p.Name,
UsersCount = p.UsersCount
}).ToList();
I get exception
The specified type member 'UsersCount' is not supported in LINQ to
Entities.
I really can't understand why Linq to Entity gives exception. Maybe someone explain what is wrong?
We also use calculated fields in another application like this
public class User : Entity
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string Email { get; set; }
[MaxLength(50)]
public string FirstName { get; set; }
[MaxLength(50)]
public string LastName { get; set; }
public virtual ICollection<Order> Orders { get; set; }
public virtual ICollection<Statistic> Statistics { get; set; }
[NotMapped]
public bool Active
{
get
{
return Orders.Any(c => c.Active && (c.TransactionType == TransactionType.Order || c.TransactionType == TransactionType.Subscription));
}
}
[NotMapped]
public int CreditsLeft
{
get
{
return Orders.Where(w => w.Active).Sum(p => p.Credits != null ? p.Credits.Value : 0);
}
}
}
public User Get(int id)
{
return _context.User.FirstOrDefault(u => u.Id == id);
}
var user = _userService.Get(_authUser.Id);
var model = new UserViewModel
{
Active = user.Active,
FullName = user.FullName,
Email = user.Email,
};
and have no problems, EF6 don't give any exception though it also has two calculated fields User.Active and User.CreditsLeft
Keep in mind that LINQ to Entities tries to translate each LINQ statement into SQL. Your statement...
var model = _productService.GetAll().Select(p => new AdminProductViewModel...
...is a LINQ extension method (Select) against an IQueryable (_productService.GetAll()). As the documentation shows, this method takes an Expression as argument.
You can see an expression as a tree of tokens that express the task it should execute. A LINQ provider, simply said, is a dictionary of tokens in "expression language" to tokens in some other language, SQL in this case. The whole statement is translated into SQL and executed by the database. The .Net runtime only sends the statement away and processes the returned result.
Inspecting EF's source code reveals that many tokens are hard-coded: all SQL keywords, a number of built-in ("canonical") functions (like DATEDIFF) and a selection of .Net methods. Other tokens are added by mapping entity properties to database columns. Recently, ToString() was added to the .Net part of the dictionary. In EF6 we can write...
_context.Product.Select(p => p.Id.ToString())
Before that, this would raise the infamous
LINQ to Entities does not recognize the method 'System.String ToString()'
Your exception has the same cause, but it pertains to members in stead of methods. p.UsersCount is not in the dictionary because it is not mapped.
We also use calculated fields in another application like this
Here a User has been fetched from the database and materialized as a C# object. Now when you access its properties it's just .Net code running. There's no SQL translation going on here. Well... it probably triggers lazy loading (of orders and credits), but the act of accessing the property does not happen in the context of an expression.
Likewise, you can access UsersCount once you've got a Product object. If you want the database to do the heavy lifting of counting the orders, you'll have to use the expression in the LINQ statement:
var model = _productService.GetAll().Select(p => new AdminProductViewModel
{
Active = p.Active,
Id = p.Id,
Name = p.Name,
UsersCount = p.Orders.Count()
}).ToList();
Try this instead:
var model = _productService.GetAll().Select(p => new AdminProductViewModel
{
Active = p.Active,
Id = p.Id,
Name = p.Name,
UsersCount = p.Orders.Count()
}).ToList();
Linq to Entities can't translate the Product.UsersCount property into sql so it gives you the error message
The specified type member 'UsersCount' is not supported in LINQ to
Entities

MVC 5 - Logging inserts and updates in database

I have looked for alot of different approaches to achive this. But I have yet to find a good and simple exmaple on how to do this without alot of 3 party instalations wich focuses on performance logging and debugging.
I am looking for a way to easily log ALL changes to my database and also when new rows are added. I want to have a own table that stores which action in which controller was called / or simply the database table would be enough to track it. And the which fields where updated or added.
I am picturing a table something like this:
ID - ACTION/TABLE/METHOD - ID - TYPE - DETAILS - CREATED BY - TIMESTAMP
x - TableName/ActionResult/JsonResult/ - ID of the new or updated item - updated or new - details on what have changed or created - user.identity - timestamp
So i can view the log table in each spesific view and i can see the history for that item and which fields where changed etc.
I looked at the bottom suggestion here: How to implement a MVC 4 change log? since my SQL database does not support the SQL Service Broker and I dont really want to start with adding Triggers in SQL.
I am using MVC 5.2 and EF 6.0 so I have looked at the Database.Log property but I really need some guidance on how to set up a good method to achive what I want.
I found a solution i am currently modifying to my needs.
Here is the code:
Overriding SaveChanges Class in
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
and adding theese methods:
public async Task SaveChangesAsync(string userId)
{
// Get all Added/Deleted/Modified entities (not Unmodified or Detached)
foreach (var ent in this.ChangeTracker.Entries().Where(p => p.State == EntityState.Added || p.State == EntityState.Deleted || p.State == EntityState.Modified))
{
// For each changed record, get the audit record entries and add them
foreach (Log x in GetAuditRecordsForChange(ent, userId))
{
this.Log.Add(x);
}
}
// Call the original SaveChanges(), which will save both the changes made and the audit records
await base.SaveChangesAsync();
}
private List<Log> GetAuditRecordsForChange(DbEntityEntry dbEntry, string userId)
{
List<Log> result = new List<Log>();
DateTime changeTime = DateTime.Now;
// Get the Table() attribute, if one exists
TableAttribute tableAttr = dbEntry.Entity.GetType().GetCustomAttributes(typeof(TableAttribute), false).SingleOrDefault() as TableAttribute;
// Get table name (if it has a Table attribute, use that, otherwise get the pluralized name)
string tableName = tableAttr != null ? tableAttr.Name : dbEntry.Entity.GetType().Name;
// Get primary key value (If you have more than one key column, this will need to be adjusted)
string keyName = dbEntry.Entity.GetType().GetProperties().Single(p => p.GetCustomAttributes(typeof(KeyAttribute), false).Count() > 0).Name;
if (dbEntry.State == EntityState.Added)
{
// For Inserts, just add the whole record
// If the entity implements IDescribableEntity, use the description from Describe(), otherwise use ToString()
result.Add(new Log()
{
LogID = Guid.NewGuid(),
EventType = "A", // Added
TableName = tableName,
RecordID = dbEntry.CurrentValues.GetValue<object>(keyName).ToString(), // Again, adjust this if you have a multi-column key
ColumnName = "*ALL", // Or make it nullable, whatever you want
NewValue = (dbEntry.CurrentValues.ToObject() is IDescribableEntity) ? (dbEntry.CurrentValues.ToObject() as IDescribableEntity).Describe() : dbEntry.CurrentValues.ToObject().ToString(),
Created_by = userId,
Created_date = changeTime
}
);
}
else if (dbEntry.State == EntityState.Deleted)
{
// Same with deletes, do the whole record, and use either the description from Describe() or ToString()
result.Add(new Log()
{
LogID = Guid.NewGuid(),
EventType = "D", // Deleted
TableName = tableName,
RecordID = dbEntry.OriginalValues.GetValue<object>(keyName).ToString(),
ColumnName = "*ALL",
NewValue = (dbEntry.OriginalValues.ToObject() is IDescribableEntity) ? (dbEntry.OriginalValues.ToObject() as IDescribableEntity).Describe() : dbEntry.OriginalValues.ToObject().ToString(),
Created_by = userId,
Created_date = changeTime
}
);
}
else if (dbEntry.State == EntityState.Modified)
{
foreach (string propertyName in dbEntry.OriginalValues.PropertyNames)
{
// For updates, we only want to capture the columns that actually changed
if (!object.Equals(dbEntry.OriginalValues.GetValue<object>(propertyName), dbEntry.CurrentValues.GetValue<object>(propertyName)))
{
result.Add(new Log()
{
LogID = Guid.NewGuid(),
EventType = "M", // Modified
TableName = tableName,
RecordID = dbEntry.OriginalValues.GetValue<object>(keyName).ToString(),
ColumnName = propertyName,
OriginalValue = dbEntry.OriginalValues.GetValue<object>(propertyName) == null ? null : dbEntry.OriginalValues.GetValue<object>(propertyName).ToString(),
NewValue = dbEntry.CurrentValues.GetValue<object>(propertyName) == null ? null : dbEntry.CurrentValues.GetValue<object>(propertyName).ToString(),
Created_by = userId,
Created_date = changeTime
}
);
}
}
}
// Otherwise, don't do anything, we don't care about Unchanged or Detached entities
return result;
}
public DbSet<Log> Log { get; set; }
And here is the Log class
[Table("N_Log")]
public class Log
{
[Key]
public Guid LogID { get; set; }
[Required]
public string EventType { get; set; }
[Required]
public string TableName { get; set; }
public string ActionID { get; set; }
[Required]
public string RecordID { get; set; }
[Required]
public string ColumnName { get; set; }
public string OriginalValue { get; set; }
public string NewValue { get; set; }
[Required]
public string Created_by { get; set; }
[Required]
public DateTime Created_date { get; set; }
}

Querying distinct values

i am trying to get this to work for hours now. I tried the examples and read documentation but i just cant figure it out.
I want to query distinct values from the DB with a name field.
I think this should work but it does not. The method Distinct is not found
[HttpGet]
public IQueryable<MvlOP> MvlOpsPerson(long mvlId)
{
System.Data.Entity.Infrastructure.DbQuery<MvlOP> query = ContextProvider.Context.MvlOps;
query = query.Include("StatusOP");
return query.Where(op => op.MvlId == mvlId).Distinct(new PropertyComparer<MvlOP>("Id_P"));
}
I get the following error:
ExceptionMessage=LINQ to Entities does not recognize the method 'System.Linq.IQueryable`1[MAHN.Model.MvlOP] Distinct[MvlOP](System.Linq.IQueryable`1[MAHN.Model.MvlOP], System.Collections.Generic.IEqualityComparer`1[MAHN.Model.MvlOP])' method, and this method cannot be translated into a store expression.
ExceptionType=System.NotSupportedException
So this is wrong. As far as i understand it breeze does not offer querying distinct values. Querying all and filtering is not an option. Any help on how this can be done much appreciated.
I post this so someone who might need it can use it. Maybe this can be improved in some way. (Inspired by Breeze DocCode (thanks for the Partial Class Api Method in the Northwind Controller :) )
Querying distinct values can be done this way:
To be able to use the Distinct(IEqualityComparer) method the query has to be in Memory as an IEnumerable. An EqualityComparer cannot be translated into an SQL-Statement.
So the where clause applies and the resulting records are then filtered by the Comparer.
return query.AsQueryable(): To be able to use skip/take and inlineCount the query has to be an IQueryable<T>. Therefor the method AsQueryable().
//The API Method ----------
[HttpGet]
public IQueryable<MvlOPPersonPartial> MvlOpsPerson(long mvlId)
{
var query = (from op in ContextProvider.Context.MvlOps
where op.MvlId == mvlId
select new MvlOPPersonPartial()
{
MvlId = op.MvlId,
Kundenname = op.Kundenname,
Kundennummer = op.Kundennummer,
Id_P = op.Id_P
})
.AsEnumerable()
.Distinct(new PropertyComparer<MvlOPPersonPartial>("Id_P"));
return query.AsQueryable();
}
public class MvlOp
{
...
public string Kostenstelle { get; set; }
public string Betreuer_Id { get; set; }
public decimal? Id_P { get; set; }
public string Kundenname { get; set; }
public string Kundennummer { get; set; }
public string Person_Typ1 { get; set; }
...
}
//The partial class needed for distinct values ------------
//MvlOP cannot be constructed in an LINQ to Entities query
public class MvlOPPersonPartial
{
public long MvlId { get; set; }
public string Kundenname { get; set; }
public string Kundennummer { get; set; }
public decimal? Id_P { get; set; }
}
//A generic comparer ---------------
public class PropertyComparer<T> : IEqualityComparer<T>
{
private PropertyInfo _PropertyInfo;
/// <summary>
/// Creates a new instance of PropertyComparer.
/// </summary>
/// <param name="propertyName">The name of the property on type T
/// to perform the comparison on.</param>
public PropertyComparer(string propertyName)
{
//store a reference to the property info object for use during the comparison
_PropertyInfo = typeof(T).GetProperty(propertyName,
BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public);
if (_PropertyInfo == null)
{
throw new ArgumentException(string.Format("{0} is not a property of type {1}.", propertyName, typeof(T)));
}
}
#region IEqualityComparer<T> Members
public bool Equals(T x, T y)
{
//get the current value of the comparison property of x and of y
object xValue = _PropertyInfo.GetValue(x, null);
object yValue = _PropertyInfo.GetValue(y, null);
//if the xValue is null then we consider them equal if and only if yValue is null
if (xValue == null)
return yValue == null;
//use the default comparer for whatever type the comparison property is.
return xValue.Equals(yValue);
}
public int GetHashCode(T obj)
{
//get the value of the comparison property out of obj
object propertyValue = _PropertyInfo.GetValue(obj, null);
if (propertyValue == null)
return 0;
else
return propertyValue.GetHashCode();
}
#endregion
}
I think that the problem is that the Entity Framework (EF) cannot use your PropertyComparer. EF only supports Distinct without a comparer.

MVC LINQ to SQL JOIN for custom type fails for a strongly typed view

I have a simple data model of two tables, email and recipients, email can be sent to one or more recipients
I have setup the database with the two tables, created the Linq to SQL repository, built the controllers and the strongly typed view.
This works fine when I want to select all records from the database
public IList<AllMailDetail> ListAll()
{
var allMail =
from m in _datacontext.mail_receiveds
join r in _datacontext.mail_recipients on m.DeliveryId equals r.DeliveryId
select new AllMailDetail {
DeliveryId = m.DeliveryId,
MessageId = m.MessageId,
SentFrom = m.SentFrom,
FilePath = m.FilePath,
FileName = m.FileName,
SentDateTime = m.SentDateTime,
ReceivedDateTime = m.ReceivedDateTime,
Subject = m.Subject,
SpamScore = m.SpamScore,
IsSpam = m.IsSpam,
SenderIP = m.SenderIP,
Header = m.Header,
SentTo = r.SentTo
};
return allMail.ToList <AllMailDetail>();
}
The custom type class
public class AllMailDetail
{
public int DeliveryId { get; set; }
public int? MessageId { get; set; }
public string SentFrom { get; set; }
public string FilePath { get; set; }
public string FileName { get; set; }
public string SentDateTime { get; set; }
public DateTime ReceivedDateTime { get; set; }
public string Subject { get; set; }
public byte? SpamScore { get; set; }
public bool? IsSpam { get; set; }
public string SenderIP { get; set; }
public string Header { get; set; }
public string SentTo { get; set; }
}
The controller simply sends the contents from the repository to the strongly typed view
public ActionResult Index()
{
return View(_repository.ListAll());
}
To get just one mail record from the database I have the following code that accepts a deliveryId
public IQueryable<AllMailDetail> GetMail(int? id)
{
var allMail =
from m in _datacontext.mail_receiveds
join r in _datacontext.mail_recipients
on m.DeliveryId equals r.DeliveryId
where m.DeliveryId == id
select new AllMailDetail
{
DeliveryId = m.DeliveryId,
MessageId = m.MessageId,
SentFrom = m.SentFrom,
FilePath = m.FilePath,
FileName = m.FileName,
SentDateTime = m.SentDateTime,
ReceivedDateTime = m.ReceivedDateTime,
Subject = m.Subject,
SpamScore = m.SpamScore,
IsSpam = m.IsSpam,
SenderIP = m.SenderIP,
Header = m.Header,
SentTo = r.SentTo
};
return allMail;
}
And its controller code
public ActionResult Details(int? id)
{
var mail = _repository.GetMail(id);
if (mail == null)
return View("NotFound");
return View(mail);
}
I had been trying to display the output for a single record by also using a strongly typed view having Inherits="System.Web.Mvc.ViewPage At the top of the aspx page but I got the following error
The model item passed into the dictionary is of type 'System.Data.Linq.DataQuery`1[projectMail.Models.AllMailDetail]' but this dictionary requires a model item of type projectMail.Models.AllMailDetail'.
I fixed this error after much searching and found this post most helpful
MVC LINQ to SQL Table Join Record Display
so my view is no longer strongly typed and I build the page as follows
<% foreach (projectMail.Models.AllMailDetail item in (IEnumerable)ViewData.Model)
{ %>
...items...
<% } %>
This works fine, but it seems the long way round. The thing I can’t figure out is
Why does the second query need to be IQueryable
Why didn’t it work when the view was strongly typed
How can it be made to work with a strongly typed view
Is this the best way of dealing with joins in MVC using LINQ to SQL
Hmmmm, Try in the controller
return View(_repository.GetMail( id).SingleOrDefault());
You're trying to bind an IQueryable datasource to an AllMailDetail View, the above should fix you.

Resources