MVC4 Linq Database Call - asp.net-mvc

Working on my first MVC app. I am using the simple membership database that comes with MVC4 to manage users and roles. My understanding is that I am having to do three steps for the Find() call below to work: get the simple membership GUID of the person logged in, use the simple membership GUID to get the ID in my Couriers database, then use that ID as the missing parameter in the Find() call below to return the model / record to edit in the view? How / what do I use as the parameter to the .Find() call? Am I on the right track, or making this a lot more complicated?
ps - I am saving the membership GUID in the Couriers database in a separate column from the Courier ID primary key column when the Courier record is created. Thank you.
public ActionResult EditCourier()
{
System.Web.Security.MembershipUser user = Membership.GetUser();
var providerUserKey = user.ProviderUserKey;
Guid userId = (Guid)providerUserKey;
String userIDstring = userId.ToString();
var model =
from c in db.Couriers
where c.CourierMembershipID == userIDstring
select c.ID;
Courier courier = db.Couriers.Find();
if (courier == null)
{
return HttpNotFound();
}
return View(courier);
}

MSDN documentation says:
Uses the primary key value to attempt to find an entity tracked by the
context. If the entity is not in the context then a query will be
executed and evaluated against the data in the data source, and null
is returned if the entity is not found in the context or in the data
source.
So if you have userId as a primary key in Couriers table then you can use .Find() like this:
Courier courier = db.Couriers.Find(userId);
Otherwise you should use standard LINQ query and then call .FirstOrDefault() or SingleOrDefault() method:
Courier courier = db.Couriers.FirstOrDefault(c=>c.CourierMembershipID == userIDstring);
EDIT:
I suppose you getting this error from next query:
var model =
from c in db.Couriers
where c.CourierMembershipID == userIDstring
select c.ID;
The reason is that you getting from this query IEnumerable<Courier> model.
For making this query to get single Courier object try to call .FirstOrDefault() at the end:
var model = (from c in db.Couriers
where c.CourierMembershipID == userIDstring
select c.ID).FirstOrDefault();

Related

ASP.NET MVC controller join two tables: "An anonymous type cannot have multiple properties with the same name"

I have the following code to join two tables in an ASP.NET MVC controller but I also want to display employee's supervisor name from the same table and same filed.
But I get an error:
An anonymous type cannot have multiple properties with the same name
Here is my code
public IHttpActionResult GetEmployees()
{
var query = (from n in db.Employees
join c in db.tblCities on n.ProjectID equals c.CityID
into nc
from c in nc.DefaultIfEmpty()
join manager in db.Employees on n.ManagerName equals manager.Name
into pc
from managerin pc.DefaultIfEmpty()
select new
{
n.Name,
manager.Name,
n.Email,
c.CityName
});
var employees = query.ToList();
return Ok(employees);
}
What is the workaround to accomplish my goal?
You need to give the properties (or at least conflicting ones) actual names so you know what is what. Which Name would be Name if you don’t? For example:
select new
{
n.Name
ManagerName = manager.Name,
n.Email,
c.CityName
}
Also often it’s better to use actual types instead of anonymous ones so you get type safety properly through the application, especially when this information is given out to views etc.

Error in breeze using EF 5 and calling stored procedure

