LINQ - Dynamic expression to OrderBy - asp.net-mvc

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;
}

Related

MVC - validation attributes with error messages from database

I need to keep my translations in database so users can add, delete, and change them. I hold all of my translations in a table with composite primary key (variableName, culture), where variableName is just a name of some text which can have multiple translations and they correspond to the culture which is a string, like "en-US". So for example I have a variable "submitLogin" which I display on login button and there are three languages in my database: English, German, and Polish. This means I keep three rows in my table for that particular text: ("submitLogin", "en-US", "English translation), ("submitLogin", "de-DE", "German translation") and ("submitLogin", "pl-PL", "Polish translation").
So far my application has been based on a class Resources.cs which contains all translation variables from database, e.g.:
public static string buttonContinueShopping {
get {
return (string) resourceProvider.GetResource("buttonContinueShopping", CultureInfo.CurrentUICulture.Name);
}
}
In views I use these static properties to get my translations like this:
#Resources.buttonContinueShopping
I can create a dynamic type which will behave exactly the same way in views (except not having static properties but I can create an object on every view, that's not the problem - although it doesn't seem nice):
public class Resource : DynamicObject
{
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
ResourceManager rsManager = new ResourceManager();
result = rsManager.GetString(binder.Name);
return true;
}
}
But I have a problem with my models' attributes. So far I've used them like this:
[Required(ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "errorRequired")]
[DataType(DataType.EmailAddress, ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "errorWrongDataType")]
[EmailAddress]
[Display(Name = "nameEmail", ResourceType = typeof(Resources.Resources))]
public string Email { get; set; }
Now I have to get rid of my Resources.cs because they are generated after running a console program which reads all unique values of translation variables from database and creates properties (like the one I showed above). I cannot have this file anymore because users can add new translation variables in runtime.
How do I change as little as possible and make these attributes read error messages, display names etc. from database?
I have three ideas but I don't know how to get them done:
Use custom attributes - I tried it for Required attribute but it just doesn't add any client-side validation nor any error messages in HTML.
Use custom DataAnnotationsModelMetadataProvider - I tried this but it doesn't work after reloading the page - on the first page load all errors exist (particularly this: 'Field Email is required.') but after reloading the page, required error message changes to 'Field is required'. This is what I do:
public class CustomDataAnnotationsProvider: DataAnnotationsModelMetadataProvider
{
private ResourceManager resourceManager = new ResourceManager();
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
string key = string.Empty;
string localizedValue = string.Empty;
foreach (var attr in attributes)
{
if (attr != null)
{
if (attr is DisplayAttribute)
{
key = ((DisplayAttribute)attr).Name;
if (!string.IsNullOrEmpty(key) && !key.Contains(" "))
{
localizedValue = resourceManager.GetString(key);
((DisplayAttribute)attr).Name = localizedValue;
}
}
else if (attr is ValidationAttribute || attr is RequiredAttribute)
{
key = ((ValidationAttribute)attr).ErrorMessage;
if (!string.IsNullOrEmpty(key) && !key.Contains(" "))
{
localizedValue = resourceManager.GetString(key);
((ValidationAttribute)attr).ErrorMessage = localizedValue;
}
}
}
}
return base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
}
Global.asax:
ModelMetadataProviders.Current = new CustomDataAnnotationsProvider();
Model:
[Required(ErrorMessage = "errorRequired")]
[EmailAddress(ErrorMessage = "errorWrongDataType")]
[Display(Name = "nameEmail")]
public string Email { get; set; }
Use reflection (would be the best but I have no idea how to do it). Let's say I leave my attributes like that and remove all properties from my Resources.cs. Now what does RequiredAttribute do? It takes the type given and gets the property given, so e.g. It tries to do this:
Resources.Resources.nameEmail.get
The question is: is it possible to write some reflection code which would take care of 'requests' for non-existing properties (like nameEmail)?
I think the answer is to supply a default value in your Resources.cs file. Whilst users can supply translations dynamically, your application cannot use them unless they have the key, which you have used in the model.
If you modify your existing method to accept a default value then you can return this if there is not a database value present:
public static string buttonContinueShopping {
get {
return GetResource("buttonContinueShopping",
CultureInfo.CurrentUICulture.Name, "Continue Shopping");
}
}
public string GetResource(string key, string cultureName, string defaultText)
{
// Get db value
if (dbValue != null)
return dbValue;
return defaultText;
}
You control the keys by manually modifying them in Resources.cs which IMO is the best place for them, as they are maintained in the same project as they are being used. You can (and I have used this technique) then write a companion console app that can use reflection to generate the sql needed to update the database.
This example is taken directly from my project but you can get the idea
static void Main(string[] args)
{
var resources = typeof(Resources).GetProperties();
StreamWriter streamWriter = new StreamWriter(new FileStream(#"..\..\Resources.sql", FileMode.Create));
streamWriter.WriteLine(createTable);
for (int i = 0; i < resources.Count(); i++)
{
var line = GetValues(resources[i].Name, resources[i].GetValue(null, null) as string);
if (i == 0)
{
streamWriter.Write(insertValues + line);
}
else if (i == resources.Count() - 1)
{
streamWriter.Write(",\r\n" + line + "\r\nGO");
}
else if (i % 4 == 0)
{
streamWriter.Write("\r\nGO\r\n\r\n" + insertValues + line);
}
else
{
streamWriter.Write(",\r\n" + line);
}
}
streamWriter.Flush();
streamWriter.Close();
}
private static string createTable = "IF NOT EXISTS (SELECT * FROM sys.objects where Object_Id = OBJECT_ID(tempdb..#Resources))"
+ "\r\n\tCREATE TABLE Resources (StaticTextKey VARCHAR(100), DefaultText VARCHAR(MAX))\r\nGO\r\n";
private static string insertValues = "INSERT INTO #Resources (StaticTextKey, DefaultText) VALUES\r\n";
private static string GetValues(string staticTextKey, string defaultText)
{
return string.Format("('{0}', '{1}')", staticTextKey, defaultText.Replace("'", "''"));
}

Want to save selected (i.e., more than 1) enums as string with NHibernate

I cannot for the life of me get this to work with my existing code, but I am trying to save my enum selections as strings in NHibernate. Basically, I have a UI check box and if the user selects multiple check boxes I want to store those selections. Now, I can get NHibernate to store ONE selection (e.g., from a drop down or radio button list, where the user is limited to one choice only).
This is the jist of what I have for an enum:
public enum IncomeType
{
[Display(Name = "Full-Time Employment")]
FullTime,
[Display(Name = "Part-Time Employment")]
PartTime,
[Display(Name = "Self-Employment")]
SelfEmployed,
[Display(Name = "Rental")]
Rental,
[Display(Name = "Social Security Payments")]
SocialSecurity,
[Display(Name = "Retirement / Pension Payments")]
Retirement,
[Display(Name = "Child Support Payments")]
ChildSupport,
[Display(Name = "Spousal Maintenance")]
Maintenance,
[Display(Name = "Other")]
Other
}
I use a method to "select" whether a checkbox list is shown (if my BulkItemThreshold equals the number of options, a checkbox list is displayed). Here is that method:
public static IEnumerable<SelectListItem> GetItemsFromEnumString<T>
(T selectedValue = default(T)) where T : struct
{
return from name in Enum.GetNames(typeof(T))
let enumValue = Convert.ToString((T)Enum.Parse(typeof(T), name, true))
select new SelectListItem
{
Text = GetEnumDescription(name, typeof(T)),
Value = enumValue,
Selected = enumValue.Equals(selectedValue)
};
}
(Note: some items in there are helpers, but I don't believe they are relevant; also, the selected input is displayed using a template .cshtml file - again, not sure if that's relevant)
Now, I call this thusly:
public class IncomeTypeSelectorAttribute : SelectorAttribute
{
public override IEnumerable<SelectListItem> GetItems()
{
return Selector.GetItemsFromEnumString<IncomeType>();
}
}
And finally, we get to the virtual property (using a proxy) but this is where NHibernate throws a wrench (Note: this was working fine for me before NHibernate, and now I am trying to get many lines of code working with it WITHOUT having to re-do everything; if I re-do everything I will probably triple the code I already have to get it to work):
Property (record):
[IncomeTypeSelector(BulkSelectionThreshold = 9)]
public virtual List<string> IndividualIncomeTypeCheckBox { get; set; }
proxy (part):
public List<string> IndividualIncomeTypeCheckBox
{
get { return Record.IndividualIncomeTypeCheckBox; }
set { Record.IndividualIncomeTypeCheckBox = value; }
}
Again, this is how I was doing things and it was working great before NHibernate. But now I have to use NHibernate. No getting around it.
I am using a service class that it tying the two together in a Create method to save in the DB with NHibernate, and for the above it would ordinarily look like this:
part.IndividualIncomeTypeCheckBox = record.IndividualIncomeTypeCheckBox;
This would work if it were just one selection.
Well, I've spent a good two (2) months trying to get this to work. It's tough because I have lots of code where the user can make only one selection (such as with a radiobutton list) and it works GREAT - even with NHibernate. Let me give you an example:
public virtual IncomeType? IndividualIncomeTypeCheckBox { get; set; }
If I do the above, it will display a drop down list, and NHibernate will store the ONE allowable option selected by the user in the DB no problem. But more than one option with List<string> does not work.
Now, I have tried everything I could find here or elsewhere and nothing works. Yes, I know it should be IList<IncomeType> or some other variant. But if I use this then NHibernate requires that IncomeType be another table in the DB. This is too much code to write for such a simple thing I believe. We are not talking a many-to-many relation in the sense that this is not a User with Multiple addresses (wherein addresses would have street, city, state, zip, etc.).
I have tried different types of proxy get and set code, but nothing works. I have tried [Flags] and other things working with string only, but to no avail. Those last solutions would "work" but ONLY to save the first item selected out of multiple (i.e., in my scenario, if the user selected "FullTime" and "Rental" as Income Types, then only "FullTime" (string) would be saved or "1" ([Flags]/int), not both items selected.
I have a situation where I re-display the choices using a ReadOnly attribute like this:
[IncomeTypeSelector]
[ReadOnly(true)]
public List<string> IndividualIncomeTypeCheckBoxPost
{
get { return IndividualIncomeTypeCheckBox; }
}
This would display on the UI, but I tried doing something like this with NHibernate and it wouldn't work.
Could anyone please show me, using the above, how I can go about getting NHibernate to store more than one enum in this checkbox list scenario?
UPDATE:
More poking around here and on the web, and I came up with the following (which still does not work).
Property (record):
[IncomeTypeSelector(BulkSelectionThreshold = 9)]
public virtual IList<IncomeTypeRecord> IndividualIncomeTypeCheckBox
{
get { return incomeType; }
set { incomeType= value; }
}
private IList<IncomeTypeRecord> incomeType =
new List<IncomeTypeRecord>();
Proxy (part):
public IList<IncomeTypeRecord> IndividualIncomeTypeCheckBox
{
get { return Record.IndividualIncomeTypeCheckBox; }
set { Record.IndividualIncomeTypeCheckBox= value; }
}
And a change to the enum:
public enum IncomeType : int // removing int & value still gives validate error
{
[Display(Name = "Full-Time Employment")]
FullTime = 1,
[Display(Name = "Part-Time Employment")]
PartTime,
....
}
And I added this class to support IncomeTypeRecord
public class IncomeTypeRecord
{
public virtual int Id { get; set; }
public virtual IncomeType Value { get; set; }
}
HOWEVER, when I get to the UI screen and pick one or more options I get a validation error (value not valid). For example, say I pick FullTime alone, or pick FullTime and Retirement, then the UI will display the following error:
The value 'FullTime' is invalid.
The value 'FullTime,Retirement' is invalid.
(respectively)
Even if I remove the int declaration for the enum and get rid of the value I started with "1", I still get this validation error. I tried messing around with and adding different model binders (which now has me stumped as to whether my original problem still exists and now I have a different problem - but you still get bounty points :) ).
Pulling my hair out. If I could offer more bounty I would. I need a definitive solution. I appreciate any help.
UPDATE
This is what I have so far:
Record:
public virtual string IndividualIncomeTypeCheckBox{ get; set; }
Part:
//If I do IEnumberable<string> my .Select throws a cast error
public IEnumerable<IncomeType> IndividualIncomeTypeCheckBox
{
get
{
return Record
.IndividualIncomeTypeCheckBox
.Split(',')
.Select(r => (IncomeType)Enum.Parse(typeof(IncomeType), r));
}
set { Record.IndividualIncomeTypeCheckBox= value
== null ? null : String.Join(",", value); }
}
Service Class:
public SimplePart CreateSimple(SimplePartRecord record)
{
SimplePart simple = Services.ContentManager.Create<SimplePart>("Simple");
...
//How I would save a FirstName property (example Part / PartRecord below)
//public virtual string FirstName { get; set; } - PartRecord
//public string FirstName - Part
//{
// get { return Record.FirstName ; }
// set { Record.FirstName= value; }
//}
simple.FirstName = record.FristName;
...
//I obviously cannot do the following with the above IncomeType
//Getting cannot convert string to IEnumerable error
//How would I write this:
simple.IndividualIncomeTypeCheckBox = record.IndividualIncomeTypeCheckBox;
...
}
And this is how it's called in a controller (this persists to DB): (Updating Controller code)
public ActionResult Confirm(string backButton, string nextButton)
{
if (backButton != null)
return RedirectToAction("WrapUp");
else if ((nextButton != null) && ModelState.IsValid)
{
_myService.CreateSimple(myData.SimplePartRecord);
return RedirectToAction("Submitted");
}
else
return View(myData);
}
Updating with additional code (serialization and view model):
"myData" is defined in the controller (using Serialization) as:
private MyViewModel myData;
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var serialized = Request.Form["myData"];
if (serialized != null)
{
myData = (MyViewModel)new MvcSerializer().Deserialize
(serialized, SerializationMode.Signed);
TryUpdateModel(myData);
}
else
myData = (MyViewModel)TempData["myData"] ?? new MyViewModel();
TempData.Keep();
}
protected override void OnResultExecuted(ResultExecutedContext filterContext)
{
if (filterContext.Result is RedirectToRouteResult)
TempData["myData"] = myData;
}
I use Serialization because I set up a multi-step wizard (as seen in the controller action "backButton" "nextButton) on the front-end. I am not using a driver (which can only display Admin or on the front-end but then only on .cshtml files directly under the ~/Views folder (not in a structured folder list like I am using)). No driver = no update view model type code = no mechanism to "create" the data in the DB. If I do not use some "create" type method, the form will submit but all the data will be "NULL".
When you say that the data should be persisted automatically, I am sorry but I do not see how. All the stuff I read or code I review has SOME method of updating the DB with whatever is entered in a form. If I am missing something, my apologies.
"MyViewModel" is pretty straightforward:
[Serializabel]
public class MyViewModel
{
public SimplePartRecord SimplePartRecord { get; set; }
}
And, just in case, here is the relevant portion of the migration (return 1 is a completely separate and unrelated table):
public int UpdateFrom1()
{
SchemaBuilder.CreateTable("SimplePartRecord",
table => table
.ContentPartRecord()
...
.Column("IndividualIncomeTypeCheckBox", DbType.String)
...
);
ContentDefinitionManager.AlterPartDefinition("SimplePart",
part => part
.Attachable(false));
return 2;
}
The error I am getting is
Cannot implicitly convert type 'string' to 'System.Collections.Generic.IEnumerable'"
when I do the following in the "Create" method of my service class:
simple.IndividualIncomeTypeCheckBox = record.IndividualIncomeTypeCheckBox;
One additional thought: I tried using the n-n Relation sample to handle this scenario. Aside from it being a lot of extra code for what I thought should be straightforward and simple, because of the way I am using Serialization I had a lot of object reference errors and could not figure out how to properly code my controller to handle it.
There's a lot of info to wade through here so hopefully I haven't missed the point. It appears to me that the goals are:
The business class has a collection property of IList<IncomeType> without requiring an additional table
The values in that collection should be persisted as a delimited string of the enum names
The best approach is to use a custom user type (an implementation of NHibernate.UserTypes.IUserType) to map the property. Below is a generic IUserType that will map an enum of type T from an IList<T> property to a comma delimited string in the database and back again. There's no easy way to restrict T to an enum but the code will only work with enums.
Mapping a property using the custom type is simple with Fluent NHibernate:
public class Person
{
public Person()
{
IncomeTypes = new List<IncomeType>();
}
public virtual int PersonId { get; protected set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual IList<IncomeType> IncomeTypes { get; protected set; }
}
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Table("Person");
Id(x => x.PersonId).GeneratedBy.Identity();
Map(x => x.FirstName);
Map(x => x.LastName);
Map(x => x.IncomeTypes).CustomType<EnumAsDelimitedStringType<IncomeType>>();
}
}
And here's the code for the user type:
public class EnumAsDelimitedStringType<T> : IUserType
{
public new bool Equals(object x, object y)
{
if (ReferenceEquals(x, y))
{
return true;
}
var xList = x as IList<T>;
var yList = y as IList<T>;
if (xList == null || yList == null)
{
return false;
}
// compare set contents
return xList.OrderBy(xValue => xValue).SequenceEqual(yList.OrderBy(yValue => yValue));
}
public int GetHashCode(object x)
{
return x.GetHashCode();
}
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
var outValue = NHibernateUtil.AnsiString.NullSafeGet(rs, names[0]) as string;
if (string.IsNullOrEmpty(outValue))
{
return new List<T>();
}
var getValueArray = outValue.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries);
return Array.ConvertAll(getValueArray, s => (T)Enum.Parse(typeof(T), s)).ToList();
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
var inValue = value as IList<T>;
// set to string.Empty if you prefer to store that instead of null when the collection is null or empty
object setValue = null;
if (inValue != null && inValue.Any())
{
var setValueArray = Array.ConvertAll(inValue.ToArray(), v => Enum.GetName(typeof(T), v));
setValue = string.Join(",", setValueArray);
}
NHibernateUtil.AnsiString.NullSafeSet(cmd, setValue, index);
}
public object DeepCopy(object value)
{
return value;
}
public object Replace(object original, object target, object owner)
{
return original;
}
public object Assemble(object cached, object owner)
{
return cached;
}
public object Disassemble(object value)
{
return value;
}
public SqlType[] SqlTypes
{
get { return new[] {new SqlType(DbType.AnsiString)}; }
}
public Type ReturnedType
{
get { return typeof(IList<T>); }
}
public bool IsMutable
{
get { return false; }
}
}
I think you're on the right track pursuing a [Flags] enum. You may have done this, but just in case -- making an enum flags-worthy is more than adding the attribute. You also have to specify the value for the items in a binary-friendly manner. I've found the easiest way to do this is as follows:
[Flags]
public enum IncomeType : long // you'll need the room with several options
{
FullTime = 1,
PartTime = 1 << 1,
SelfEmployed = 1 << 2
// And so on
}
If you don't do this, then you'll get sequential integer values, which breaks the bitwise comparison that allows you to do multiple values in a single integer.
Your code to create the SelectList looks fine. Your options should construct form values that get posted back with the same name. If you want to use the default modelbinder, that means the associated property on your view model would need to be List<int>. If you're not using a view model (you probably should) you can pull it out of the forms collection.
Once you have this set up, then translating from your view model to your NHibernate entity is simple if a little annoying. You basically have to cycle through the values in the list and |= them onto your NHibernate entity's single enum property.
So let's assume you have a view model like this:
public class MyEditViewModel
{
public string Name { get; set; }
public List<int> IncomeSelections { get; set; }
// You'll probably have this to populate the initial view rendering
public SelectList AllIncomeOptions { get; set; }
}
You'll build your view using your helpers and all that, then build the checkboxes using the SelectList but making sure the input name is IncomeSelections, then when it's posted back you will push the view model data into your NHibernate entity something like this:
var myNHEntity = new NHEntity();
// If you're editing an existing entity, then be sure to reset the enum
// value to 0 before going into the following foreach loop...
foreach (var incomeSelection in viewModel.IncomeSelections)
{
myNHEntity.IncomeSelection |= incomeSelection;
}
There's probably a more clever way to do this, and you might have to cast the int to your enum type, but you'll figure that out (I'd do it for you, but it is Friday and I already have a beer open).
NHibernate should persist it without you having to do anything funky on the NH side.
In summary...
It seems like this is more a problem of how you handle the posted data than the NHibernate side. If you implement something like this, then be sure to use Fiddler or FireBug to inspect the posted values to make sure 1) they're integers and 2) the names are the same so they'll be added to the list.
Good luck!
The problem is simply that it won't be able to map a List without building a full relationship with an intermediate association table. It is way simpler to have the record store the values as a comma-separated string (so your record property is a string, not a list of string) and your part can map back and forth between string and List.
You can find an example of something very close here:
https://bitbucket.org/bleroy/nwazet.commerce/src/d722cbebea525203b22c445905c9f28d2af7db46/Models/ProductAttributesPartRecord.cs?at=default
https://bitbucket.org/bleroy/nwazet.commerce/src/d722cbebea525203b22c445905c9f28d2af7db46/Models/ProductAttributesPart.cs?at=default
It's not using enum values, instead it's a list of ids, but that should give you a good idea about how to make this work fairly simply: parsing enums you already know how to do.
Let me know if you need more details, but I think that's what you needed to get unblocked.

