Blog

Personal Finance

2024-05-03 23:30

I've been absorbing a lot of financial information, and I wanted to kind of put together an informational post distilling some of that. It's not to be taken as specific financial advice for your situation, nor specific investment reccomendations. Talk to your own financial advisor.

Income

Income is incredibly important to making financial matters easier. While you can survive on minimum wage (barely and with extra hours and unpleasant cost cutting), to really achieve financial independence later in life you'll need to grow your income. As you'll see in this blog post though, you don't necessarily have to make a six figure salary to be set up for success. So don't be discouraged if you currently don't have a high income.

Expenses

Expenses are one of the most difficult things to control. It's easy to see others being wasteful with their money around you, and think that makes it okay to spend your own money on things you don't need. Cutting unnecessary expenses (eating out, alcohol, cigarettes, drugs, online shopping) and spending wisely on necessary expenses can go a long ways. Some ideas:

Time

This is probably the most underrated tool needed to build wealth. Too many people don't start soon enough, and allow compound interest to work in their favor. As an example of how powerful this can be, if you start at age 18 and were to invest $500/mo in an asset that has been proven to get 8% annual returns over long periods of time, and you do that until you're 65 years old, you would have an investment portfolio of $2,717,400, despite only putting in $282,000 of your own money. Start at age 30 instead of 18, and your investment portfolio would only be $1,033,900.

One of my favorite tools for calculating compound interest is actually the US government created Compound Interest Calculator. Plug in your starting amount, how many years, how much you'll invest monthly, and what interest rate you expect and it will spit you out a nice graph.

Pic showing $500 invested per month for 47 years ends up with $2M

The green dotted line represents how much money you put into your investments, and the red line is how much your investments will grow to assuming you achieve the specified rate of return. Notice that for the first 18 years or so, the lines are very close to each other, hard to distinguish. Don't let that discourage you. By year 24, you can see some decent seperation starting to happen, by year 36 it's a wide gap, and by year 47 (retirement year!) it's a massive gap. Patience and consistency are the key.

I said $500/mo earlier, but I realize that may be daunting for someone still starting out. By recording some starting amounts and doing shorter time periods, you can begin to build up a chart like this...

Pic showing amounts invested at certain ages

Notice we start out easy and ramp up the investments over time, to reflect that our income has grown through raises/promotions, or moving to other companies and getting a pay bump. By age 65 we have over $2M, even though we slowed down our investing towards the end (perhaps you did a partial retirement!). This gives me confidence that those who are willing to work hard to grow their income and keep their expenses reasonable can build towards a successful retirement. You don't have to have a massive salary or eat ramen your entire life to do it, though obviously both of those things can help.

Why 8%?

A note about why 8% for the returns in my above examples....but first you must understand the S&P 500 Index Funds. These funds track what the S&P 500 does, which are 500 of the largest public companies in the United States. Collectively, their combined market cap (total dollar amount of all the shares of their stock) is worth 80% of the total market cap of all US public companies. Index Funds that follow the S&P 500 buy shares of all these companies that are listed in the S&P 500, at amounts equal to the proportion of their market cap. These following numbers are made up, but let's say Apple is 25%, Microsoft is 25%, Meta is 25%, Netflix is 15%, and Ford is 10%,. If you buy 1 share of an S&P 500 index fund for $100, they will turn around and use that to buy $25 worth of Apple, $25 worth of Microsoft, $25 worth of Meta, $15 worth of Netflix, and $10 worth of Ford. If Ford does really well and grows to 15% while Netflix shrinks to 10%, they will rebalance their holdings to reflect that as well, selling $5 of Neflix and buying $5 of Ford. The dividends that these companys pay out also get passed back to you.

One important way to think about the S&P 500 is that you're buying slices of 500 of the best performing companies in America. Even if you're not patriotic, it's important to recognize that the US is the oldest democracy, has the largest Gross Domestic Product, is strategically positioned well with easily defensible oceans on either side, friendly enough relations with countries to our north and south (and enough military might to clobber them), tons of economic advantages including farms, natural resources, and navigable rivers (Mississippi).

The S&P 500 has been around since 1923 (though it had fewer stocks originally). Over the last 100 years, it has averaged an annual return of about 7.5% factoring in dividends and inflation. There are some years where it's had negative returns, but just like America continues to grow, it bounces back. Over the past 10 years it's averaged about 9.5% per year. Past performance does not guarantee future returns. But if I had to take a safe bet, I'd guess that America will continue to do well. 8% is a decent middle ground to pick for how well the S&P 500 will do in the future. Click here if you want to see its annual returns each year since inception.

