I'm writing unit test for ASP.MVC 3 app with EF 4.0 and I have problem with System.NullReferenceException during the testing. I'm testing this method in service layer:
public IQueryable<Pricing> GetPricing(int categoryID)
{
var query = from t in _repository.GetAllPricing()
where t.FK_Category == categoryID
where t.Status.Equals("1")
select t;
return query;
}
It's working fine. But when Status equals to null and I call
svc.GetPricing(1).Count();
in the test method, then it throws exception. I'm using fake repository and other (empty) string works well.
I've tried to use pricing.Status = Convert.ToString(null); instead of pricing.Status = null; but this doesn work either.
The problem is that you can't call .Equals on a null reference - it will as you've experienced throw a NullReferenceException.
Instead you can call the equality operator:
public IQueryable<Pricing> GetPricing(int categoryID)
{
var query = from t in _repository.GetAllPricing()
where t.FK_Category == categoryID
where t.Status == "1"
select t;
return query;
}
Related
I have a few methods that act on a query one that is for a simple where filter an the other with an expression to be used in the where filter and the third also has an expression as a parameter.
public static IQueryable<Employee> FilterExpression(this IQueryable<Employee> employees,
Expression<Func<Employee, Boolean>> expression)
{
return employees.Where(expression);
}
public static IQueryable<Employee> Active(this IQueryable<Employee> employees)
{
return employees.Where(e => e.IsActive);
}
The first two execute fine without any problems.
var active = db.Employees.Active().Take(5).ToList();
var activeExpression = db.Employees.Take(5).FilterExpression((e) => e.IsActive).ToList();
The next one is being called in another extension method that returns a delegate for a select on a tasks collection.
public static Expression<Func<SpwTask, TaskView>> Tasks(this TaskService service)
{
return (x) => new TaskView()
{
NotifyList = db.TaskContacts.JoinedEmployees
(t => t.TaskId == x.Id).Select(Projections.SimpleEmployees)
};
}
public static IQueryable<Employee> JoinedEmployees(this IQueryable<TaskContact> contacts,
Expression<Func<TaskContact, Boolean>> expression)
{
var id = ServicesRoot.Company.Id;
return from c in contacts.Where(expression)
join e in db.Employees on c.EmployeeId equals e.Id
where e.CompanyId == id
select e;
}
The calling code looks like this
// db is the DbContext and the Tasks is the extension method
...
return db.Tasks.Select(this.Tasks());
...
The error I get is this:
System.NotSupportedException: 'LINQ to Entities does not recognize the method 'System.Linq.IQueryable1[SafetyPlus.Data.Models.Employee] JoinedEmployees(System.Linq.IQueryable1[SafetyPlus.Data.TaskContact], System.Linq.Expressions.Expression1[System.Func2[SafetyPlus.Data.TaskContact,System.Boolean]])' method, and this method cannot be translated into a store expression.'
Are there any ways to work around this problem? It would be really nice to reused this an other similar queries. It seems like this should work.
Inline version:
public static Expression<Func<SpwTask, TaskView>> Tasks(this TaskService service)
{
return (x) => new TaskView()
{
NotifyList =
(from c in service.db.TaskContacts.
where c.TaskId == x.Id
join e in db.Employees on c.EmployeeId equals e.Id
where e.CompanyId == id
select e).Select(Projections.SimpleEmployees)
};
}
When I run the code inline version of the query I get a single SQL produced which is optimized and what I want but is not reusable.
I had thought the Compile() solution given below was the answer that I needed, at first, but upon looking at the SQL Profiler I realize that each row of the outer task query is running a single query for each task instead of the single query for the whole dataset. This pretty much defeats the purpose of reusing the query.
You should pass it as a delegate without invoking it in the Select and compile before use.
Try this:
db.Tasks.Select(Tasks.Compile());
I have the following ActionFilter class, to implement my custom authorization system:-
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class CheckUserPermissionsAttribute : ActionFilterAttribute
{
Repository repository = new Repository();
public string Model { get; set; }
public string Action { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string ADusername = filterContext.HttpContext.User.Identity.Name.Substring(filterContext.HttpContext.User.Identity.Name.IndexOf("\\") + 1);
if (!repository.can(ADusername,Model,Action))
{
filterContext.Result = new HttpUnauthorizedResult("You cannot access this page");
}
base.OnActionExecuting(filterContext);
}
}
The above class will call the following repository method:-
public bool can(string user, string Model, string Action)
{
bool result;
bool result2;
int size =tms.PermisionLevels.Where(a5 => a5.Name == Action).SingleOrDefault().PermisionSize;
var securityrole = tms.SecurityroleTypePermisions.Where(a => a.PermisionLevel.PermisionSize >= size && a.TechnologyType.Name == Model).Select(a => a.SecurityRole).Include(w=>w.Groups).Include(w2=>w2.SecurityRoleUsers).ToList();
foreach (var item in securityrole)
{
result = item.SecurityRoleUsers.Any(a => a.UserName.ToLower() == user.ToLower());
var no = item.Groups.Select(a=>a.TMSUserGroups.Where(a2=>a2.UserName.ToLower() == user.ToLower()));
result2 = no.Count() == 1;
if (result || result2) {
return true;
}}
return false;
}
But inside my repository method , I am doing the following:-
Query the database and include all the Groups & SecurityRoleUsers when executing the .tolist()
Then filter the returned records insdie the server, based on the foreach loop.
But this will cause the following drawbacks:-
If I have many Groups and SecurityRoleUsers, then I will be getting them all from the DB, and then filter the result on the server.
And since this code will be executed whenever an action method is called, as it Is a security attribute at the begging of the controller class. So this might not be very efficient.
So my question is whether I can join all the queries inside the repository method to be single query , and do all the work on the Database and just return true or false to the server ?
The associated tables looks as follow:-
Ideally remove this foreach.
Try riding with Linq to Sql.
You should be more comfortable because it resembles SQL.
This link has several examples.
http://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b
Att
Julio Spader
wessolucoes.com.br
Use linq.
Ideally you should only have one line of code after you got the size value. e.g.
int size =tms.PermisionLevels.Where(a5 => a5.Name == Action).SingleOrDefault().PermisionSize;
var result = //One line of code to determine user authenticity
return result;
I think you should design you database in the way that join queries are easy to do. So you don't have to perform more than one select.
Try code-first EF, which links tables very easily.
You need to take care with Lazy Loading. If not used correctly, it will make a query to the database each object segmentation, especially in your foreach. With that already has a good improvement.
Take a look at this article. I think it will help you too.
http://www.sql-server-performance.com/2012/entity-framework-performance-optimization/
Att
Julio Spader
wessolucoes.com.br
Hi,
I am new to Linq and entity framework. I am doing something like this
I have 3 viewmodel:
1.
public class FlowViewModel
{
..........................
public List<FlowLevelViewModel> Levels { get; set; }
}
public class FlowLevelViewModel
{
.........................
public List<BlockDetailsViewmodel> Blocks { get; set; }
}
public class BlockDetailsViewmodel
{
.......................
}
and from my controller I am calling the datalayer.
var model = new FlowViewModel();
model = dataOb.GetFlowForTheDocument(company, docType);
model = dataOb.GetFlowStageForTheDocument(model);
return model;
and in my datalayer
public FlowViewModel GetFlowStageForTheDocument(FlowViewModel model)
{
var flowlevelviewModel = (from p in dbContext.FlowStages
where p.FlowID == model.FlowId
select new FlowLevelViewModel()
{
.................
Blocks = GetBlockDetailsForTheDocument(p.StageID, .StageType)
}).ToList();
model.Levels = flowlevelviewModel;
return model;
}
public List<BlockDetailsViewmodel> GetBlockDetailsForTheDocument(int StageID, string stageType)
{
var blockDetails = new List<BlockDetailsViewmodel>();
......................................
return blockDetails;
}
While I am running the program I am getting this error:
**NotSupportedException Was unhandled by user Code**
LINQ to Entities does not recognize the method 'System.Collections.Generic.List`1[SEADViewModel.BlockDetailsViewmodel] GetBlockDetailsForTheDocument(Int32, System.String)' method, and this method cannot be translated into a store expression.
My project is in production stage so I have no time at all. Does anyone know what I am doing wrong?
This should solve your problem:
var data = (from p in dbContext.FlowStages
where p.FlowID == model.FlowId
select p).ToList();
var flowlevelviewModel = (from p in data
select new FlowLevelViewModel()
{
.................
Blocks = GetBlockDetailsForTheDocument(p.StageID, .StageType)
}).ToList();
Note that this will evaluate the query at the first ToList(). If you need to run the entire query at once, you need to build a simple LINQ expression, you can't use your method GetBlockDetailsForTheDocument inside the query. See #Tilak's answer for a link to supported build in methods.
You are using Linq to Entities.
It does not support all the functions. List of supported and non supported functions
You need to write custom model defined function GetBlockDetailsForTheDocument to use it in LINQ query.
I have a little issue, I'll try to explain this in detail.
Over my system I have a Generic Repository working with EF 4.1.
Everything works fantastic, but I have a problem in a certain situation I would need to do dynamic orderBy over some querys.
I recieve by parameter an "string" that represent the field on my class, to do the orderBy (like "id" or "description")
Some portion of code:
public class SomeClass
{
public int id { get; set; }
public string description { get; set; }
}
// First we define the parameter that we are going to use
// in our OrderBy clause. This is the same as "(parameter =>"
// in the example above.
var param = Expression.Parameter(typeof(SomeClass), "parameter");
// Now we'll make our lambda function that returns the
// request.SortingName property by it's name.
var expression = Expression.Lambda<Func<SomeClass, int>>(Expression.Property(param, request.SortingName), param);
Well, this code works if the "request.SortingName" is type "int" (id) , but if I want to make the orderBy by "string" (description) or another type this code doesn't work.
I changed the expression to using "object":
var expression = Expression.Lambda<Func<SomeClass, object>>(Expression.Property(param, request.SortingName), param);
But when I run the code, the compiler throws the next exception:
Expression of type 'System.Int32' cannot be used for return type 'System.Object'
In case the property is string type, the exception is
Expression of type 'System.String' cannot be used for return type 'System.Object'
In other words the code doesn't work with "object" type.
Anyone knows how can I figure this out?
Thanks for your time.
Here's how I do dynamic sort and paginate using EF4 and some generic methods I've created in my standard dev library. The important thing is the second bit of code which you use to create the Lambda expression for the SortBy method.
public enum SqlOrderByDirecton
{
ASC,
DESC
}
//Derive Lambda Expression from string
string sortByKey = "BusinessId";
string value = "DESC";
var p = Expression.Parameter(typeof(T));
this.SortBy = Expression.Lambda<Func<T, dynamic>>(Expression.TypeAs(Expression.Property(p, sortByKey), typeof(object)), p).Compile();
this.SortOrder = (DevCore.SqlOrderByDirecton)Enum.Parse(typeof(DevCore.SqlOrderByDirecton), value, true);
public static List<T> SortAndPaginate<T>(IEnumerable<T> query,
Func<T, object> sortBy,
SqlOrderByDirecton sortOrder,
int rowLimit,
int startRecord,
out int recordCount)
{
recordCount = query.Count();
List<T> list = new List<T>();
if (sortOrder == SqlOrderByDirecton.ASC)
{
list = query.OrderBy(sortBy).Skip(startRecord).Take(rowLimit).ToList();
}
else
{
list = query.OrderByDescending(sortBy).Skip(startRecord).Take(rowLimit).ToList();
}
return list;
}
I am using POCO objects in EF 4 without any T4 template generation.
I have a DataContext class that encapsulates all ObjectSets, something like this
public sealed class DataContext :IDisposable
{
public IObjectSet GetObjectSet() where T : MyBase
{
object objectSet = null;
this.objectSets.TryGetValue(typeof(T), out objectSet);
if (objectSet == null)
{
objectSet = this.context.CreateObjectSet();
this.objectSets.Add(typeof(T), objectSet);
}
return (IObjectSet)objectSet;
}
public ObjectContext ObjectContext
{
get
{
return this.context;
}
}
}
When i write the following compiled query and try to pass in this class as one of the parameters, it gives me a runtime error saying only scalar parameters are allowed
static readonly Func<ObjectContext , DataContext, string, int?> getOperationByOrchestrationName
= CompiledQuery.Compile(
(ObjectContext ctx, DataContext container, string name) =>
(from or in container.GetObjectSet<MyOrClass>()
join op in container.GetObjectSet<MyOpClass>()
on or.Id equals op.Id
where op.Name == name
select op.Id).FirstOrDefault()
);
If i modify the query like this it works, but i deeply suspect its being compiled every time, since i am not seeing the performance boost i would see from a compiled query, can someone point out whats going on ?
static readonly Func, IObjectSet, string, IQueryable>
getOperationByOrchestrationName
= CompiledQuery.Compile(
(ObjectContext ctx, IObjectSet ors, IObjectSet ops,string operationName) =>
from or in ors
join op in ops
on or.Id equals op.Id
where op.Name == name
select op.Id
);
for anyone interested, if you return IQueryable from compiled query and call any of the methods that can change the query ( singleordefault, or firstordefault etc), you would not get benefit of a compiled query.