Object initialization syntax - syntax

I'm just starting out with F# and I can't find the syntax to do object initialization like in C# 3.
I.e. given this:
public class Person {
public DateTime BirthDate { get; set; }
public string Name { get; set; }
}
how do I write the following in F#:
var p = new Person { Name = "John", BirthDate = DateTime.Now };

You can do it like this:
let p = new Person (Name = "John", BirthDate = DateTime.Now)

the answer from CMS is definitely correct. Here is just one addition that may be also helpful. In F#, you often want to write the type just using immutable properties. When using the "object initializer" syntax, the properties have to be mutable. An alternative in F# is to use named arguments, which gives you a similar syntax, but keeps things immutable:
type Person(name:string, ?birthDate) =
member x.Name = name
member x.BirthDate = defaultArg birthDate System.DateTime.MinValue
Now we can write:
let p1 = new Person(name="John", birthDate=DateTime.Now)
let p2 = new Person(name="John")
The code requires you to specify the name, but birthday is an optional argument with some default value.

You can also omit the new keyword and use less verbose syntax:
let p = Person(BirthDate = DateTime.Now, Name = "John")
https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/members/constructors

Related

The query specified in the URI is not valid + Projection + LINQ Select

I apologize if this is a repetitive question. Basically what I am trying to achieve is to have my projection is a separate method/class which I can reuse like this (keep in mind I am beginner with LINQ).
public static Expression<Func<MyObject, object>> getProjection()
{
return r => new
{
Name = r.Name,
Address = r.Address,
City = r.City,
PostalCode = r.PostalCode,
Province = r.Province,
Country = r.Country,
Phone = r.Phone,
Website = r.Website
};
}
However, when I call Projection like this.
var filteredList = db.MyObject.Select(Projections.getProjection()).AsQueryable();
return Ok(filteredList);
Then I get the error
The query specified in the URI is not valid. Could not find a property
named 'Name' on type 'System.Object'.
If I replace the Projection helper method with actual Projection just by copy and pasting then it works. I am just trying to avoid rewriting the same projection again for other Select methods by creating a helper method "getProjection". First if you can verify if this is the right way of calling Projection. Secondly how can I get rid of that OData error.
Thanks
LINQ to Entities needs strong types. They can be generic, and the CAN be anonymous. Your problem is that your function returns a weak type Expression>.
Use a strong type to solve this, or don't use a projection method.
A: Strong type. This is actually quite tidy, and could be considered lazy to try and get away with an anonymous type here.
public class MyRecord { /* fields here */ }
public static Expression<Func<MyObject, MyRecord>> getProjection()
{
return r => new MyRecord
{
Name = r.Name,
Address = r.Address,
City = r.City,
PostalCode = r.PostalCode,
Province = r.Province,
Country = r.Country,
Phone = r.Phone,
Website = r.Website
};
}
/* of type IQueryable<MyRecord> */
var filteredList = db.MyObject.Select(getProjection());
B: Remove the projection method:
/* of type IQueryable<Anonymous> - this is why 'var' exists */
var filteredList = db.MyObject.Select(r => new
{
Name = r.Name,
Address = r.Address,
City = r.City,
PostalCode = r.PostalCode,
Province = r.Province,
Country = r.Country,
Phone = r.Phone,
Website = r.Website
});
Note this if you intend to return this to another method, you're still going to need a strong (non-anonymous) type. The only way you can pass anonymous types through methods is via generics. e.g:
function T Do<T>(func<T> something) { return something(); }
var anon = Do(() => { a = 1, b = 2 });
C: If you do this projection often AND you DON'T want to create projection classes then ask yourself 'why not?'. If you want to avoid writing this project code often and are happy to create projection classes then consider using a tool such as AutoMapper. It's use is pretty common nowadays.
So, what if you will try to use simple method instead of expression?
E.g. :
public static object GetProjection(MyObject o){
return new{
Name = o.Name,
Address = o.Address,
City = o.City,
PostalCode = o.PostalCode,
Province = o.Province,
Country = o.Country,
Phone = o.Phone,
Website = o.Website
};
}
And then in your controller:
var filteredList = db.MyObject.Select(GetProjection).AsQueryable();
return Ok(filteredList);