Financial Order of Operations

Just like PEMDAS (Parentheses Exponents Multiply Divide Add Subtract) that you might remember from math classes, there are a series of steps to take through life regarding your finances. There's multiple programs out there, perhaps you've heard of Dave Ramsey's Baby Steps. However, I prefer The Money Guy Show's Financial Order of Operations.

The Financial Order Of Operations from The Money Guy Show

1 Deductibles Covered

Step one is making sure you have enough cash on hand to meet your deductibles for your health insurance, car insurance, and home insurance. That way you're prepared if something very bad was to happen. Keep this money locked away in a Savings account or High Yield Savings Account. This is the beginnings of your emergency fund.

2 Employer Match

If your employer offers matching on your 401(k), contribute just enough to your 401(k) to get all the match offered. For example, if your employer offers a 100% match up to 3%, then contribute 3% of your salary to your 401(k). That employer match money is a guaranteed 100% return on your investment - it's something you should never pass up. If your current employer does not offer a 401(k) match, then proceed to step 3 and return here if you change to an employer that does offer matching.

3 High Interest Debt

Next, begin to pay off any high interest debt. The exact amount varies, but generally anything over 6% can be considered high interest debt. This could be a vehicle loan, student loans, credit card debt etc. There are two main approaches to this:

4 Emergency Reserves