Getting an error client side with breeze: "Cannot call method 'map' of undefined" when trying to pull over some data. The difference between this action and one that works is that this action is calling a stored procedure and returning ObjectResult<T> instead of DbSet<T>.
Might this be why I get an error? Using Chrome Developer tools, I do see that the breeze controller is returning json data.
I have created a complex model type in the edmx for mapping the rows returned from the stored procedure.
The action in the breeze controller has a return type of IEnumerable<T>.
I experienced the same error when using an EF complex type. A workaround was to create a view in my database instead of using a complex type, set the stored procedure to return a type of the new view which had a primary key and then it worked. It would seem that breeze requires entities to have a primary key defined.
Hm... not quite sure what is happening, so just guessing here, but try adding an AsQueryable() to the result returned, and changing the result type to a IQueryable.
We don't have any stored proc tests for breeze yet, but this is impetus for me to add some :)
I had the very same issue, but thank God I figured out a solution. Instead of using a stored procedure, you should use a view, as Breeze recognizes views as DbSet<T>, just like tables. Say you have a SQL server table that contains two tables Customers and Orders.
Customers (**CustomerId**, FirstName, LastName)
Orders (OrderId, #CustomerId, OrderDate, OrderTotal)
Now, say you want a query that returns orders by CustomerId. Usually, you would do that in a stored procedure, but as I said, you need to use a view instead. So the query will look like this in the view.
Select o.OrderId, c.CustomerId, o.OrderDate, o.OrderTotal
from dbo.Orders o inner join dbo.Customers c on c.CustomerId = o.CustomerId
Notice there is no filtering (where ...). So:
i. Create a [general] view that includes the filtering key(s) and name it, say, OrdersByCustomers
ii. Add the OrdersByCustomers view to the entity model in your VS project
iii. Add the entity to the Breeze controller, as such:
public IQueryable<OrdersByCustomers> OrdersByCustomerId(int id)
{
return _contextProvider.Context.OrdersByCustomers
.Where(r => r.CustomerId == id);
}
Notice the .Where(r => r.CustomerId == id) filter. We could do it in the data service file, but because we want the user to see only his personal data, we need to filter from the server so it only returns his data.
iv. Now, that the entity is set in the controller, you may invoke it in the data service file, as such:
var getOrdersByCustomerId = function(orderObservable, id)
{
var query = breeze.EntityQuery.from('OrdersByCustomerId')
.WithParameters({ CustomerId: id });
return manager.executeQuery(query)
.then(function(data) {
if (orderObservable) orderObservable(data.results);
}
.fail(function(e) {
logError('Retrieve Data Failed');
}
}
v. You probably know what to do next from here.
Hope it helps.

.NET web application - how to find logged in user's database records?

In my .NET CRUD web application I implemented MembershipProvider class.
Now I have function that lists records from database (this controller requires authenticated user). I need to filter out these records with respect to this logged-in user.
//
// GET: /Library/
public ViewResult Index(String orderBy = "")
{
var books = db.Books.Include(b => b.Category).Include(b => b.Writer).Include(b => b.User);
return View(books.ToList());
}
I need to know how to get logged in user's UserId and use it in Where condition (every tutorial I found talks about getting username but I need UserId). Thank you in advance.
Data I store in *.mdf data file.
I think that what you need is:
Membership.GetUser().ProviderUserKey
The type of this is object, but if you are using the out of the box membership, it should be a guid. This can then be used to filter your select accordingly.
Membership is part of the built in Membership Provider with a number of static methods.
Without knowing your DB structure/ORM, I can't exactly say, but it will be something like:
Guid userId = (Guid)Membership.GetUser().ProviderUserKey;
var books = db.Books.Where(b => b.UserId == userId);

How to do update a new record before it is committed in EF4?

I'm using EF4 POCOs and UnitOfWork/repository patterns with MVC 3. I'm trying to understand how I would modify a new record that is to be inserted.
My service method to insert/update looks something like this (the repository is injected in the service constructor via IoC):
public void UpdateData(Guid id, int newValue)
{
MyPoco poco = _repository.FirstOrDefault(p => p.Id = id);
if (poco == null)
{
poco = new Poco
{
//set properties
};
_repository.Add(poco);
}
poco.SomeFieldToUpdate = newValue;
}
And my changes get persisted via my UnitOfWork on a UseUnitOfWorkAttribute action filter on my controller:
void IResultFilter.OnResultExecuted(ResultExecutedContext filterContext)
{
var unitOfWork = IoCFactory.Instance.CurrentContainer.Resolve<IUnitOfWork>();
unitOfWork.Commit();
}
Of course, this works fine if this is ever hit just once, for existing or new data. And it works fine on multiple passes if it already exists.
But if the Guid value doesn't exist in the table, then it tries to do multiple inserts if this is called multiple times.
So that's my dilemma. I understand why this doesn't work, I'm just not sure the proper way to fix it. Basically, I need to somehow get a reference to the existing POCO in the UnitOfWork, and somehow update it. But the UnitOfWork is not available in my service (by design) -- and I'm not even sure I know how to pull an entity out of the UoW and update it anyway.
Am I going about this wrong or am I overlooking something simple here? Or do I have a fundamental flaw in how I've designed this? I have a feeling I may be making this harder than it should be.
Thanks in advance.
The reason why this happens is because your entity is not saved yet and you execute query to get it. Query will not find it in database and correctly return null.
You should not need to use repository / unit of work / ObjectContex as internal storage of not saved entities among service calls. If you need it you should check your application design and refactor it because something is probably wrong.
Anyway you can get not saved entity from context but it is not very nice code. You will need special method on your repository to get entity by id. You will use it instead of calling FirstOrDefault. Something like:
public MyPoco GetById(Guid id)
{
MyPoco enity = context.ObjectStateManager.GetObjectStateEntries(EntityState.Added)
.Where(e => e.Entity != null && e.Entity.GetType() == typeof(MyPoco)))
.Select(e => (MyPoco)e.Entity)
.Where(p => p.Id == id)
.SingleOrDefault();
if (entity == null)
{
entity = context.MyPocos.FirstOrDefault(p => p.Id == id);
}
}
Do you set the id you pass into UpdateData as the key on the new Poco object, like so:
poco = new Poco
{
Id = id;
//set properties
};
If yes, you could query for the object not with FirstOrDefault but by using the TryGetObjectByKey in a repository method:
public Poco GetPocoByKey(Guid id)
{
EntityKey key = new EntityKey("MyEntities.Pocos", "Id", id);
object pocoObject;
if (context.TryGetObjectByKey(key, out pocoObject))
return (Poco)pocoObject;
return null;
}
The advantage is that TryGetObjectByKey looks at first into the ObjectContext if it can find an object with the specified key. Only if not, then the database will be queried. Since you add the new Poco to the context the first time it isn't found, TryGetObjectByKey should find it in the context when you search for the object with the same key a second time, even if it has not been saved to the database yet.
Edit: This solution doesn't work!
Because TryGetObjectByKey does not find the key for objects which are in added state in the ObjectContext, even not if the key is not a DB generated key and supplied by the application (see comments below).

Linq to SQL MVC issue with FK

right having a bit of a hard mental blog with some Linq to SQL and MVC.
Getting the basic MVC and L2SQL going ok.
FK relationships. -> when using MVC and we have a fk relationship in the model,
and the view is based on a strongly typed object - how do you get the data from the related table?
So for example
User (Table)
UserId
UserName
Call (Table)
CallId
UserId
CountryId
Country(Table)
CountryID
CountryName
SO I want to get only the calls for a user with a specific country?
The view - based on Call Object as this is the "Details" view -
how do i get a UserName and CountryName and still maintain a view based on Call?
It would seem that I still have to create an Object CallForUserByCountry - but this gets messy
on save as the CallForUserByCountry object also needs to implement how to create Call User and Country.
the linq query
var calls = from c in db.Call
where c.CountryID == id
select new CallForUserByCountry or new something// this is bit that suggests a new object.
Hopefully I missing something ...
If the Call data class has associations to User and Call data classes you can access Call's User and Country properties directly in your View. Also you can load data from related User and Country tables immediately (instead of lazy loading by default):
In Controller:
DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Call>(x => x.User);
options.LoadWith<Call>(x => x.Country);
db.LoadOptions = options;
var calls = from c in db.Call
where c.Country.CountryName == countryName && c.User.UserName == userName
select c;
In View:
<%= Model.User.UserName %>
<%= Model.Country.CountryName %>
You could use the TempData bag and save the Username and CountryName there.
in the controller:
TempData["CountryName"] = countryName;
If I'm understanding this correctly you are trying to get calls from a specific user with a specific country. If that's the case, then as long as you have the relationships mapped in the Linq2SQL designer, then you should be able to access the user and country from the call as properties. So it would be something like:
var userName = call.User.UserName;
As I said, this is contingent on the relationships being mapped in the designer, otherwise Linq2SQL will have no indication that the two tables are related.
You could also have a separate query to just get the calls you need based on User and Country:
var calls = from c in db.Call
where c.User.UserID = userID
&& c.Country.CountryID == countryID
select c;
Your question can be taken two ways.
You want to display a list of calls using the current Call Details View. - This would need a new view. Unless the details view is a ViewUserControl. Then you could use PartialRender.
You want to add extra data to the current Details View - Add the extra data to the ViewData.
For number 2 you could do the following.
i prefer using joins for this, and I like working with objects detached from my DataContext. So normally i don't have the extra IQueryable in my objects.
I would still use the CallForUserByCountry object. Makes things verbose.
var calls = from call in db.Calls
join country in db.Countries on call.CountryId equals country.CountryID
join user in db.Users on call.UserId equals user.UserId
where user.UserName == userName && country.CountryName == countryName
select new CallForUserByCountry
{
UserName = user.UserName,
CountryName = country.CountryName,
Call = call
};
View["CallsForUserByCountry"] = calls.ToList();
In the view.
<% foreach(var callForUserByCountry in (IList<CallForUserByCountry>)ViewData["CallsForUserByCountry"]) { %>
.. Do stuff here .. I like using PartialRendering
<% Html.PartialRender("CallForUserByCountryDetail", callForUserByCountry); %>
<%}

Resources