coding to an interface, in a list - syntax

I have extracted some of my concrete classes into interfaces
I used to have a class called City, and it implements interface ICity
now i tried to do the following
public List<ICity> Cities { get; private set; }
var efCities = (from c in myentity.Cities
orderby c.CityName
select c);
Cities = (efCities.Select(o => new City() { Id = o.Id, Country = o.Country,
Province = o.Province, CityName = o.CityName }).ToList());
I get tuned the following:
Cannot implicitly convert type 'System.Collections.Generic.List<City>'
to 'System.Collections.Generic.List<ICity>'
as i understood it, since City implements ICity, i should be fine, no?
Isn't what I am doing in the same vein as going:
ICity c = new City();
No one has really said the reason why this doesn't work. Suppose Apple and Orange both implement IFruit:
List<Orange> oranges = new List<Orange>();
List<IFruit> fruits = oranges; // You are trying to do this, which is illegal.
// Suppose it were legal. Then you could do this:
fruits.Add(new Apple());
Because you can add an apple to a list of fruits, but that list is really a list of oranges! You just put an apple into a list of oranges, and apples are not oranges.
The C# compiler knows that this could happen, so it disallows it. Unfortunately, it does not disallow that for arrays:
Orange[] oranges = new Orange[1];
IFruit[] fruits = oranges; // dangerous, but legal!
fruits[0] = new Apple(); // legal at compile time, crashes at runtime.
This is a form of unsafe covariance. We decided to not allow the same dangerous pattern for interfaces; interfaces can only be covariant if the compiler can prove that such an error is impossible.
Unfortunately, generic type parameters do not follow the same typecasting rules as stand-alone types. They are restricted by what the generic type says it allows; this is called covariance and contravariance and, in C#, only arrays, interfaces and delegates can be covariant or contravariant. Concrete types like List cannot be (at least, as of C# 4.0).
(The reason generics don't work the way you think in general is because its impossible to know what the generic type does with its type parameters; covariance is intuitive because that's how simple assignments works, but in many cases what we really want is contravariance; since the compiler cannot make the decision for us, it defaults to neither unless you say otherwise.)
For more information on co/contravariance in C# 4 I'd recommend you check out Eric Lippert's series of posts about it, in particular:
http://blogs.msdn.com/b/ericlippert/archive/2009/11/30/what-s-the-difference-between-covariance-and-assignment-compatibility.aspx
and the MSDN article about it:
http://msdn.microsoft.com/en-us/library/dd799517.aspx
Fortunately, in this case there is a simple answer, the explicit IEnumerable.Cast method:
Cities = (efCities.Select(o => new City() { Id = o.Id, Country = o.Country,
Province = o.Province, CityName = o.CityName }).Cast<ICity>.ToList());
Another option is to use IEnumerable<T> instead of List<T>. IEnumerable<T> is covariant in T so your assignment would work:
interface IA
{
int Foo();
}
class A : IA
{
public int Foo()
{
return 0;
}
}
public DoStuff()
{
List<A> la = new List<A> { new A(), new A(), new A(), new A() };
// This is an error -- List<A> is not covariant with List<IA>
// List<IA> lia = la;
// This is fine; List<A> implements IEnumerable<A>
IEnumerable<A> iea = la;
// Also fine: IEnumerable<A> is covariant with IEnumerable<IA>
IEnumerable<IA> ieia = la;
}
It is not same as
ICity c = new City();
List<ICity> and List<City> are tehmselves types, and List<City> is not derived from List<ICity>.
Adding a cast to the selection would solve the problem:
Cities = (efCities.Select(o => (ICity)(new City() { Id = o.Id, Country = o.Country,
Province = o.Province, CityName = o.CityName })).ToList());
No. List<City> is not that same as List<ICity>. Instead of assigning the select.toList(); to cities try something like:
Cities.AddRange((efCities.Select(o => new City() { Id = o.Id, Country = o.Country, Province = o.Province, CityName = o.CityName }))

MvcContrib Grid Sorting on complex object

I am trying to work with MvcContrib Grid control. But I cannot seem to get the sorting to work on complex objects that hold other objects.
I have setup my controller/classes/Views similar to the OP in this question.
Sorting with MVCContrib
I have tried to use the SortColumnName to my childobject.property but it gives me an error saying My main object does not have this property. This is my code snippet
//POCO class
class Issue {
public int ID {get; get; }
.....
public int priorityId {get; set;}
public virtual Priority priority {get; set;}
}
//Controller code
public ViewResult Index(int? pageNo, GridSortOptions sort)
{
var issues = db.issues.Include(i => i.priority);
ViewBag.sort = sort;
if (!string.IsNullOrEmpty(sort.Column))
{
issues = issues.OrderBy(sort.Column, sort.Direction);
}
return View(issues.ToList().AsPagination(pageNo ?? 1, 10));
}
//View code for the Grid
#Html.Grid(Model).Sort(ViewBag.sort as GridSortOptions).Columns(column => {
column.For(issue => Html.ActionLink(" ", "Edit", new { id = issue.ID, areas = "Issues", controller = "Main"}, new { #id="editBtn"})).Named("Edit");
column.For(issue => Html.ActionLink(issue.ID.ToString(), "Edit", new {id = issue.ID, areas = "Issues", controller = "Main"})).Named("ID").Sortable(true);
column.For(issue => issue.priority.codeDesc).Named("Priority").SortColumnName("priority.codeDesc").Sortable(true);
}).Empty("No data found")
When I try to sort on the priority string, it gives me an error saying 'priority.codeDesc is not a property of Issue'.
TIA
The issue here isn't actually related to the grid, but rather to the .OrderBy extension method provided as part of the MvcContrib sorting extensions. This extension is fairly simplistic and I only wrote it to cover simple cases where you want to sort on a direct property of the object, however in your case you're trying to order on a nested property ("priority.codeDesc") which isn't supported - you can't use dot notation with this extension.
You'd either need to switch to using a different mechanism to perform the actual sorting, or if this is a one-off situation then you could hard-code the sorting logic for this particular column (not ideal, but if it's a one off then it's simpler than writing a new sorting mechanism), eg:
if (!string.IsNullOrEmpty(sort.Column))
{
if(sort.Column == "priority.codeDesc")
{
issues = issues.OrderBy(x => x.priority.codeDesc);
}
else
{
issues = issues.OrderBy(sort.Column, sort.Direction);
}
}
OMG! Dots!
I was in the same boat but thanks God I found a brilliant solution posted by our fellow developer Jarrett Meyer. I found it after maybe 3 hours Googling in the past and just now when I decided to boost my pagination and sorting with MvcContrib Grid.
You can find the full post here:
Server-Side Sorting With Dynamic LINQ
His code saved me... :D The use of LINQ's Aggregate function was AWESOME! Kudozzz to him.
I had to change Jarretts' original code a little bit to fit it to my needs. Here's the code after I modified it:
public static IQueryable<T> OrderBy<T>(this IQueryable<T> collection, GridSortOptions sortOptions)
{
if (string.IsNullOrEmpty(sortOptions.Column))
{
return collection;
}
Type collectionType = typeof(T);
ParameterExpression parameterExpression = Expression.Parameter(collectionType, "p");
Expression seedExpression = parameterExpression;
Expression aggregateExpression = sortOptions.Column.Split('.').Aggregate(seedExpression, Expression.Property);
MemberExpression memberExpression = aggregateExpression as MemberExpression;
if (memberExpression == null)
{
throw new NullReferenceException(string.Format("Unable to cast Member Expression for given path: {0}.", sortOptions.Column));
}
LambdaExpression orderByExp = Expression.Lambda(memberExpression, parameterExpression);
const string orderBy = "OrderBy";
const string orderByDesc = "OrderByDescending";
Type childPropertyType = ((PropertyInfo)(memberExpression.Member)).PropertyType;
string methodToInvoke = sortOptions.Direction == MvcContrib.Sorting.SortDirection.Ascending ? orderBy : orderByDesc;
var orderByCall = Expression.Call(typeof(Queryable), methodToInvoke, new[] { collectionType, childPropertyType }, collection.Expression, Expression.Quote(orderByExp));
return collection.Provider.CreateQuery<T>(orderByCall);
}
Now you can call this extension method like this in your controller method:
var users = Database.Memberships.OrderBy(sort);
where sort is GridSortOptions that lives in MvcContrib.UI.Grid.
sort.ColumnName can contain strings like these ones now:
User.UserName
User.MyRelatedEntity.RelatedEntityProperty
User.MyRelatedEntity.RelatedEntityProperty.AndSoON
Note that when you create your Grid columns you can specify
.SortColumnName("User.UserName")

IN and NOT IN with Linq to Entities (EF4.0)

This has been ruining my life for a few days now, time to ask...
I am using Entity Framework 4.0 for my app.
A Location (such as a house or office) has one or more facilities (like a bathroom, bedroom, snooker table etc..)
I want to display a checkbox list on the location page, with a checkbox list of facilities, with the ones checked that the location currently has.
My View Model for the facilities goes like this...
public class FacilityViewItem
{
public int Id { get; set; }
public string Name { get; set; }
public bool Checked { get; set; }
}
So when im passing the Location View Model to the UI, i want to pass a List<T> of facilities where T is of type FacilityViewItem.
To get the facilities that the location already has is simple - i make a query using Location.Facilities which returns an EntityCollection where T is of type Facility. This is because Facilities is a navigation property....
var facs = from f in location.Facilities
select new FacilityViewItem()
{
Id = f.FacilityId,
Name = f.Name,
Checked = true
};
So here is where my problem lies - i want the rest of the facilities, the ones that the Location does not have.
I have tried using Except() and Any() and Contains() but i get the same error.
Examples of queries that do not work...
var restOfFacilities = from f in ctx.Facilities
where !hasFacilities.Contains(f)
select new FacilityViewItem()
{
Id = f.FacilityId,
Name = f.Name
};
var restOfFacilities = ctx.Facilities.Except(facilitiesThatLocationHas);
var notFacs = from e in ctx.Facilities
where !hasFacilities.Any(m => m.FacilityId == e.FacilityId)
select new FacilityViewItem()
{
Id = e.FacilityId,
Name = e.Name
};
And the error i get with every implementation...
System.NotSupportedException was unhandled
Message=Unable to create a constant value of type 'Chapter2ConsoleApp.Facility'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
What am i overlooking here?
ironically enough i solved it in a matter of hours after i posted the question on here, after days of suffering.
The error is basically saying 'i dont know how to calculate what items are not included by comparing strongly typed objects. Give me a list of Ints or some simple types, and i can take care of it'.
So, first you need to get a list of the primary keys, then use that in the contains clause...
//get the primary key ids...
var hasFacilityIds = from f in hasFacilities
select f.FacilityId;
//now use them in the contains clause...
var restOfFacilities = from f in ctx.Facilities
where !hasFacilityIds.Contains(f.FacilityId)
select new FacilityViewItem()
{
Id = f.FacilityId,
Name = f.Name
};
The first query seems fine, but you need to compare the Ids:
var restOfFacilities = from f in ctx.Facilities
where !facs.Select(fac => fac.Id).Contains(f.Id)
select f;
I wanna see what's hasFacilities, anyway, as L2E shows, "Only primitive types ('such as Int32, String, and Guid') are supported in this context", so I suppose you must retrieve first the data and put into a collection of FacilityViewItem.
var restOfFacilities = ctx
.Facilities
.Where(f => !hasFacilities.Contains(f))
.Select(f => new { f.FacilityId, f.Name })
.ToList()
.Select(f => new FacilityViewItem {
Id = f.FacilityId,
Name = f.Name
});
var notFacs = ctx
.Facilities
.Where(e => !hasFacilities.Any(m => m.FacilityId == e.FacilityId))
.Select(e => new { e.FacilityId, e.Name })
.ToList()
.Select(e => new FacilityViewItem {
Id = e.FacilityId,
Name = e.Name
});
hope it helps

Linq nested select new not working

I'm trying to get eager loading working with Subsonic, and it's been returning null for me.
In the method below, I'm trying to hydrate a domain model (UserModel) which contains another domain model (CompanyModel). However, with the code below, UserModel.Company is always null.
What am I missing here. Any help would be appreciated.
public IList<UserModel> GetUsers()
{
return (from u in SubsonicSqlServer.Users.All()
select new UserModel
{
UserId= u.UserId,
Company = (from c in u.Companies
select new CompanyModel
{
CompanyId = c.CompanyId,
CompanyName = c.CompanyName
}).SingleOrDefault(),
FirstName = u.FirstName,
LastName = u.LastName,
BirthDate = u.BirthDate
}).ToList();
}
Update (08/11/09):
More toying around with the code, I found out that setting CompanyId in the following example doesn't work either. I initially thought this was an issue with Subsonic, but if the code below doesn't work, I'm guessing it has something to do with my Linq statement. Any ideas?
public IList<UserModel> GetUsers()
{
return (from u in SubsonicSqlServer.Users.All()
select new UserModel
{
UserId= u.UserId,
CompanyId = Guid.NewGuid(),
FirstName = u.FirstName,
LastName = u.LastName,
BirthDate = u.BirthDate
}).ToList();
}
Update (11/17/2009):
Still haven't found a solution. But we are switching to nHibernate (not because of this issue).
"UserModel.Company is always null."
since you are setting this with an expression that ends with .SingleOrDefault(), I'm going to suggest that the query isn't returning a single item. Start investigating there. If you are expecting exactly one item in u.Companies, change to .Single() and force an early failure.
You can do the .Single() before creating the new CompanyModel object, I think.
As for style, I like the query comprehension syntax ("from x in y select") but find it awkward when combined with traditional dot-notation syntax. It's just hard to read. (LINQ - Fluent and Query Expression - Is there any benefit(s) of one over other?).
Consider using let in the query comprehension to make it clearer.
Also, since a query already returns an IEnumerable<T>, and calling ToList() forces all items to be realized, I would modify my method to return IEnumerable<T> if possible.
So, in your case, I would refactor the first to say:
public IEnumerable<User> GetUsers()
{
return from u in SubsonicSqlServer.Users.All()
let c = u.Companies.Single()
select new UserModel
{
UserId = u.UserId,
Company = new CompanyModel
{
CompanyId = c.CompanyId,
CompanyName = c.CompanyName
},
FirstName = e.FirstName,
LastName = e.LastName,
BirthDate = e.BirthDate
};
}
If it makes sense in your object model, you could modify User to have a constructor that takes whatever type u is, and it gets even simpler:
return from u in SubsonicSqlServer.Users.All()
select new UserModel (u);
or even
return SubsonicSqlServer.Users.All().Select(u => new UserModel (u));
Two things
You're returning a List<UserModel> when your method's signature line says IList<User> does UserModel inherit from User?
Am I missing something, where does e come from?
FirstName = e.FirstName,
LastName = e.LastName,
BirthDate = e.BirthDate Blockquote
Please check out my fork # github (http://github.com/funky81/SubSonic-3.0/commit/aa7a9c1b564b2667db7fbd41e09ab72f5d58dcdb) for this solution, actually there's a problem when subsonic try to project new type class, so there's nothin wrong with your code actually :D

Resources