The next step is to build upon what you did in Step 1, to build up your emergency fund. Save up 3-6 months worth of expenses and put this in a High Yield Savings Account (you may also purchase CD's). The only reason to dip into your emergency reserves is if you have an actual emergency that you can't cover as part of your normal budget. Air conditioner breaks, car is totaled and insurance doesn't fully cover it etc. Since this is going to be a fairly sizable chunk of cash, placing it in an HYSA or CD helps you earn a little bit of interest on it. It may not be enough to beat inflation, but it will help keep your money from losing value.

5 Roth IRA & HSA

This is where we start investing. Contribute up to the limit to a Roth IRA and HSA if you are eligible. The contribution limits for Roth IRA's are here and for HSA's the limits are here. See below for more details about Roth IRA and HSA. If you're not eligible for both, then skip this step. Return later if you become eligible.

6 Max Out Retirement

The next step is to max out your contributions to your 401(k). You may not have enough to reach the contribution limit, but keep contributing until your income is high enough to reach the limit.

7 Hyper-Accumulation

Once you reach this point, you're well on your way to building a solid financial foundation and setting yourself up for retirement. Since you've maxed out the Roth IRA, HSA, and 401(k) and you may want to retire early, it's time to start building up your Taxable Brokerage Account and potentially other forms of investment, such as real estate. You may be in this step for quite a long time. Try to invest at least 25% of your income, though more doesn't hurt.

8 Pre Paid Future Expenses

Once you're deep into step 7 and have built up a sizable investment portfolio, now you can begin pre-paying some future expenses. This might be pre-paying for a child's college via 529 plan, or paying for life insurance, or pre-paying funeral costs. Basically this is tying up loose ends so that nothing is left to chance. You'll probably start this when you're close to retirement.

9 Low Interest Debt

The last step is to begin paying down any remaining low interest debt. This may be a mortgage or student loans. We pay this down last, as by definition it has the lowest return on our investment.

Types of Accounts

Checking

This is a basic bank account. When I was young and had low expenses, I kept a minimum balance of $1,000 at all times. As I got older and had large expenses, I started keeping a minimum of $2,500 in here. My paycheck direct deposit goes here to my checking account, and then I distribute the money from here to wherever else it needs to go.

Savings

In the same bank where I keep my checking account, I have a savings account. This has the advantage that if you were to accidentally overdraft your checking account (drop your balance below $0) then most banks can be set up to automatically pull from your savings account. You should avoid getting in this situation in the first place, but it's a good safety net in case mistakes happen. I generally keep the same minimum balance in my savings account that I keep in my checking account. I don't like to keep a lot of money here, as many banks pay abysmally low interest rates (around .01%).

High Yield Savings Account (HYSA)

A high yield savings account is just a regular savings account, except it has a much higher interest rate attached to it. If your checking and savings are located in the same bank, you can generally skip the regular savings account and just use a HYSA, no need to seperate them. But if not, open a HYSA somewhere! Since the bulk of your emergency fund will probably live here, earning that high interest (around 5% as of this writing) will help make up for the fact that you haven't invested it in something that has a higher risk/reward.

Certificate of Deposits (CD)

A CD or Certificate of Deposit is a type of savings account that pays a fixed interest rate on money held for a fixed periods of time. Their rates tend to be slightly better than a high yield savings account, but you typically pay a penalty for withrawing money before the CD expires. Their interest rates generally aren't high enough to compete with actual risky investments, but it can be a good option to leave a portion of your emergency fund in. They're protected by FDIC (for banks) or NCUA (for credit unions), so there's no risk other than the small penalty if you end up withdrawing your money early. You can read more about CD's on Investopedia.

Roth IRA

This is an investment account that you put after-tax money into. Meaning, you've already paid money on that tax. Any growth in the account grows tax free, and you can withdraw the money tax-free at age 59.5. You can generally withdraw the principal (the money you put in) penalty free if you must, but it's best to avoid this. When you retire and you're trying to stay in a low tax bracket, being able to pull money out of your Roth IRA is very useful, so it's important to fund this one. That's why it's step 5 of the Financial Order of Operations. Right now, each individual can only invest $7,000 per year, and there's income caps beyond which you can contribute a lower amount or nothing at all. You can read more about Roth IRA's on Investopedia.

Traditional 401(k)

A traditional 401(k) is a retirement plan that you put pre-tax money into. Meaning, any income you earn that you place into a traditional 401(k) account does not increase your tax liability for the year. Depending on what tax bracket you're in, this basically means the money you put into it saves you 10 to 37%. Within the 401(k) you typically have options to invest in various ETF's or mutual funds. Typically there is an option to invest in a Target Retirement Fund based on the year you plan to retire. These can be a good option, as those funds start out aggressive and then as they get closer to the retirement year, they become less aggressive and start investing in safer investments, such as bonds.

An important feature of most 401(k) plans is company matching. Your employer sponsors the 401(k) plan and agrees to match to some extent the contributions that you put into the 401(k). Often it will be structured as "100% matching up to the first 4% of your salary". If your salary is $50,0000 per year, and you choose to contribute 4% of your salary ($2,000) then they will match it and put $2,000 in as well. This is a 100% return on your investment, and absolutely something you should take advantage of if offered! You can usually contribute beyond what the employer says they will match, up to the maximum contribution limits that the government sets. You can read more about 401(k) plans on Investopedia.

There are some similar plans called 403(b) and 457, but I am not as familiar with them.

Roth 401(k)

A Roth 401(k) works very similarly to the traditional 401(k) plan, but contributions to it are done with after-tax dollars instead of pre-tax dollars. Contributing to it does not decrease your current year's tax liability. Similar to a Roth IRA, any investment in a Roth 401(k) grows tax free and can be withdrawn penalty free at age 59.5. Not all employers offer it, and those that do often let you choose how much of your 401(k) contribution should be traditional (pre-tax) and how much should be Roth (after-tax). Which you should choose depends on what tax bracket you're in now and which you think you'll be in during retirement. I couldn't decide, so I did 50/50.

Health Savings Account (HSA)

A Health Savings Account or HSA is another type of tax advantaged account. You are only allowed to contributed to an HSA if you have a qualifying High Deductible Health Plan for your health insurance. Like a 401(k), any money you contribute to an HSA is done pre-tax, lowering your taxable income. Like a Roth IRA or Roth 401(k), any growth from investments within the HSA are tax-free. HSA's can also be used to pay for qualified medical expenses (deductibles, dental, vision, drugs etc) - tax free. You can also use it to pay for non-qualified expenses, but you have to pay a 20% tax if you do so. After age 59.5, the 20% tax is eliminated, but you would have to pay income tax on non-qualified expenses.

Employers also can do matching for the 401(k). My own employer contributes $1,100 per year to our HSA. Since the max contribution limit set by the government is $8,300 for a family, I only have to contribute $7,200 of my own money each year in order to max out my HSA. The tax advantages (sometimes called triple tax advantage) of HSA's are fantastic, and that's why it's part of step 5 of the Financial Order of Operations.

Taxable Brokerage Account

This is a non-tax advantaged brokerage account. The money you put into this, you'll have paid income tax on when you earned it. Any growth in it will be taxed as well. Though you can take advantage of long term capital gains tax rates for qualified investments that you hold for over a year. On your financial journey, hopefully you reach the point where you're maxing out contributions to all the tax-advantaged accounts and still have more to contribute, and that's when you come to the taxable brokerage account. It can also be handy to build this up to help bridge the gap between your retirement date and when you're allowed to withdraw money from your retirement accounts (assuming you retire before age 59.5).

What to Invest In

Individual Stocks

You can buy individual shares of publicly traded companies. I generally don't recommend it, at least for the bulk of your investment portfolio. Buying into individual companies is risky. It's hard to predict the long term success of any given company. Yeah, you might be smart or lucky and invest in the next Apple, Amazon, or Tesla before they hit it big. But it's hard to know they will hit it big until it's too late. For everyone that's invested in individual companies for any appreciable length of time, it's likely that they have at least a few stocks that have lost significant amounts of money.

That said, I feel like I have solid core investment portfolio in other fairly risk free investments, so I do dabble in some individual stocks. Taking some risk while young and in a decent financial position is okay. Just don't make your portfolio all about it.

Exchange Traded Funds (ETF's) and Mutual Funds

ETF's are funds that are traded on stock exchanges. They may hold investments in particular types of assets such as particular sector of the stock market, or some other type of asset such as gold. You can buy shares of an ETF, just like you might purchase shares of a public company traded on the stock exchange.

Mutual funds are similar to ETF's, but not traded on the stock exchange (you may be able to buy into them via a 401(k)).

I mentioned earlier the S&P 500 Index Funds, which follow the S&P 500. There are other index funds. Index funds typically have a low expense ratio (they charge a very small management fee) compared to other ETF's, since they are easy for companies to manage. Some follow other indices like the Nasdaq 100 or Dow Jones Industrial Average. You can see a list of ETF's offered by Vanguard or by Blackrock.

Real Estate

Real estate can be a good investment, but in my opinion it's a lot less passive than most people realize. Buying houses to fix them up and flip them takes a lot of research to find the right opportunity. Renting property out has the risk of bad tenants, vacancy, constant repairs etc. If you take out a loan to buy a rental property, you have to really do the math and understand the expenses to make sure that the property will cash flow enough to be worth your time. I'm not totally against real estate, just be prepared to put some effort into it.

House Hacking

This is a fairly attractive form of real estate investing that I wish I had taken more advantage of myself when younger. At a small scale, you can buy a house at a young age and then rent out the other room (perhaps at $500/mo) to your friends. This can work great in college to help offset the mortgage on the property.

At a larger scale, you might purchase a duplex, triplex, or quadplex. Though these are bigger and may cost more than a normal house, banks tend to look more favorably on this than simply purchasing an investment property, since you'll be living in one of the units. Then you rent out the other units to other tenants. That rent goes towards the mortgage - now your own housing is paid for by others! You may even be lucky enough to actually generate a profit beyond paying for the mortage. After a few years, then you can move out and get your own place and start renting out the unit you formerly occupied - or sell the whole thing to an investor.

Cryptocurrency

You've undoubtedly heard of the cryptocoins such as Bitcoin and Ethereum. I'm not a big fan of them, as I don't believe in the long term vision, and I believe many of the people that claim to believe it are just trying to hype others into buying it, like a pyramid scheme. I have hedged my bets with some very small holdings of Bitcoin and Ethereum, just in case I'm wrong.

Handy Apps and Resources

Investopedia

Investopedia has lots of handy tools for investors. When I was first becoming aware of personal finance, I found their terms list very handy.

The Money Guy Show

These are some certified financial advisors from a firm called Abound Wealth. They do a podcast and I watch their videos regularly on YouTube. Unlike Dave Ramsey, they are calm, they understand how difficult it can be young in life and don't just say random boomer nonsense, and I've found their advice to be better.

Empower Personal Dashboard

The Empower Personal Dashboard (this is a referral link), formerly called Personal Capital, is a tool that you can link to your various bank, loan, and investment accounts, to easily see the big picture of your money and track your net worth over time. They have a website and phone apps.

M1 Finance

M1 Finance is a website (referral link) and app that can contain various types of investment accounts. In their investment accounts (Roth IRA or taxable brokerage account), you define a "pie" that has slices composed of various assets. For example, you might choose to define a pie that is composed of 75% VOO (Vanguard S&P 500 Index Fund), 15% VXUS (Vanguard international stock fund), and 10% BND (Vanguard total bond market fund). When you put $100 into your pie, it will automatically acquire the stocks or funds that you defined for your slices, in the proportion that you specified. If one of your slices outperforms the other, it will automatically rebalance to keep things at the proportion you specified. This makes it very easy to create a diverse portfolio once, then set it and forget it. All I do is send money to M1 and then they take care of everything else for me. It's kind of like being able to build your own index fund!

They also have a high yield savings account that pays 5%. That's not bad at all.

Robinhood

Robinhood is a website (referral link) and app that can contain various types of investments accounts, such as taxable brokerage accounts or Roth IRA's. You can buy stocks, index funds, and cryptocurrency. Their app is easy to use, doesn't charge you for each trade, and when you transfer money in they give you instant access to those funds up to a certain limit, without having to wait for those funds to fully transfer over from your bank.

You can also earn a 1% match on Roth IRA contributions from them, or 3% if you pay for their premium service, Robinhood Gold. Any IRA or 401(k) rollovers to Robinhood are also eligible for a 1% match bonus.

Fundrise

Fundrise (referral link) originally started as a crowd-funded investment platform for real estate, allowing investors to pool their money together to purchase real estate, without having to directly look for real estate deals yourself or rely on publicly traded REIT's (Real Estate Investment Trusts, basically companies that own lots of real estate). They're known for carefully researching their deals before purchasing, and trying to identify long term trends before anyone else. They don't go for short term profits.

Recently they've started branching out beyond real estate with their Innovation Fund, which pools investor's resources to purchase equity in promising private technology companies. This can be a great way to get some ownership of up-and-coming companies before they go public. It doesn't have a proven track record, but I have high hopes for it.

One disadvantage of Fundrise is liquidity. Unlike stocks or index funds, it's more difficult to sell your stake in Fundrise's funds. For the good of the fund, they may even block all withdrawls in order to prevent a short term market panic from destabilizing the entire fund. You can look at that how you see fit, but I think it's a good thing. Just don't put money into Fundrise if you're not comfortably with the level of liquidity they offer. But, generally you shouldn't pull from any of your investments for many years. You should have your stable income from your job, and then your emergency fund if something does come up.

The Whitecoat Investor

The Whitecoat Investor is a site by and for doctors, though I've found that a lot of the advice given applies to those in other fields that earn a high income. They write high quality articles about personal finance. They can be very precise, you can almost feel the intelligence of these docs coming through their writing.

Ally Bank

There's several online banks out there, but I've found that Ally bank is pretty easy to use, offering checking, high yield savings accounts, CD's, and vehicle loans. It's easy to open multiple savings accounts if you want to seperate money that way, or to subdivide your money within a single account by organizing it into buckets (such as emergency fund, vacation fund, saving up for a home or vehicle downpayment etc.) Right now their HYSA is paying 4.2%.

Stop Using ASP.NET Web Forms

2019-09-08 00:30

I am active on Stack Overflow. I usually follow the asp.net tag pretty closely, as I've been working with the framework for years and I feel quite knowledgeable about it. I see a disheartening number of questions about ASP.NET Web Forms, particularly from people that appear to be just starting out and learning the framework for the first time. This is shocking to me. There are only a few reasons to learn this framework. For most people, it's an absolute waste of time. When I tell people this, they often want to know why.

There's so many reasons why! And it's a shame they even have to ask. People really need to take the time to research a framework more carefully before learning it, because investing time in a framework that is difficult or dying is a waste of valuable time. We can only learn so many frameworks in depth, you should use it to learn one that is easy to work with and has a bright future.

Why is Web Forms so bad?

No feature updates

Web Forms hasn't received a major feature update in a long time. It actually came as a shock when .NET 4.7.2 in April 2018 added a feature (half-baked Dependency Injection)! No new features being added is a sure sign that no innovation is happening. And if no innovation is happening, it's getting left behind because other frameworks are adding great features that make them easier to work with and faster.

Viewstate ignores how the web works

The web is normally stateless. This means that when a web server receives an HTTP request, it processes that request without past knoweledge of what previous requests may have come from that particular client. There are ways to add state, such as cookies. Web Forms adds another approach: Viewstate. Viewstate is a hidden form element added to the DOM. It contains the serialized state of all the controls on the page as of when the page was rendered. When the form gets submitted (via a postback) that information is sent to the server, which can compare the viewstate with the new values that get POSTed and determine what has changed. Controls that may have initially had their values set when the page first don't need to have them manually reset, as the Viewstate will be able to persist their previous values.

Sounds great in theory. But the problem is that this adds a large amount of redundant information to the page that must be sent to and from the server with each request/response and then maintained in the DOM. In the case of data-heavy controls such as a GridView, this large chunk of serialized data can significantly slow down everything down.

You can disable ViewState per control or at the page/application level. Which is probably the best thing to do. But beware that many legacy sites were designed with the assumption that ViewState is enabled. By turning it off, you're likely breaking functionality and now every piece of your UI needs to be tested again. And since people generally don't create new Web Forms sites anymore, turning it off up front is a moot point.

Visual Designer

Web Forms has a visual designer, where you can drop and drop controls onto a surface and visually see how your page will look. Or at least, that's the idea. Unfortunately, in practice it doesn't work so well. Most web pages will use some amount of JavaScript and CSS to format a page after it loads - the visual designer often doesn't take this JavaScript into account or does a poor job of applying the CSS. Also, dropping controls onto pages and moving them around can result in ugly markup that's difficult to maintain - and you will have to directly modify the markup at some point to fix issues. Good luck messing with the spaghetti markup!

Like ViewState, you can chose to not use the visual designer. It's what I do when working with Web Forms. But the reaeson I mention it in the first place is that so many new developers are attracted to the idea of Web Forms because of its visual designer, and they don't think about or ignore the ramifications of using it.

Mangled client ID's

When server side controls (elements declared in the ASPX markup with the runat=\"server\" attribute) are created, we normally assign them and ID, such as \"FirstNameTB\" for a text box that the user should enter a first name into. When the ASPX is rendered to HTML and sent to the client, the element of that ID on the client side is often not what the developer expects. Controls that are nested (and they most often are!) inside a content page, or a user control (.ascx) or some other container (such as a Panel control) will have the names of the parent containers appended to the ID on the client side. This is done because it's not valid to have multiple elements within the DOM on the client with the same ID, and by the composite nature of master pages, templated user controls and other containers, it's conceivable that you could have two elements with the same server side ID declared in different places. The framework \"helpfully\" tries to prevent conflicts on the client side by appending the container names in front of it, thus ensuring the rendered page has unique ID's for all elements.

This is often suprising to developers new to Web Forms when they try to write some JavaScript that tries to interact with these elements by ID. There are some possible solutions:

It's a problem that can be worked around, I just don't like that I have to do so in the first place.

UpdatePanel

The UpdatePanel is a control that you can add to your page, and other controls you place insisde the UpdatePanel can be updated without having to refresh the entire page. It does this behind the scenes by using XmlHttpRequest (just like AJAX). It seems magical at first. But many developers end up regretting ever using it.

When an UpdatePanel refreshes, it posts the entire form pack to the server side. That can potentially be a large amount of data! The entire page lifecycle will run (such as Page_Load) so the developer needs to take care to check if it's a PostBack so that they don't reinitialize values. This ends up being extraordinarily resource intensive. Even though these postbacks happen asynchronously, they happen slowly and use a lot of server resources due to the entire page lifecycle running again. That slows things down further.

What should you do instead? A little custom JavaScript to communicate data with the server side will be a lot easier to debug, and you can choose to send only the data that the server needs, and the server only needs to response with the data actually needed by the client.

Developers sometimes pair the UpdatePanel with a Timer control, so that the page can refresh certain parts at regular intervals. Now you're polling the server, sending massive amounts of data on a regular schedule, even though data may not have changed!

What should you do instead? Push data from the server to the client, only when the data changes. You can push data from the server to the client via technologies like WebSockets, something that Microsoft's SignalR framework makes very easy.

SqlDataSource

SqlDataSource is a control that you drop on the page to enable getting data from or into a relational database. It's often paired with a data control, such as a GridView, DetailsView, or Repeater. It takes one or more CRUD commands (INSERT, SELECT, UPDATE, or DELETE) to allow it to perform this wor. The command is embedded right in the ASPX markup.

This should set of red flags in your mind if you know anything about seperation of concerns. Your UI layer (am ASPX page is firmly in the UI layer) should not be strongly tied to your database layer. This makes it difficult to change the storage layer without breaking your UI, and it makes it difficult to test your page without having a real database. You also don't get some of the design time benefits that working with strongly-typed model classes can offer, such as compilation errors when you mistype the name of a column.

What should you do instead? Create a proper database layer following the repository pattern.

public interface IProductRepository
{
    public Product GetProductById(int id);

    public IEnumerable<Product> GetAllProducts();

    public void CreateOrUpdateProduct(Product product);

    public void DeleteProduct(int id);
}

public class SqlProductRepository : IProductRepository
{
    // Implementation here
}

Then your form should depend on an IProductRepository (not SqlProductRepository!) that should be injected via DependencyInjection. Or better yet, have your UI layer depend on a busineess logic layer, and that business logic layer should handle all interaction with the IProductRepository.

Half baked Dependency Injection

Web Forms has had dependency injection for a while. Certain DI containers such as Ninject were able to hook into events in the ASP.NET pipeline and do property injection of dependencies into some Web Forms classes. But property injection is generally considered inferior to constructor injection - because we can find out much earlier if our dependencies aren't satisfied.

ASP.NET 4.7.2 added support for constructor injection. No container is implemented by default, you need to add additional libraries such as AutoFac, Unity, Ninject, or Simple Injector which can provide an IServiceProvider (an interface built into the framework for resolving dependencies). However, you must manually register any of the Web Forms types that you want to resolve, because they're not automatically reigstered. So you have to wrap the container of your choice with some reflection logic to resolve the Web Forms types yourself.

public class AutofacServiceProvider : IServiceProvider
{
    private readonly ILifetimeScope _rootContainer;

    public AutofacServiceProvider(ILifetimeScope rootContainer)
    {
        _rootContainer = rootContainer;
    }

    public object GetService(Type serviceType)
    {
        if (_rootContainer.IsRegistered(serviceType))
        {
            return _rootContainer.Resolve(serviceType);
        }

        return Activator.CreateInstance(serviceType, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance, null, null, null);
    }
}

What a mess!

Difficult to test

Mangled client ID's, difficult Dependency Injection, tight coupling of the UI to the back-end all make it extremely difficult to unit/integration test a Web Forms project. And untestable code often leads to unmaintainable code.

What should I learn instead?

When should I learn Web Forms?

I've often heard it said that Web Forms is faster to make apps with than alternatives such as ASP.NET MVC. Anyone saying this shouldn't be taken seriously. Given the same amount of time to learn both frameworks, you can accomplish just about any task in the same amount of time. However, applications written in more mature frameworks tend to be easier to maintain and debug, because of the more mature coding practices that they enable you to take advantage of - such as tight control over your rendered HTML markup, seperation of UI logic from business logic, and ability to test more layers of the application. I've heard quite a few people say this \"Web Forms enables rapid development!\" line, and then I look at their code and it's utter garbage - and what's worse is they don't understand why it's not good. They're often not aware of what SOLID code is. Web Forms proponents are usually older developers that learned Web Forms at point in the early 2000's and didn't bother updating their skillset. In an industry that moves as fast as web development, that's an unforgiveable sin.

There's very few reasons to learn Web Forms. Companies sometimes have old legacy Web Forms apps - this is a sign that the company isn't interested in keeping their code base up to date. Before accepting a position at a company that has Web Forms apps, ask if there's a plan in place to modernize the app and get rid of Web Forms. See if they're aware of the many downsides or if they're in denial. Pay close attention to their answers - you probably don't want to accept a position where you learn a framework that has very little future and that will be frustrating to work with.

If you're in an education envirornment and your teacher is teaching Web Forms, this is a red flag that the teacher is not in touch with the industry. Web development changes rapidly - a teacher that isn't keeping up probably isn't worth your money. You might consider asking them to update their course materials, or even raise the issue with their supervisor.

Web Forms Legacy

A long time ago, Web Forms was a great framework to work with. It brought over many concepts that were familiar to Windows Forms developers and allowed them to quickly transition to creating web applications, without needing to actually learn how the web works. But that was a long time ago, when rich web application were still in their infancy. Now users expect far more of their web applications. They want quick client side validation without having to submit a form. They want sections of pages to refresh without refreshing the entire page. They want data to be pushed to their browser without needing to manually refresh. They want quick loading and good mobile support. And they want sites that can be updated rapidly, meaning those sites need to have well maintained and code that's easy to automatically test. Web Forms either doesn't work with any of this, or requires so much deviation from standard Web Forms patterns that you might as well not use Web Forms at all.

Web Forms was highly successful for its time, and the underpinnings of it were able to be abstracted away to make it possible to create ASP.NET MVC, which eventually was rewritten as ASP.NET Core MVC. So I'm very grateful that Scott Guthrie and the rest of the team at Microsoft created it. But it's time for us to transition away from it and stop teaching it to new developers. Let it rest in peace.

Properly Generating Excel Files in .NET

2015-02-15 23:00

I've seen a lot of new web developers struggle with generating Excel files in their web applications. They often take one of two incorrect approaches.

  1. Take a Gridview, render it to HTML, then send that to the client with an Excel MIME type and/or a .xls file extension.
  2. Use Office Interop.

The first approach often looks something like this:

protected void ExportBtn_Click(object sender, EventArgs e)
{
    Response.Clear();
    Response.AddHeader("content-disposition", "attachment;filename=myexcelfile.xls");
    Response.ContentType = "application/vnd.xls";
    StringWriter sw = new StringWriter();
    HtmlTextWriter htw = new HtmlTextWriter(sw);
    ResultGrid.RenderControl(htw);
    Response.Write(sw.ToString());
    Response.End();
}

This results in an HTML file being served to the client pretending to be a file that contains Excel data. But it isn't. So when opening this file in Microsoft Office on a desktop computer, it will display a warning that the file may be corrupted. The user can click past the warning, but that's not very user friendly or professional.

Excel Warning

Another downside to this approach is that any consuming application that doesn't know how to handle .xls files that contain HTML will report the file is corrupted and refuse to display anything at all, even if the application knows how to open HTML files with a .html extension. Often this approach is taken by someone who asks themselves "How can I export a GridView to Excel?" Which is not a good way of thinking about what you're trying to accomplish. Most of the time, the goal is to export the underlying data to Excel, not the GridView.

The Office Interop approach is even worse. Office Interop was not meant to be used on servers. It will cause plenty of strange errors. And the Office Interop libraries aren't friendly to work with. Don't use it. That's all I'm going to say about that approach.

The correct approach to providing Excel files to your user is to use a library capable of generating Open Office XML Spreadsheet (.xlsx) files natively. There's several libraries for this, such as Open Office XML SDK from Microsoft, NPOI (NuGet), or EPPlus (NuGet).

My favorite is EPPlus. Here's an example of creating a real .xlsx file from data in a DataTable.

protected void ExportBtn_Click(object sender, EventArgs e)
{
    Response.Clear();
    Response.AddHeader("content-disposition", "attachment;filename=myexcelfile.xlsx");
    Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
    var package = new ExcelPackage(); //create a new package, this is the equivalent of an XLSX file.
    var sheet = package.Workbook.Worksheets.Add("My Data"); //Add a new sheet to the workbook
    var dt = GetDataTable(); //This retrieves a System.Data.DataTable, you'd likely get it from your database.
    sheet.Cells["A1"].LoadFromDataTable(dt, true); //EPPlus contains helper function to load data from a DataTable, though you could manually fill in rows/column values yourself if you want
    Response.BinaryWrite(package.GetAsByteArray());
    Response.End();
}

You might be thinking "Okay, that got the data in there. But my Export GridView to HTML approach also included some styling info!". Which is true. Let's say we want to use an Excel table style.

protected void ExportBtn_Click(object sender, EventArgs e)
{
    Response.Clear();
    Response.AddHeader("content-disposition", "attachment;filename=myexcelfile.xlsx");
    Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
    var package = new ExcelPackage(); //create a new package, this is the equivalent of an XLSX file.
    var sheet = package.Workbook.Worksheets.Add("My Data"); //Add a new sheet to the workbook
    var dt = GetDataTable(); //This retrieves a System.Data.DataTable, you'd likely get it from your database.
    sheet.Cells["A1"].LoadFromDataTable(dt, true); //EPPlus contains helper function to load data from a DataTable, though you could manually fill in rows/column values yourself if you want
    var range = sheet.Cells[1, 1, dt.Rows.Count + 1, 2]; //We're getting a reference to all the cells that contain data
    var table = sheet.Tables.Add(range, "My Data Table"); //Creating an Excel table
    table.TableStyle = TableStyles.Medium8; //Setting the table style to something that looks nice
    range.AutoFitColumns(); //Auto fitting the column widths.
    Response.BinaryWrite(package.GetAsByteArray());
    Response.End();
}

Also, commonly among Web Forms developers (and less commonly among MVC developers), people generate their file directly in some code behind button event handler. You can take advantage of strongly typed models instead, and gain a bit more control over how your Excel file is generated.

public static ExcelPackage GenerateExcelFileForProducts(IEnumerable<Product> products)
{
    var package = new ExcelPackage();
    var sheet = package.Workbook.Worksheets.Add("Products");
    int rownum = 1;
    sheet.Cells["A" + rownum].Value = "Id";
    sheet.Cells["B" + rownum].Value = "Name";
    sheet.Cells["C" + rownum].Value = "Description";
    sheet.Cells["D" + rownum].Value = "Price";            
    foreach (var p in products)
        {
        rownum++;
        sheet.Cells["A" + rownum].Value = p.Id;
        sheet.Cells["B" + rownum].Value = p.Name;
        sheet.Cells["C" + rownum].Value = p.Description;
        sheet.Cells["D" + rownum].Value = p.Price;
        }
    var range = sheet.Cells[1, 1, rownum, 4];
    var table = sheet.Tables.Add(range, "Products Table");
    table.TableStyle = TableStyles.Medium8;
    range.AutoFitColumns();
    return package;
}

Some people don't like .xlsx files, because they believe that they're incompatible with Office 2003 and Office XP. That's partially true. Originally they did not support the Open Office XML formats. However, an available service pack provides support for the formats. You really have no excuse to be using .xls files anymore.