Linq casting Issue in EntityFramework

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.

Unit test NullReferenceException with string property problem

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;
}

Linq to SQL using Repository Pattern: Object has no supported translation to SQL

I have been scratching my head all morning behind this but still haven't been able to figure out what might be causing this.
I have a composite repository object that references two other repositories. I'm trying to instantiate a Model type in my LINQ query (see first code snippet).
public class SqlCommunityRepository : ICommunityRepository
{
private WebDataContext _ctx;
private IMarketRepository _marketRepository;
private IStateRepository _stateRepository;
public SqlCommunityRepository(WebDataContext ctx, IStateRepository stateRepository, IMarketRepository marketRepository)
{
_ctx = ctx;
_stateRepository = stateRepository;
_marketRepository = marketRepository;
}
public IQueryable<Model.Community> Communities
{
get
{
return (from comm in _ctx.Communities
select new Model.Community
{
CommunityId = comm.CommunityId,
CommunityName = comm.CommunityName,
City = comm.City,
PostalCode = comm.PostalCode,
Market = _marketRepository.GetMarket(comm.MarketId),
State = _stateRepository.GetState(comm.State)
}
);
}
}
}
The repository objects that I'm passing in look like this
public class SqlStateRepository : IStateRepository
{
private WebDataContext _ctx;
public SqlStateRepository(WebDataContext ctx)
{
_ctx = ctx;
}
public IQueryable<Model.State> States
{
get
{
return from state in _ctx.States
select new Model.State()
{
StateId = state.StateId,
StateName = state.StateName
};
}
}
public Model.State GetState(string stateName)
{
var s = (from state in States
where state.StateName.ToLower() == stateName
select state).FirstOrDefault();
return new Model.State()
{
StateId = s.StateId,
StateName = s.StateName
};
}
AND
public class SqlMarketRepository : IMarketRepository
{
private WebDataContext _ctx;
public SqlMarketRepository(WebDataContext ctx)
{
_ctx = ctx;
}
public IQueryable<Model.Market> Markets
{
get
{
return from market in _ctx.Markets
select new Model.Market()
{
MarketId = market.MarketId,
MarketName = market.MarketName,
StateId = market.StateId
};
}
}
public Model.Market GetMarket(int marketId)
{
return (from market in Markets
where market.MarketId == marketId
select market).FirstOrDefault();
}
}
This is how I'm wiring it all up:
WebDataContext ctx = new WebDataContext();
IMarketRepository mr = new SqlMarketRepository(ctx);
IStateRepository sr = new SqlStateRepository(ctx);
ICommunityRepository cr = new SqlCommunityRepository(ctx, sr, mr);
int commCount = cr.Communities.Count();
The last line in the above snippet is where it fails. When I debug through the instantiation (new Model.Community), it never goes into any of the other repository methods. I do not have a relationship between the underlying tables behind these three objects. Would this be the reason that LINQ to SQL is not able to build the expression tree right?
These are non-hydrated queries, not fully-hydrated collections.
The Communities query differs from the other two because it calls methods as objects are hydrated. These method calls are not translatable to SQL.
Normally this isn't a problem. For example: if you say Communities.ToList(), it will work and the methods will be called from the objects as they are hydrated.
If you modify the query such that the objects aren't hydrated, for example: when you say Communities.Count(), linq to sql attempts to send the method calls into the database and throws since it cannot. It does this even though those method calls ultimately would not affect the resulting count.
The simplest fix (if you truly expect fully hydrated collections) is to add ToList to the community query, hydrating it.
Try adding another repository method that looks like this:
public int CommunitiesCount()
{
get { return _ctx.Communities.Count(); }
}
This will allow you to return a count without exposing the entire object tree to the user, which is what I think you're trying to do anyway.
As you may have already guessed, I suspect that what you are calling the anonymous types are at fault (they're not really anonymous types; they are actual objects, which you are apparently partially populating in an effort to hide some of the fields from the end user).

Resources